aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/auto/qml/qmllint/data/FromRootDirectParent.qml14
-rw-r--r--tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml8
-rw-r--r--tests/auto/qml/qmllint/data/spuriousParentWarning.qml7
-rw-r--r--tests/auto/qml/qmllint/main.cpp34
-rw-r--r--tools/qmllint/findunqualified.cpp6
-rw-r--r--tools/qmllint/findunqualified.h1
-rw-r--r--tools/qmllint/scopetree.cpp25
-rw-r--r--tools/qmllint/scopetree.h2
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;