Keep track of implicit and explicit Component
This allows us to ignore them when we e.g. check for required properties. Fixes: QTBUG-95373 Change-Id: I2b02e3c24b4891773ac6619a9e051edd9d87aa1f Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
f0e5ff83a1
commit
3dccc84336
|
@ -489,8 +489,13 @@ void QQmlJSImportVisitor::processDefaultProperties()
|
|||
|
||||
// Assigning any element to a QQmlComponent property implicitly wraps it into a Component
|
||||
// Check whether the property can be assigned the scope
|
||||
if (propType->canAssign(scope))
|
||||
if (propType->canAssign(scope)) {
|
||||
if (propType->causesImplicitComponentWrapping()) {
|
||||
// mark the scope as implicitly wrapped, unless it is a Component
|
||||
scope->setIsWrappedInImplicitComponent(!scope->causesImplicitComponentWrapping());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m_logger->logWarning(
|
||||
QStringLiteral("Cannot assign to default property of incompatible type"),
|
||||
|
@ -529,6 +534,10 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
|
|||
if (property.isValid() && !property.type().isNull()
|
||||
&& (objectBinding.onToken || property.type()->canAssign(objectBinding.childScope))) {
|
||||
|
||||
|
||||
if (property.type()->causesImplicitComponentWrapping())
|
||||
objectBinding.childScope->setIsWrappedInImplicitComponent(!objectBinding.childScope->causesImplicitComponentWrapping());
|
||||
|
||||
QQmlJSMetaPropertyBinding binding =
|
||||
objectBinding.scope->hasOwnPropertyBinding(propertyName)
|
||||
? objectBinding.scope->ownPropertyBinding(propertyName)
|
||||
|
@ -629,7 +638,7 @@ void QQmlJSImportVisitor::checkRequiredProperties()
|
|||
}
|
||||
|
||||
for (const auto &defScope : m_objectDefinitionScopes) {
|
||||
if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent())
|
||||
if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent() || defScope->isComponentRootElement())
|
||||
continue;
|
||||
|
||||
QVector<QQmlJSScope::ConstPtr> scopesToSearch;
|
||||
|
@ -1611,7 +1620,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
|
|||
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
|
||||
{
|
||||
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
|
||||
const QQmlJSScope::ConstPtr childScope = m_currentScope;
|
||||
// must be mutable, as we might mark it as implicitly wrapped in a component
|
||||
const QQmlJSScope::Ptr childScope = m_currentScope;
|
||||
leaveEnvironment();
|
||||
|
||||
auto group = uiob->qualifiedId;
|
||||
|
|
|
@ -198,7 +198,7 @@ protected:
|
|||
struct PendingPropertyObjectBinding
|
||||
{
|
||||
QQmlJSScope::Ptr scope;
|
||||
QQmlJSScope::ConstPtr childScope;
|
||||
QQmlJSScope::Ptr childScope;
|
||||
QString name;
|
||||
QQmlJS::SourceLocation location;
|
||||
bool onToken;
|
||||
|
|
|
@ -179,6 +179,45 @@ QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
|
|||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns if assigning to a property of this type would cause
|
||||
implicit component wrapping for non-Component types.
|
||||
|
||||
\note This method can also be used to check whether a type needs
|
||||
to be implicitly wrapped: A type for which this function returns true
|
||||
doesn't need to be actually wrapped.
|
||||
*/
|
||||
bool QQmlJSScope::causesImplicitComponentWrapping() const {
|
||||
if (internalName() == u"QQmlComponent")
|
||||
return true;
|
||||
else if (isComposite()) // composite types are never treated as Component
|
||||
return false;
|
||||
// A class which is derived from component is not treated as a Component
|
||||
// However isUsableComponent considers also QQmlAbstractDelegateComponent
|
||||
// See isUsableComponent in qqmltypecompiler.cpp
|
||||
|
||||
for (auto cppBase = nonCompositeBaseType(baseType()); cppBase; cppBase = cppBase->baseType())
|
||||
if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns true if the scope is the outermost element of a separate Component
|
||||
Either because it has been implicitly wrapped, e.g. due to an assignment to
|
||||
a Component property, or because it is the first (and only) child of a
|
||||
Component.
|
||||
*/
|
||||
bool QQmlJSScope::isComponentRootElement() const {
|
||||
if (m_flags.testFlag(WrappedInImplicitComponent))
|
||||
return true;
|
||||
auto base = nonCompositeBaseType(parentScope()); // handles null parentScope()
|
||||
if (!base)
|
||||
return false;
|
||||
return base->internalName() == u"QQmlComponent";
|
||||
}
|
||||
|
||||
bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const
|
||||
{
|
||||
if (m_scopeType == QQmlJSScope::QMLScope)
|
||||
|
@ -602,13 +641,7 @@ bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
|
|||
if (!derived)
|
||||
return false;
|
||||
|
||||
bool isBaseComponent = false;
|
||||
for (auto scope = this; scope; scope = scope->baseType().get()) {
|
||||
if (internalName() == u"QQmlComponent"_qs) {
|
||||
isBaseComponent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool isBaseComponent = causesImplicitComponentWrapping();
|
||||
|
||||
for (auto scope = derived; !scope.isNull(); scope = scope->baseType()) {
|
||||
if (isSameType(scope))
|
||||
|
|
|
@ -115,7 +115,8 @@ public:
|
|||
Script = 0x8,
|
||||
CustomParser = 0x10,
|
||||
Array = 0x20,
|
||||
InlineComponent = 0x40
|
||||
InlineComponent = 0x40,
|
||||
WrappedInImplicitComponent = 0x80
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
Q_FLAGS(Flags);
|
||||
|
@ -201,6 +202,9 @@ public:
|
|||
QString internalName() const { return m_internalName; }
|
||||
void setInternalName(const QString &internalName) { m_internalName = internalName; }
|
||||
|
||||
bool causesImplicitComponentWrapping() const;
|
||||
bool isComponentRootElement() const;
|
||||
|
||||
void addExport(const QString &name, const QString &package, const QTypeRevision &version);
|
||||
QList<Export> exports() const { return m_exports; }
|
||||
|
||||
|
@ -295,6 +299,7 @@ public:
|
|||
}
|
||||
void setIsArrayScope(bool v) { m_flags.setFlag(Array, v); }
|
||||
void setIsInlineComponent(bool v) { m_flags.setFlag(InlineComponent, v); }
|
||||
void setIsWrappedInImplicitComponent(bool v) { m_flags.setFlag(WrappedInImplicitComponent, v); }
|
||||
|
||||
void setAccessSemantics(AccessSemantics semantics) { m_semantics = semantics; }
|
||||
AccessSemantics accessSemantics() const { return m_semantics; }
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Row {
|
||||
// explicit component
|
||||
Component {
|
||||
id: foo
|
||||
Item {
|
||||
required property int i
|
||||
}
|
||||
}
|
||||
|
||||
// implicit component, plain property
|
||||
property Component com: Item {}
|
||||
|
||||
Repeater {
|
||||
model: 3
|
||||
// implicit component, default property
|
||||
Text {
|
||||
required property int index
|
||||
height: 40
|
||||
color: "black"
|
||||
text: "I'm item " + index
|
||||
}
|
||||
}
|
||||
}
|
|
@ -820,6 +820,7 @@ void TestQmllint::cleanQmlCode_data()
|
|||
QTest::newRow("QQmlEasingEnums::Type") << QStringLiteral("animationEasing.qml");
|
||||
QTest::newRow("ValidLiterals") << QStringLiteral("validLiterals.qml");
|
||||
QTest::newRow("GoodModulePrefix") << QStringLiteral("goodModulePrefix.qml");
|
||||
QTest::newRow("required property in Component") << QStringLiteral("requiredPropertyInComponent.qml");
|
||||
}
|
||||
|
||||
void TestQmllint::cleanQmlCode()
|
||||
|
|
Loading…
Reference in New Issue