aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-08-17 16:27:38 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-08-22 08:37:36 +0000
commitf751745dac2ff8d213e2b05bb9d0848cc90c905a (patch)
treed4d4eaef6a240afcd0eea906851aaf7079b7aebd
parent06b20fdd739ac0aa415d01429434be1b7d264ffc (diff)
QtQml: Create implicit components in inline components early
When the resolve() method is called on an inline component root we can resolve implicit components inside that element right away. If we wait for the inline component root to show up again in an outer element, there are a number of code paths that may miss it. Fixes: QTBUG-98859 Change-Id: I5a37422332fc2cde6b9b2f7ca9ad73ac3bbd4ff2 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> (cherry picked from commit ec25258346102484257a708be3b604aec6ea951f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qml/qml/qqmlcomponentandaliasresolver_p.h73
-rw-r--r--tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml27
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp12
3 files changed, 88 insertions, 24 deletions
diff --git a/src/qml/qml/qqmlcomponentandaliasresolver_p.h b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
index 5ac4ab210f..e1c3cb2dea 100644
--- a/src/qml/qml/qqmlcomponentandaliasresolver_p.h
+++ b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
@@ -61,6 +61,7 @@ private:
const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
[[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
[[nodiscard]] QQmlError resolveAliases(int componentIndex);
+ [[nodiscard]] QQmlError resolveComponentsInInlineComponentRoot(int root);
QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
@@ -187,6 +188,36 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::findAndRegisterImplici
return QQmlError();
}
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveComponentsInInlineComponentRoot(
+ int root)
+{
+ // Find implicit components in the inline component itself. Also warn about inline
+ // components being explicit components.
+
+ const auto rootObj = m_compiler->objectAt(root);
+ Q_ASSERT(rootObj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot));
+
+ if (const int typeName = rootObj->inheritedTypeNameIndex) {
+ const auto *tref = resolvedType(typeName);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject) {
+ qCWarning(lcQmlTypeCompiler).nospace().noquote()
+ << m_compiler->url().toString() << ":" << rootObj->location.line() << ":"
+ << rootObj->location.column()
+ << ": Using a Component as the root of an inline component is deprecated: "
+ "inline components are "
+ "automatically wrapped into Components when needed.";
+ return QQmlError();
+ }
+ }
+
+ const QQmlPropertyCache::ConstPtr rootCache = m_propertyCaches->at(root);
+ Q_ASSERT(rootCache);
+
+ return findAndRegisterImplicitComponents(rootObj, rootCache);
+}
+
// Resolve ignores everything relating to inline components, except for implicit components.
template<typename ObjectContainer>
QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
@@ -197,6 +228,12 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
// on the left hand side is of QQmlComponent type.
const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
+ if (root != 0) {
+ const QQmlError error = resolveComponentsInInlineComponentRoot(root);
+ if (error.isValid())
+ return error;
+ }
+
// root+1, as ic root is handled at the end
const int startObjectIndex = root == 0 ? root : root+1;
@@ -208,41 +245,29 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
= obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
- bool isExplicitComponent = false;
- if (obj->inheritedTypeNameIndex) {
- auto *tref = resolvedType(obj->inheritedTypeNameIndex);
- Q_ASSERT(tref);
- if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
- isExplicitComponent = true;
- }
-
- if (isInlineComponentRoot && isExplicitComponent) {
- qCWarning(lcQmlTypeCompiler).nospace().noquote()
- << m_compiler->url().toString() << ":" << obj->location.line() << ":"
- << obj->location.column()
- << ": Using a Component as the root of an inline component is deprecated: "
- "inline components are "
- "automatically wrapped into Components when needed.";
- }
-
if (root == 0) {
// normal component root, skip over anything inline component related
if (isInlineComponentRoot || isPartOfInlineComponent)
continue;
} else if (!isPartOfInlineComponent || isInlineComponentRoot) {
- // We've left the current inline component (potentially entered a new one),
- // but we still need to resolve implicit components which are part of inline components.
- if (cache && !isExplicitComponent) {
- const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
- if (error.isValid())
- return error;
- }
+ // When handling an inline component, stop where the inline component ends
+ // Note: We do not support nested inline components. Therefore, isInlineComponentRoot
+ // tells us that the element after the current inline component is again an
+ // inline component
break;
}
if (obj->inheritedTypeNameIndex == 0 && !cache)
continue;
+ bool isExplicitComponent = false;
+ if (obj->inheritedTypeNameIndex) {
+ auto *tref = resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
+ isExplicitComponent = true;
+ }
+
if (!isExplicitComponent) {
if (cache) {
const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml
new file mode 100644
index 0000000000..902dfb501d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml
@@ -0,0 +1,27 @@
+import QtQml
+
+QtObject {
+ component C1: QtObject {
+ property Component comp: null
+ }
+
+ component C2: C1 {
+ comp: QtObject {
+ objectName: "green"
+ }
+ }
+
+ component C3: C1 {
+ comp: Component {
+ QtObject {
+ objectName: "blue"
+ }
+ }
+ }
+
+ property QtObject c1: C1 {}
+ property QtObject c2: C2 {}
+ property QtObject c3: C3 {}
+
+ objectName: c2.comp.createObject().objectName + " " + c3.comp.createObject().objectName
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 624c21fe56..82eb505a80 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -316,6 +316,7 @@ private slots:
void inlineComponentFoundBeforeOtherImports();
void inlineComponentDuplicateNameError();
void inlineComponentWithAliasInstantiatedWithNewProperties();
+ void inlineComponentWithImplicitComponent();
void selfReference();
void selfReferencingSingleton();
@@ -6113,6 +6114,17 @@ void tst_qqmllanguage::inlineComponentWithAliasInstantiatedWithNewProperties()
QCOMPARE(root->property("result").toString(), "Bar");
}
+void tst_qqmllanguage::inlineComponentWithImplicitComponent()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("inlineComponentWithImplicitComponent.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+
+ QCOMPARE(root->objectName(), "green blue"_L1);
+}
+
struct QJSValueConvertible {
Q_GADGET