aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2019-08-09 16:20:22 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2019-08-09 16:20:22 +0200
commit36990a732eab2dc16bee89715662f6d3d8b3a0cd (patch)
treed835ba67426e12e51dc1028e8ebac9087a3d43b0 /tools
parentcb37a1d0c3fce7951f5d7f1fb4a04f90e038f01b (diff)
parentd1b72c98b4e617530bfb23c3a5b7ebc68c15c089 (diff)
Merge remote-tracking branch 'origin/dev' into wip/qt6
Diffstat (limited to 'tools')
-rw-r--r--tools/qmllint/findunqualified.cpp35
-rw-r--r--tools/qmllint/findunqualified.h1
-rw-r--r--tools/qmllint/scopetree.cpp50
-rw-r--r--tools/qmllint/scopetree.h7
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 &param : 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;