aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-05-13 15:22:07 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-05-24 15:44:16 +0200
commit4d71091a198fbbd3f84c61af0f2915493f2dad1a (patch)
tree28b337e45731b79fc8e3ef77df05d0ed5f32eb69
parentff0b9ec6bf817f741e3c9fefbfcd55592e9b2542 (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.cpp11
-rw-r--r--src/qmlcompiler/qqmljsscopesbyid_p.h23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/boundComponents.qml24
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp17
-rw-r--r--tests/auto/qml/qmllint/data/badlyBoundComponents.qml25
-rw-r--r--tests/auto/qml/qmllint/data/boundComponents.qml23
-rw-r--r--tests/auto/qml/qmllint/data/unboundComponents.qml22
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp10
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()