From 979f9f4d3442ef9373e823cd681c5c23f84cc55b Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 24 Apr 2017 09:54:02 +0200 Subject: [PATCH] QLatin1String: fix qt_compare_strings(QLatin1String, QLatin1String) for null strings qstrcmp sorts null strings before empty ones, while the Qt string classes consider them equal. The qt_compare_strings() overload for QLatin1String was using qstrcmp(), but is supposed to implement the semantics that Qt string classes use, so we need to add an extra check. Was uncovered by tests for QLatin1String::startsWith(), but added a new test for qCompareStrings() now, which is a bit more complicated than desired, due to the lack of QUtf8String. Change-Id: I0493c4491df928a68861a1bc7f0962f1c870a416 Reviewed-by: Edward Welbourne Reviewed-by: Lars Knoll --- src/corelib/tools/qstring.cpp | 2 + .../tst_qstringapisymmetry.cpp | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index e5e2376888..5d250df99c 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -695,6 +695,8 @@ static int qt_compare_strings(QStringView lhs, QLatin1String rhs, Qt::CaseSensit static int qt_compare_strings(QLatin1String lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { + if (lhs.isEmpty()) + return lencmp(0, rhs.size()); const auto l = std::min(lhs.size(), rhs.size()); int r; if (cs == Qt::CaseSensitive) diff --git a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp index f2ca48d739..4c2219cf7a 100644 --- a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -319,6 +319,9 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) QTest::newRow("null <> empty") << QStringRef() << QLatin1String() << QStringRef(&empty) << QLatin1String("") << 0 << 0; + QTest::newRow("empty <> null") << QStringRef(&empty) << QLatin1String("") + << QStringRef() << QLatin1String() + << 0 << 0; } #define ROW(lhs, rhs) \ @@ -362,6 +365,38 @@ struct has_nothrow_compare { enum { value = is_utf8_encoded::value == is_utf8_encoded::value }; }; +template +struct has_qCompareStrings { + enum { value = !std::is_same::value && !std::is_same::value && + !is_utf8_encoded::value && !is_utf8_encoded::value }; +}; + +template +using if_has_qCompareStrings = typename std::enable_if::value, bool>::type; + +template +using if_lacks_qCompareStrings = typename std::enable_if::value, bool>::type; + +static inline Q_DECL_CONSTEXPR int sign(int x) Q_DECL_NOTHROW +{ + return x < 0 ? -1 : + x > 0 ? +1 : + /*else*/ 0 ; +} + +template = true> +int qCompareStringsWrapper(const LHS &lhs, const RHS &rhs, Qt::CaseSensitivity cs, int) + Q_DECL_NOEXCEPT_EXPR(noexcept(qCompareStrings(lhs, rhs, cs))) +{ + return qCompareStrings(lhs, rhs, cs); +} + +template = true> +int qCompareStringsWrapper(const LHS &, const RHS &, Qt::CaseSensitivity, int result) +{ + return result; +} + template void tst_QStringApiSymmetry::compare_impl() const { @@ -370,6 +405,7 @@ void tst_QStringApiSymmetry::compare_impl() const QFETCH(QStringRef, rhsUnicode); QFETCH(QLatin1String, rhsLatin1); QFETCH(int, caseSensitiveCompareResult); + QFETCH(const int, caseInsensitiveCompareResult); const auto lhsU8 = lhsUnicode.toUtf8(); const auto rhsU8 = rhsUnicode.toUtf8(); @@ -386,6 +422,10 @@ void tst_QStringApiSymmetry::compare_impl() const # define QVERIFY_NOEXCEPT(expr) #endif + QCOMPARE(sign(qCompareStringsWrapper(lhs, rhs, Qt::CaseSensitive, caseSensitiveCompareResult)), + sign(caseSensitiveCompareResult)); + QCOMPARE(sign(qCompareStringsWrapper(lhs, rhs, Qt::CaseInsensitive, caseInsensitiveCompareResult)), + sign(caseInsensitiveCompareResult)); #define CHECK(op) \ QVERIFY_NOEXCEPT(lhs op rhs); \ do { if (caseSensitiveCompareResult op 0) { \