Align qmllint default property handling with QQmlComponent model

Make qmllint work on default properties that come from the base type
of the scope and not from the scope itself. Otherwise, cases like:
QtObject {
  default property QtObject child
  QtObject {}
}
become valid in qmllint (and related tooling), while this code
produces an error when QQmlComponent reads it

Pick-to: 6.2
Change-Id: I347a2753b8674e53aaad6febb239c44089f08a8a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Andrei Golubev 2021-07-19 16:32:18 +02:00
parent 24bff37f9e
commit df351e0739
16 changed files with 60 additions and 20 deletions

View File

@ -416,13 +416,27 @@ void QQmlJSImportVisitor::processDefaultProperties()
{ {
for (auto it = m_pendingDefaultProperties.constBegin(); for (auto it = m_pendingDefaultProperties.constBegin();
it != m_pendingDefaultProperties.constEnd(); ++it) { it != m_pendingDefaultProperties.constEnd(); ++it) {
const QQmlJSScope::ConstPtr parentScope = it.key(); QQmlJSScope::ConstPtr parentScope = it.key();
// We can't expect custom parser default properties to be sensible, discard them for now. // We can't expect custom parser default properties to be sensible, discard them for now.
if (parentScope->isInCustomParserParent()) if (parentScope->isInCustomParserParent())
continue; continue;
const QString defaultPropertyName = parentScope->defaultPropertyName(); /* consider:
*
* QtObject { // <- parentScope
* default property var p // (1)
* QtObject {} // (2)
* }
*
* `p` (1) is a property of a subtype of QtObject, it couldn't be used
* in a property binding (2)
*/
// thus, use a base type of parent scope to detect a default property
parentScope = parentScope->baseType();
const QString defaultPropertyName =
parentScope ? parentScope->defaultPropertyName() : QString();
if (defaultPropertyName.isEmpty()) { if (defaultPropertyName.isEmpty()) {
// If the parent scope is based on Component it can have any child element // If the parent scope is based on Component it can have any child element

View File

@ -0,0 +1,4 @@
import QtQml 2.0
QtObject {
default property list<QtObject> children
}

View File

@ -0,0 +1,4 @@
import QtQml 2.0
QtObject {
default property QtObject child
}

View File

@ -0,0 +1,4 @@
import QtQml 2.0
QtObject {
default property string child
}

View File

@ -0,0 +1,4 @@
import QtQml 2.0
QtObject {
default property var child
}

View File

@ -1,6 +1,5 @@
import QtQml 2.0 import QtQml 2.0
QtObject { TypeWithDefaultProperty {
default property QtObject child
QtObject {} QtObject {}
} }

View File

@ -1,8 +1,7 @@
import QtQml 2.0 import QtQml 2.0
import QtQuick 2.15 import QtQuick 2.15
QtObject { TypeWithDefaultListProperty {
default property list<QtObject> children
QtObject {} QtObject {}
Rectangle {} Rectangle {}
} }

View File

@ -1,7 +1,5 @@
import QtQml 2.0 import QtQml 2.0
QtObject { TypeWithDefaultVarProperty {
default property var child
QtObject {} QtObject {}
} }

View File

@ -1,7 +1,6 @@
import QtQml 2.0 import QtQml 2.0
QtObject { TypeWithDefaultProperty {
default property QtObject child
QtObject {} QtObject {}
QtObject {} QtObject {}
} }

View File

@ -1,6 +1,6 @@
import QtQml 2.0 import QtQml 2.0
QtObject { TypeWithDefaultStringProperty {
default property string child // default property has type `string`, so cannot assing an object to it
QtObject {} QtObject {}
} }

View File

@ -0,0 +1,6 @@
import QtQml 2.0
QtObject {
default property QtObject child
QtObject {}
}

View File

@ -0,0 +1,4 @@
import DuplicateImport // imports QtQml
QtObject {
default property QtObject child
}

View File

@ -1,8 +1,6 @@
import DuplicateImport // imports QtQml directly and indirectly via QtQuick import DuplicateImport // imports QtQml directly and indirectly via QtQuick
QtObject { QtObjectWithDefaultProperty { // for default property
default property QtObject child
ItemDerived { // item derived has compatible QtObject type ItemDerived { // item derived has compatible QtObject type
x: 4 x: 4
} }

View File

@ -0,0 +1,4 @@
import Things // imports QtQml
QtObject {
default property QtObject child
}

View File

@ -1,7 +1,6 @@
import Things import Things
QtObject { QtObjectWithDefaultProperty { // for default property
default property QtObject child
objectName: "QtQml was imported from Things/qmldir" objectName: "QtQml was imported from Things/qmldir"
ItemDerived { ItemDerived {

View File

@ -508,8 +508,12 @@ void TestQmllint::dirtyQmlCode_data()
<< false; << false;
QTest::newRow("MissingDefaultProperty") QTest::newRow("MissingDefaultProperty")
<< QStringLiteral("defaultPropertyWithoutKeyword.qml") << QStringLiteral("defaultPropertyWithoutKeyword.qml")
<< QStringLiteral("Cannot assign to non-existent default property") << QString() << false; << QStringLiteral("Cannot assign to non-existent default property") << QString()
<< false;
QTest::newRow("MissingDefaultPropertyDefinedInTheSameType")
<< QStringLiteral("defaultPropertyWithinTheSameType.qml")
<< QStringLiteral("Cannot assign to non-existent default property") << QString()
<< false;
QTest::newRow("DoubleAssignToDefaultProperty") QTest::newRow("DoubleAssignToDefaultProperty")
<< QStringLiteral("defaultPropertyWithDoubleAssignment.qml") << QStringLiteral("defaultPropertyWithDoubleAssignment.qml")
<< QStringLiteral("Cannot assign multiple objects to a default non-list property") << QStringLiteral("Cannot assign multiple objects to a default non-list property")