diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-05-13 15:22:07 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-05-24 15:44:16 +0200 |
commit | 4d71091a198fbbd3f84c61af0f2915493f2dad1a (patch) | |
tree | 28b337e45731b79fc8e3ef77df05d0ed5f32eb69 | |
parent | ff0b9ec6bf817f741e3c9fefbfcd55592e9b2542 (diff) |
qmlcompiler: Evaluate pragma ComponentBehavior
If components are bound we can assume the IDs of outer components are
reachable.
Fixes: QTBUG-102806
Fixes: QTBUG-101012
Change-Id: Ia26d3963d6c2fb9698debb12f9c655c5522f81ea
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 11 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscopesbyid_p.h | 23 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/boundComponents.qml | 24 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 17 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/badlyBoundComponents.qml | 25 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/boundComponents.qml | 23 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/unboundComponents.qml | 22 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 10 |
9 files changed, 154 insertions, 2 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index b39e004cd6..148a55845c 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -2102,6 +2102,17 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma) if (pragma->name == u"Singleton") m_rootIsSingleton = true; + if (pragma->name == u"ComponentBehavior") { + if (pragma->value == u"Bound") { + m_scopesById.setComponentsAreBound(true); + } else if (pragma->value == u"Unbound") { + m_scopesById.setComponentsAreBound(false); + } else { + m_logger->log(u"Unkonwn argument \"%s\" to pragma ComponentBehavior"_s + .arg(pragma->value), Log_Syntax, pragma->firstSourceLocation()); + } + } + return true; } diff --git a/src/qmlcompiler/qqmljsscopesbyid_p.h b/src/qmlcompiler/qqmljsscopesbyid_p.h index 59b83ea13f..2513f7b752 100644 --- a/src/qmlcompiler/qqmljsscopesbyid_p.h +++ b/src/qmlcompiler/qqmljsscopesbyid_p.h @@ -50,6 +50,8 @@ QT_BEGIN_NAMESPACE class QQmlJSScopesById { public: + void setComponentsAreBound(bool bound) { m_componentsAreBound = bound; } + QString id(const QQmlJSScope::ConstPtr &scope) const { for (auto it = m_scopesById.begin(), end = m_scopesById.end(); it != end; ++it) { @@ -70,11 +72,13 @@ public: const auto range = m_scopesById.equal_range(id); if (range.first == range.second) return QQmlJSScope::ConstPtr(); - const QQmlJSScope::ConstPtr root = componentRoot(referrer); + const QQmlJSScope::ConstPtr referrerRoot = componentRoot(referrer); + for (auto it = range.first; it != range.second; ++it) { - if (componentRoot(*it) == root) + if (isComponentVisible(componentRoot(*it), referrerRoot)) return *it; } + return QQmlJSScope::ConstPtr(); } @@ -108,7 +112,22 @@ private: return scope; } + bool isComponentVisible( + const QQmlJSScope::ConstPtr &observed, const QQmlJSScope::ConstPtr &observer) const + { + if (!m_componentsAreBound) + return observed == observer; + + for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) { + if (scope == observed) + return true; + } + + return false; + } + QMultiHash<QString, QQmlJSScope::ConstPtr> m_scopesById; + bool m_componentsAreBound = false; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 7e31bb1711..e5d9dc5351 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -45,6 +45,7 @@ set(qml_files attachedBaseEnum.qml bindToValueType.qml blockComments.qml + boundComponents.qml callContextPropertyLookupResult.qml childobject.qml colorAsVariant.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/boundComponents.qml b/tests/auto/qml/qmlcppcodegen/data/boundComponents.qml new file mode 100644 index 0000000000..6394fc9ef3 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/boundComponents.qml @@ -0,0 +1,24 @@ +pragma Strict +pragma ComponentBehavior: Bound + +import QtQml + +QtObject { + id: root + property string foo: "bar" + property Component c1: Component { + QtObject { + id: c1 + objectName: root.foo + property int i: 12 + property Component c2: Component { + QtObject { + objectName: root.foo + c1.i + } + } + property QtObject o: c2.createObject() + } + } + + property QtObject o: c1.createObject() +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index cbcd1afd21..1ccbd81745 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -135,6 +135,7 @@ private slots: void invisibleTypes(); void invalidPropertyType(); void valueTypeLists(); + void boundComponents(); }; void tst_QmlCppCodegen::simpleBinding() @@ -2120,6 +2121,22 @@ void tst_QmlCppCodegen::valueTypeLists() QVERIFY(!o->property("intOutOfBounds").isValid()); } +void tst_QmlCppCodegen::boundComponents() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/boundComponents.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QObject *c1o = o->property("o").value<QObject *>(); + QVERIFY(c1o != nullptr); + QCOMPARE(c1o->objectName(), u"bar"_s); + + QObject *c2o = c1o->property("o").value<QObject *>(); + QVERIFY(c2o != nullptr); + QCOMPARE(c2o->objectName(), u"bar12"_s); +} + void tst_QmlCppCodegen::runInterpreted() { #ifdef Q_OS_ANDROID diff --git a/tests/auto/qml/qmllint/data/badlyBoundComponents.qml b/tests/auto/qml/qmllint/data/badlyBoundComponents.qml new file mode 100644 index 0000000000..4f25f1d508 --- /dev/null +++ b/tests/auto/qml/qmllint/data/badlyBoundComponents.qml @@ -0,0 +1,25 @@ +pragma ComponentBehavior: Bound + +import QtQml + +QtObject { + id: root + property string foo: "bar" + property Component c1: Component { + QtObject { + id: cc + objectName: root.foo + property int i: 12 + } + } + + property Component c2: Component { + QtObject { + objectName: root.foo + cc.i + } + } + + property QtObject o1: c1.createObject() + property QtObject o2: c2.createObject() + +} diff --git a/tests/auto/qml/qmllint/data/boundComponents.qml b/tests/auto/qml/qmllint/data/boundComponents.qml new file mode 100644 index 0000000000..2ffa2f4124 --- /dev/null +++ b/tests/auto/qml/qmllint/data/boundComponents.qml @@ -0,0 +1,23 @@ +pragma ComponentBehavior: Bound + +import QtQml + +QtObject { + id: root + property string foo: "bar" + property Component c1: Component { + QtObject { + id: c1 + objectName: root.foo + property int i: 12 + property Component c2: Component { + QtObject { + objectName: root.foo + c1.i + } + } + property QtObject o: c2.createObject() + } + } + + property QtObject o: c1.createObject() +} diff --git a/tests/auto/qml/qmllint/data/unboundComponents.qml b/tests/auto/qml/qmllint/data/unboundComponents.qml new file mode 100644 index 0000000000..f4ce8c6f5d --- /dev/null +++ b/tests/auto/qml/qmllint/data/unboundComponents.qml @@ -0,0 +1,22 @@ +pragma ComponentBehavior: Unbound +import QtQml + +QtObject { + id: root + property string foo: "bar" + property Component c1: Component { + QtObject { + id: c1 + objectName: root.foo + property int i: 12 + property Component c2: Component { + QtObject { + objectName: root.foo + c1.i + } + } + property QtObject o: c2.createObject() + } + } + + property QtObject o: c1.createObject() +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 88879ec8c1..ecc7641cc8 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -1016,6 +1016,15 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", << QStringLiteral("assignNonExistingTypeToVarProp.qml") << Result { { Message { QStringLiteral( "NonExistingType was not found. Did you add all import paths?") } } }; + QTest::newRow("unboundComponents") + << QStringLiteral("unboundComponents.qml") + << Result { { + Message { QStringLiteral("Unqualified access"), 10, 25 }, + Message { QStringLiteral("Unqualified access"), 14, 33 } + } }; + QTest::newRow("badlyBoundComponents") + << QStringLiteral("badlyBoundComponents.qml") + << Result { { Message { QStringLiteral("Unqualified access"), 18, 36 } } }; } void TestQmllint::dirtyQmlCode() @@ -1169,6 +1178,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("requiredWithRootLevelAlias") << QStringLiteral("RequiredWithRootLevelAlias.qml"); QTest::newRow("jsVarDeclarations") << QStringLiteral("jsVarDeclarations.qml"); QTest::newRow("qmodelIndex") << QStringLiteral("qmodelIndex.qml"); + QTest::newRow("boundComponents") << QStringLiteral("boundComponents.qml"); } void TestQmllint::cleanQmlCode() |