diff options
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 25 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope.cpp | 21 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope_p.h | 4 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 8 | ||||
-rw-r--r-- | tools/qmllint/findwarnings.cpp | 33 | ||||
-rw-r--r-- | tools/qmllint/findwarnings.h | 2 |
6 files changed, 85 insertions, 8 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 04bb27dc84..bb789a1c66 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -51,7 +51,10 @@ void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QS const QQmlJS::SourceLocation &location) { m_currentScope = QQmlJSScope::create(type, m_currentScope); - m_currentScope->setBaseTypeName(name); + if (type == QQmlJSScope::GroupedPropertyScope) + m_currentScope->setInternalName(name); + else + m_currentScope->setBaseTypeName(name); m_currentScope->setIsComposite(true); m_currentScope->setSourceLocation(location); } @@ -126,6 +129,7 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) void QQmlJSImportVisitor::endVisit(UiObjectDefinition *) { + m_currentScope->resolveGroupedScopes(); leaveEnvironment(); } @@ -235,11 +239,27 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *) bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) { - if (scriptBinding->qualifiedId->name == QLatin1String("id")) { + const auto id = scriptBinding->qualifiedId; + if (!id->next && id->name == QLatin1String("id")) { const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement); const auto *idExprension = cast<IdentifierExpression *>(statement->expression); m_scopesById.insert(idExprension->name.toString(), m_currentScope); + } else { + for (auto group = id; group->next; group = group->next) { + const QString name = group->name.toString(); + + if (name.isEmpty() || name.front().isUpper()) + break; // TODO: uppercase grouped scopes are attached properties. Handle them. + + enterEnvironment(QQmlJSScope::GroupedPropertyScope, name, group->firstSourceLocation()); + } + + // TODO: remember the actual binding, once we can process it. + + while (m_currentScope->scopeType() == QQmlJSScope::GroupedPropertyScope) + leaveEnvironment(); } + return true; } @@ -448,6 +468,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob) { + m_currentScope->resolveGroupedScopes(); const QQmlJSScope::ConstPtr childScope = m_currentScope; leaveEnvironment(); diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index bea6c29932..0787e1062e 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -168,6 +168,27 @@ void QQmlJSScope::resolveTypes(const QHash<QString, QQmlJSScope::ConstPtr> &cont } } +void QQmlJSScope::resolveGroupedScopes() +{ + for (auto it = m_childScopes.begin(), end = m_childScopes.end(); it != end; ++it) { + QQmlJSScope::Ptr childScope = *it; + if (childScope->scopeType() != QQmlJSScope::GroupedPropertyScope) + continue; + + const QString propertyName = childScope->internalName(); + for (const QQmlJSScope *type = this; type; type = type->baseType().data()) { + auto propertyIt = type->m_properties.find(propertyName); + if (propertyIt != type->m_properties.end()) { + childScope->m_baseType = QQmlJSScope::ConstPtr(propertyIt->type()); + childScope->m_baseTypeName = propertyIt->typeName(); + break; + } + } + + childScope->resolveGroupedScopes(); + } +} + QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope) { auto qmlScope = scope; diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index 175b2ecda1..99645a8071 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -94,7 +94,8 @@ public: { JSFunctionScope, JSLexicalScope, - QMLScope + QMLScope, + GroupedPropertyScope }; enum class AccessSemantics { @@ -221,6 +222,7 @@ public: } void resolveTypes(const QHash<QString, ConstPtr> &contextualTypes); + void resolveGroupedScopes(); void setSourceLocation(const QQmlJS::SourceLocation &sourceLocation) { diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index d9671a6631..56dcd368c6 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -222,11 +222,11 @@ void TestQmllint::dirtyQmlCode_data() << QString(); QTest::newRow("nanchors2") << QStringLiteral("nanchors2.qml") - << QString() + << QString("unknown grouped property scope nanchors.") << QString(); QTest::newRow("nanchors3") << QStringLiteral("nanchors3.qml") - << QString() + << QString("unknown grouped property scope nanchors.") << QString(); QTest::newRow("badAliasObject") << QStringLiteral("badAliasObject.qml") @@ -246,9 +246,7 @@ void TestQmllint::dirtyQmlCode() QVERIFY(process.waitForFinished()); QCOMPARE(process.exitStatus(), QProcess::NormalExit); QEXPECT_FAIL("anchors3", "We don't see that QQuickItem cannot be assigned to QQuickAnchorLine", Abort); - QEXPECT_FAIL("nanchors1", "Invalid grouped properties are not detected", Abort); - QEXPECT_FAIL("nanchors2", "Invalid grouped properties are not detected", Abort); - QEXPECT_FAIL("nanchors3", "Invalid grouped properties are not detected", Abort); + QEXPECT_FAIL("nanchors1", "Invalid grouped properties are not always detected", Abort); QVERIFY(process.exitCode() != 0); }); diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp index 06e647dcd5..d7880b0506 100644 --- a/tools/qmllint/findwarnings.cpp +++ b/tools/qmllint/findwarnings.cpp @@ -90,6 +90,28 @@ void FindWarningVisitor::checkInheritanceCycle(QQmlJSScope::ConstPtr scope) } } +void FindWarningVisitor::checkGroupedScopes(QQmlJSScope::ConstPtr scope) +{ + auto children = scope->childScopes(); + while (!children.isEmpty()) { + auto childScope = children.takeFirst(); + if (childScope->scopeType() != QQmlJSScope::GroupedPropertyScope) + continue; + + if (!childScope->baseType()) { + m_errors.append({ + QStringLiteral("unknown grouped property scope %1.") + .arg(childScope->internalName()), + QtWarningMsg, + childScope->sourceLocation() + }); + m_visitFailed = true; + } + + children.append(childScope->childScopes()); + } +} + void FindWarningVisitor::flushPendingSignalParameters() { const SignalHandler handler = m_signalHandlers[m_pendingSingalHandler]; @@ -349,6 +371,14 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) return true; } +void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob) +{ + QQmlJSImportVisitor::endVisit(uiob); + + if (m_warnUnqualified) + checkGroupedScopes(m_currentScope); +} + bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod) { using namespace QQmlJS::AST; @@ -433,6 +463,9 @@ void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod) auto childScope = m_currentScope; QQmlJSImportVisitor::endVisit(uiod); + if (m_warnUnqualified) + checkGroupedScopes(childScope); + if (m_currentScope == m_globalScope || m_currentScope->baseTypeName() == QStringLiteral("Component")) { return; diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h index 7ce8b569e8..96cd0c3897 100644 --- a/tools/qmllint/findwarnings.h +++ b/tools/qmllint/findwarnings.h @@ -91,6 +91,7 @@ private: QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered void checkInheritanceCycle(QQmlJSScope::ConstPtr scope); + void checkGroupedScopes(QQmlJSScope::ConstPtr scope); void flushPendingSignalParameters(); void throwRecursionDepthError() override; @@ -108,6 +109,7 @@ private: /* --- end block handling --- */ bool visit(QQmlJS::AST::UiObjectBinding *uiob) override; + void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override; bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override; void endVisit(QQmlJS::AST::UiObjectDefinition *) override; bool visit(QQmlJS::AST::UiScriptBinding *uisb) override; |