From 9e6c9eda6f7f98b5308703edea47d204f54ffdf2 Mon Sep 17 00:00:00 2001 From: Ahmad Samir Date: Fri, 27 Oct 2023 17:25:25 +0200 Subject: [PATCH] QMetaEnum: let key(s)ToValue match fully-qualified unscoped enumerators For an unscoped enumerator, e.g.: namespace N { class C { enum E { F }; }; }; N::C::F and N::C::E::F are equivalent and key(s)ToValue should match both. This already works for scoped enums because the name of the enum can't be dropped. Fixes: QTBUG-118240 Pick-to: 6.6 Change-Id: I84d7bbb7aa8f82b2a7c2bc7e4edd5d77d37335c4 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetaobject.cpp | 32 +++++++++++++++++-- .../kernel/qmetaobject/tst_qmetaobject.cpp | 17 ++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 0873e8811d7..f82d793f242 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -3193,6 +3193,33 @@ const char *QMetaEnum::scope() const return mobj ? mobj->className() : nullptr; } +static bool isScopeMatch(QByteArrayView scope, const QMetaEnum *e) +{ + const QByteArrayView className = e->enclosingMetaObject()->className(); + + // Typical use-cases: + // a) Unscoped: namespace N { class C { enum E { F }; }; }; key == "N::C::F" + // b) Scoped: namespace N { class C { enum class E { F }; }; }; key == "N::C::E::F" + if (scope == className) + return true; + + // Not using name() because if isFlag() is true, we want the actual name + // of the enum, e.g. "MyFlag", not "MyFlags", e.g. + // enum MyFlag { F1, F2 }; Q_DECLARE_FLAGS(MyFlags, MyFlag); + QByteArrayView name = e->enumName(); + + // Match fully qualified enumerator in unscoped enums, key == "N::C::E::F" + // equivalent to use-case "a" above + const auto sz = className.size(); + if (scope.size() == sz + qsizetype(qstrlen("::")) + name.size() + && scope.startsWith(className) + && scope.sliced(sz, 2) == "::" + && scope.sliced(sz + 2) == name) + return true; + + return false; +} + /*! Returns the integer value of the given enumeration \a key, or -1 if \a key is not defined. @@ -3210,9 +3237,10 @@ int QMetaEnum::keyToValue(const char *key, bool *ok) const *ok = false; if (!mobj || !key) return -1; + const auto [scope, enumKey] = parse_scope(QLatin1StringView(key)); for (int i = 0; i < int(data.keyCount()); ++i) { - if ((!scope || *scope == objectClassName(mobj)) + if ((!scope || isScopeMatch(*scope, this)) && enumKey == stringDataView(mobj, mobj->d.data[data.data() + 2 * i])) { if (ok != nullptr) *ok = true; @@ -3317,7 +3345,7 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const return -1; for (const auto &untrimmed : list) { const auto parsed = parse_scope(untrimmed.trimmed()); - if (parsed.scope && *parsed.scope != objectClassName(mobj)) + if (parsed.scope && !isScopeMatch(*parsed.scope, this)) return -1; // wrong type name in qualified name if (auto thisValue = lookup(parsed.key)) value |= *thisValue; diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 362f2707e76..2717c7b670f 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -2366,6 +2366,8 @@ void tst_QMetaObject::keysToValue() QVERIFY(!me.isFlag()); QCOMPARE(QByteArray(me.scope()), QByteArray("MyNamespace::" + name)); QCOMPARE(me.keyToValue("MyNamespace::" + name + "::MyEnum2", &ok), 1); + // Fully qualified unscoped enumerator + QCOMPARE(me.keyToValue("MyNamespace::" + name + "::MyEnum::MyEnum2", &ok), 1); QCOMPARE(ok, true); QCOMPARE(me.keyToValue(name + "::MyEnum2", &ok), -1); QCOMPARE(ok, false); @@ -2395,6 +2397,9 @@ void tst_QMetaObject::keysToValue() QCOMPARE(QByteArray(mf.scope()), QByteArray("MyNamespace::" + name)); QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag2", &ok), 2); QCOMPARE(ok, true); + // Fully qualified + QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag::MyFlag2", &ok), 2); + QCOMPARE(ok, true); QCOMPARE(mf.keysToValue(name + "::MyFlag2", &ok), -1); QCOMPARE(ok, false); QCOMPARE(mf.keysToValue("MyNamespace::MyFlag2", &ok), -1); @@ -2404,7 +2409,12 @@ void tst_QMetaObject::keysToValue() QCOMPARE(mf.keysToValue("MyFlag", &ok), -1); QCOMPARE(ok, false); QCOMPARE(QLatin1String(mf.valueToKey(2)), QLatin1String("MyFlag2")); - QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag1|MyNamespace::" + name + "::MyFlag2", &ok), 3); + + const QByteArray prefix = "MyNamespace::" + name; + QCOMPARE(mf.keysToValue(prefix + "::MyFlag1|" + prefix + "::MyFlag2", &ok), 3); + QCOMPARE(ok, true); + // Fully qualified + QCOMPARE(mf.keysToValue(prefix + "::MyFlag::MyFlag1|" + prefix + "::MyFlag::MyFlag2", &ok), 3); QCOMPARE(ok, true); QCOMPARE(mf.keysToValue(name + "::MyFlag1|" + name + "::MyFlag2", &ok), -1); QCOMPARE(ok, false); @@ -2416,7 +2426,10 @@ void tst_QMetaObject::keysToValue() QCOMPARE(ok, true); QCOMPARE(mf.keysToValue("MyFlag1|MyNamespace::" + name + "::MyFlag2", &ok), 3); QCOMPARE(ok, true); - QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag2|MyNamespace::" + name + "::MyFlag2", &ok), 2); + QCOMPARE(mf.keysToValue(prefix + "::MyFlag2|" + prefix + "::MyFlag2", &ok), 2); + QCOMPARE(ok, true); + // Fully qualified + QCOMPARE(mf.keysToValue(prefix + "::MyFlag::MyFlag2|" + prefix + "::MyFlag::MyFlag2", &ok), 2); QCOMPARE(ok, true); QCOMPARE(QLatin1String(mf.valueToKeys(3)), QLatin1String("MyFlag1|MyFlag2"));