diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-08-09 16:20:22 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-08-09 16:20:22 +0200 |
commit | 36990a732eab2dc16bee89715662f6d3d8b3a0cd (patch) | |
tree | d835ba67426e12e51dc1028e8ebac9087a3d43b0 /tools | |
parent | cb37a1d0c3fce7951f5d7f1fb4a04f90e038f01b (diff) | |
parent | d1b72c98b4e617530bfb23c3a5b7ebc68c15c089 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/qt6
Change-Id: I507e252f9cb11b75dd9f7f409c39d93094e8c3ef
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qmllint/findunqualified.cpp | 35 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.h | 1 | ||||
-rw-r--r-- | tools/qmllint/scopetree.cpp | 50 | ||||
-rw-r--r-- | tools/qmllint/scopetree.h | 7 |
4 files changed, 57 insertions, 36 deletions
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 3abdfcfa53..0d1cbf5823 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -38,6 +38,7 @@ #include <private/qqmljsast_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> +#include <private/qv4codegen_p.h> QDebug &operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc); @@ -225,8 +226,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) break; } case UiObjectMember::Kind_UiEnumDeclaration: { - auto enumDeclaration = static_cast<UiEnumDeclaration *>(initMembers->member); - qDebug() << "enumdecl" << enumDeclaration->name; + // nothing to do break; } case UiObjectMember::Kind_UiObjectBinding: { @@ -235,8 +235,6 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) } case UiObjectMember::Kind_UiObjectDefinition: { // creates nothing accessible - /*auto objectDefinition = static_cast<UiObjectDefinition*>(initMembers->member); - qDebug() << "objdef" << objectDefinition->qualifiedTypeNameId->name;*/ break; } case UiObjectMember::Kind_UiPublicMember: { @@ -325,7 +323,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; } } @@ -503,7 +503,9 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) } else { auto method = m_currentScope->methods()[signal]; for (auto const ¶m : method.parameterNames()) { - m_currentScope->insertSignalIdentifier(param, method, uisb->statement->firstSourceLocation()); + auto firstSourceLocation = uisb->statement->firstSourceLocation(); + bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine; + m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody); } } return true; @@ -524,7 +526,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; } @@ -545,15 +547,19 @@ FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDir m_colorOut.insertColorMapping(Normal, ColorOutput::DefaultColor); m_colorOut.insertColorMapping(Hint, ColorOutput::GreenForeground); QLatin1String jsGlobVars[] = { - QLatin1String ("Array"), QLatin1String("Boolean"), QLatin1String("Date"), QLatin1String("Function"), QLatin1String("Math"), QLatin1String("Number"), QLatin1String("Object"), QLatin1String("RegExp"), QLatin1String("String"), - QLatin1String("Error"), QLatin1String("EvalError"), QLatin1String("RangeError"), QLatin1String("ReferenceError"), QLatin1String("SyntaxError"), QLatin1String("TypeError"), QLatin1String("URIError"), - QLatin1String("encodeURI"), QLatin1String("encodeURIComponent"), QLatin1String("decodeURI"), QLatin1String("decodeURIComponent"), QLatin1String("escape"), QLatin1String("unescape"), - QLatin1String("isFinite"), QLatin1String("isNanN"), QLatin1String("parseFloat"), QLatin1String("parseInt"), - QLatin1String("eval"), QLatin1String("console"), QLatin1String("print"), QLatin1String("gc"), + /* Not listed on the MDN page; browser and QML extensions: */ + // console/debug api + QLatin1String("console"), QLatin1String("print"), + // garbage collector + QLatin1String("gc"), + // i18n QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"), QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"), - QLatin1String("XMLHttpRequest"), QLatin1String("JSON"), QLatin1String("Promise"), - QLatin1String("undefined") + // XMLHttpRequest + QLatin1String("XMLHttpRequest") }; + for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr; ++globalName) { + m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), QQmlJS::AST::VariableScope::Const); + } for (const auto& jsGlobVar: jsGlobVars) m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const); } @@ -593,7 +599,6 @@ void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::Functi m_currentScope->insertJSIdentifier(name, VariableScope::Const); } } - // qDebug() << fexpr->firstSourceLocation() << "function expression" << fexpr->name; QString name = fexpr->name.toString(); if (name.isEmpty()) name = "<anon>"; 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 b064117dd4..2eff3fa319 100644 --- a/tools/qmllint/scopetree.cpp +++ b/tools/qmllint/scopetree.cpp @@ -30,6 +30,8 @@ #include "qcoloroutput_p.h" +#include <algorithm> + #include <QQueue> ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope) @@ -66,10 +68,10 @@ void ScopeTree::insertQMLIdentifier(QString id) m_currentScopeQMLIdentifiers.insert(id); } -void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc) +void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody) { Q_ASSERT(m_scopeType == ScopeType::QMLScope); - m_injectedSignalIdentifiers.insert(id, {method, loc}); + m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody}); } void ScopeTree::insertPropertyIdentifier(QString id) @@ -84,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); } } @@ -146,31 +154,37 @@ 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)) { auto qmlScope = currentScope->getCurrentQMLScope(); - MethodUsage methodUsage = qmlScope->m_injectedSignalIdentifiers[idLocationPair.first]; + auto methodUsages = qmlScope->m_injectedSignalIdentifiers.values(idLocationPair.first); + auto location = idLocationPair.second; + // sort the list of signal handlers by their occurrence in the source code + // then, we select the first one whose location is after the unqualified id + // and go one step backwards to get the one which we actually need + std::sort(methodUsages.begin(), methodUsages.end(), [](const MethodUsage m1, const MethodUsage m2) { + return m1.loc.startLine < m2.loc.startLine || (m1.loc.startLine == m2.loc.startLine && m1.loc.startColumn < m2.loc.startColumn); + }); + auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(), [&location](MethodUsage methodUsage) { + return location.startLine < methodUsage.loc.startLine || (location.startLine == methodUsage.loc.startLine && location.startColumn < methodUsage.loc.startColumn); + }); + auto methodUsage = *(--oneBehindIt); colorOut.write("Note:", Info); colorOut.write(idLocationPair.first + QString::asprintf(" is accessible in this scope because you are handling a signal at %d:%d\n", methodUsage.loc.startLine, methodUsage.loc.startColumn), Normal); colorOut.write("Consider using a function instead\n", Normal); IssueLocationWithContext context {code, methodUsage.loc}; colorOut.write(context.beforeText + QLatin1Char(' ')); - colorOut.write("function(", Hint); + colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint); const auto parameters = methodUsage.method.parameterNames(); for (int numParams = parameters.size(); numParams > 0; --numParams) { colorOut.write(parameters.at(parameters.size() - numParams), Hint); @@ -178,10 +192,10 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan colorOut.write(", ", Hint); } } - colorOut.write(")", Hint); + colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint); colorOut.write(" {...", Normal); } - colorOut.write("\n", Normal); + colorOut.write("\n\n\n", Normal); } for (auto const& childScope: currentScope->m_childScopes) { workQueue.enqueue(childScope); diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h index cb495584c1..872a509123 100644 --- a/tools/qmllint/scopetree.h +++ b/tools/qmllint/scopetree.h @@ -56,6 +56,7 @@ struct MethodUsage { LanguageUtils::FakeMetaMethod method; QQmlJS::AST::SourceLocation loc; + bool hasMultilineHandlerBody; }; class ColorOutput; @@ -70,11 +71,11 @@ public: void insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope); void insertQMLIdentifier(QString id); - void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc); + void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody); 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; @@ -88,7 +89,7 @@ public: private: QSet<QString> m_currentScopeJSIdentifiers; QSet<QString> m_currentScopeQMLIdentifiers; - QHash<QString, MethodUsage> m_injectedSignalIdentifiers; // need iteration in insertion order for hints, rarely more than 4 parameters needed + QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers; QMap<QString, LanguageUtils::FakeMetaMethod> m_methods; QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers; QVector<ScopeTree*> m_childScopes; |