diff options
-rw-r--r-- | tests/auto/qml/qmllint/data/FromRootDirectParent.qml | 14 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml | 8 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/spuriousParentWarning.qml | 7 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/main.cpp | 34 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.cpp | 6 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.h | 1 | ||||
-rw-r--r-- | tools/qmllint/scopetree.cpp | 25 | ||||
-rw-r--r-- | tools/qmllint/scopetree.h | 2 |
8 files changed, 64 insertions, 33 deletions
diff --git a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml b/tests/auto/qml/qmllint/data/FromRootDirectParent.qml deleted file mode 100644 index c0bfd0f26b..0000000000 --- a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml +++ /dev/null @@ -1,14 +0,0 @@ -import QtQuick 2.0 - -Item { - id: root - property int unqualified: 42 - - Item { - x: unqualified // user defined property from root - } - - QtObject { - property int check: x // existing property from root - } -} diff --git a/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml new file mode 100644 index 0000000000..a59b736929 --- /dev/null +++ b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml @@ -0,0 +1,8 @@ +import QtQuick 2.12 +import QtQml 2.12 + +Item { + QtObject { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml new file mode 100644 index 0000000000..1323593031 --- /dev/null +++ b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml @@ -0,0 +1,7 @@ +import QtQuick 2.12 + +Item { + Unknown { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/main.cpp b/tests/auto/qml/qmllint/main.cpp index 1069aa5a33..928575bc82 100644 --- a/tests/auto/qml/qmllint/main.cpp +++ b/tests/auto/qml/qmllint/main.cpp @@ -40,6 +40,7 @@ private Q_SLOTS: void test_data(); void testUnqualified(); void testUnqualified_data(); + void testUnqualifiedNoSpuriousParentWarning(); private: QString m_qmllintPath; }; @@ -73,13 +74,14 @@ void TestQmllint::test_data() void TestQmllint::testUnqualified() { + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); QFETCH(QString, filename); QFETCH(QString, warningMessage); QFETCH(int, warningLine); QFETCH(int, warningColumn); filename.prepend(QStringLiteral("data/")); QStringList args; - args << QStringLiteral("-U") << filename; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; QProcess process; process.start(m_qmllintPath, args); @@ -106,9 +108,6 @@ void TestQmllint::testUnqualified_data() // access property of root object QTest::newRow("FromRootDirect") << QStringLiteral("FromRoot.qml") << QStringLiteral("x: root.unqualified") << 9 << 16; // new property QTest::newRow("FromRootAccess") << QStringLiteral("FromRoot.qml") << QStringLiteral("property int check: root.x") << 13 << 33; // builtin property - // access property of root object from direct child - QTest::newRow("FromRootDirectParentDirect") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("x: parent.unqualified") << 8 << 12; - QTest::newRow("FromRootDirectParentAccess") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("property int check: parent.x") << 12 << 29; // access injected name from signal QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 5 << 21; QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPositionChanged: function(mouse) {...") << 10 << 21; @@ -117,6 +116,33 @@ void TestQmllint::testUnqualified_data() } +void TestQmllint::testUnqualifiedNoSpuriousParentWarning() +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + { + QString filename = QLatin1String("spuriousParentWarning.qml"); + filename.prepend(QStringLiteral("data/")); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode() == 0); + } + { + QString filename = QLatin1String("nonSpuriousParentWarning.qml"); + filename.prepend(QStringLiteral("data/")); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode()); + } +} + void TestQmllint::test() { QFETCH(QString, filename); diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index e6a72df44c..69e1473975 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -325,7 +325,9 @@ void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString na break; } } else { - qDebug() << name << "not found"; + m_colorOut.write(QLatin1String("warning: "), Warning); + m_colorOut.write(name + QLatin1String(" was not found. Did you add all import paths?\n")); + m_unknownImports.insert(name); break; } } @@ -526,7 +528,7 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp) auto name = idexp->name; if (!m_exportedName2MetaObject.contains(name.toString())) { m_currentScope->addIdToAccssedIfNotInParentScopes( - { name.toString(), idexp->firstSourceLocation() }); + { name.toString(), idexp->firstSourceLocation() }, m_unknownImports); } return true; } diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index 937194d142..8fc8257bef 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -57,6 +57,7 @@ private: QString m_rootId; QString m_filePath; QSet<QPair<QString, QString>> m_alreadySeenImports; + QSet<QString> m_unknownImports; ColorOutput m_colorOut; struct OutstandingConnection {QString targetName; ScopeTree *scope; QQmlJS::AST::UiObjectDefinition *uiod;}; diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp index f58fde061e..2eff3fa319 100644 --- a/tools/qmllint/scopetree.cpp +++ b/tools/qmllint/scopetree.cpp @@ -86,8 +86,14 @@ bool ScopeTree::isIdInCurrentScope(const QString &id) const return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id); } -void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair) { - if (!isIdInCurrentScope(id_loc_pair.first)) { +void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair, const QSet<QString>& unknownImports) { + // also do not add id if it is parent + // parent is almost always defined valid in QML, and if we could not find a definition for the current QML component + // not skipping "parent" will lead to many false positives + // Moreover, if the top level item is Item or inherits from it, it will have a parent property to which we would point the user + // which makes for a very nonsensical warning + auto qmlScope = getCurrentQMLScope(); + if (!isIdInCurrentScope(id_loc_pair.first) && !(id_loc_pair.first == QLatin1String("parent") && qmlScope && unknownImports.contains(qmlScope->name()))) { m_accessedIdentifiers.push_back(id_loc_pair); } } @@ -148,20 +154,15 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan while (parentScope && parentScope->scopeType() != ScopeType::QMLScope) { parentScope = parentScope->m_parentScope; } - bool directChild = parentScope && parentScope->isVisualRootScope(); colorOut.write("Note: ", Info); colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal ); - if (directChild) { - colorOut.write(QLatin1String(" As the current element is a direct child of the root element,\n you can qualify the access with parent to avoid this warning:\n"), Normal); - } else { - colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal); - if (rootId == QLatin1String("<id>")) { - colorOut.write("Note: ", Warning); - colorOut.write(("You first have to give the root element an id\n")); - } + colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal); + if (rootId == QLatin1String("<id>")) { + colorOut.write("Note: ", Warning); + colorOut.write(("You first have to give the root element an id\n")); } colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); - colorOut.write( (directChild ? QLatin1String("parent") : rootId) + QLatin1Char('.'), Hint); + colorOut.write(rootId + QLatin1Char('.'), Hint); colorOut.write(issueLocationWithContext.issueText.toString(), Normal); colorOut.write(issueLocationWithContext.afterText + QLatin1Char('\n'), Normal); } else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) { diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h index a65ed30d61..872a509123 100644 --- a/tools/qmllint/scopetree.h +++ b/tools/qmllint/scopetree.h @@ -75,7 +75,7 @@ public: void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding bool isIdInCurrentScope(QString const &id) const; - void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair); + void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports); bool isVisualRootScope() const; QString name() const; |