diff options
-rw-r--r-- | src/qml/qml/qqmltypecompiler.cpp | 18 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/enumScopeLeak.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 57 |
3 files changed, 77 insertions, 4 deletions
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index a9f5cdbf8d..a49722f57e 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -616,10 +616,20 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, bool ok = false; auto *tr = resolvedType(obj->inheritedTypeNameIndex); - if (type.isValid() && tr && tr->type() == type) { - // When these two match, we can short cut the search - QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); - QMetaEnum menum = mprop.enumerator(); + + // When these two match, we can short cut the search, unless... + bool useFastPath = type.isValid() && tr && tr->type() == type; + QMetaProperty mprop; + QMetaEnum menum; + if (useFastPath) { + mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); + menum = mprop.enumerator(); + // ...the enumerator merely comes from a related metaobject, but the enum scope does not match + // the typename we resolved + if (!menum.isScoped() && scopedEnumName.isEmpty() && typeName != QString::fromUtf8(menum.scope())) + useFastPath = false;; + } + if (useFastPath) { QByteArray enumName = enumValue.toUtf8(); if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8()) return true; diff --git a/tests/auto/qml/qqmllanguage/data/enumScopeLeak.qml b/tests/auto/qml/qqmllanguage/data/enumScopeLeak.qml new file mode 100644 index 0000000000..b4bdc54f86 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/enumScopeLeak.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import test 1.0 + +ObjectC { + testEnum: ObjectC.TestValue1 +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 77e545722a..411ff7df56 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -228,6 +228,7 @@ private slots: void scopedEnum(); void scopedEnumsWithNameClash(); void scopedEnumsWithResolvedNameClash(); + void enumNoScopeLeak(); void qmlEnums(); void literals_data(); void literals(); @@ -4155,6 +4156,62 @@ void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() QVERIFY(obj->property("success").toBool()); } +class ObjectA : public QObject +{ + Q_OBJECT + +public: + + enum TestEnum { + Default = 42, + TestValue1, + TestValue2 + }; + Q_ENUM(TestEnum) + + ObjectA() = default; +}; + +class ObjectB : public QObject +{ + Q_OBJECT + Q_PROPERTY(ObjectA::TestEnum testEnum READ testEnum WRITE setTestEnum NOTIFY testEnumChanged) + +public: + ObjectB() = default; + ObjectA::TestEnum testEnum() const {return m_testEnum;} + +public slots: + void setTestEnum(ObjectA::TestEnum testEnum) {auto old = m_testEnum; m_testEnum = testEnum; if (old != m_testEnum) testEnumChanged(m_testEnum);} +signals: + void testEnumChanged(ObjectA::TestEnum testEnum); +private: + ObjectA::TestEnum m_testEnum = ObjectA::Default; +}; + +class ObjectC : public ObjectB +{ + Q_OBJECT + +public: + ObjectC() = default; +}; + +void tst_qqmllanguage::enumNoScopeLeak() +{ + qmlRegisterType<ObjectA>("test", 1, 0, "ObjectA"); + qmlRegisterType<ObjectB>("test", 1, 0, "ObjectB"); + qmlRegisterType<ObjectC>("test", 1, 0, "ObjectC"); + QQmlEngine engine; + auto url = testFileUrl("enumScopeLeak.qml"); + QQmlComponent component(&engine, url); + const auto msg = url.toString() + ":5:5: Unable to assign [undefined] to ObjectA::TestEnum"; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, msg.toUtf8().constData()); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QCOMPARE(root->property("testEnum").toInt(), ObjectA::Default); +} + void tst_qqmllanguage::qmlEnums() { QQmlEngine engine; |