diff options
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 195 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor_p.h | 36 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope.cpp | 20 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypereader.cpp | 36 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypereader_p.h | 18 | ||||
-rw-r--r-- | tools/qmllint/checkidentifiers.cpp | 4 | ||||
-rw-r--r-- | tools/qmllint/findwarnings.cpp | 168 | ||||
-rw-r--r-- | tools/qmllint/findwarnings.h | 30 | ||||
-rw-r--r-- | tools/qmllint/main.cpp | 3 |
9 files changed, 220 insertions, 290 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 0cb5632d40..ac21c7715e 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -28,21 +28,46 @@ #include "qqmljsimportvisitor_p.h" +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> + QT_BEGIN_NAMESPACE using namespace QQmlJS::AST; -QQmlJSScope::Ptr QQmlJSImportVisitor::result(const QString &scopeName) const +QQmlJSImportVisitor::QQmlJSImportVisitor( + QQmlJSImporter *importer, const QString &implicitImportDirectory, + const QStringList &qmltypesFiles) + : m_implicitImportDirectory(implicitImportDirectory) + , m_qmltypesFiles(qmltypesFiles) + , m_currentScope(QQmlJSScope::create(QQmlJSScope::JSFunctionScope)) + , m_importer(importer) +{ + m_globalScope = m_currentScope; +} + +void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QString &name) +{ + m_currentScope = QQmlJSScope::create(type, m_currentScope); + m_currentScope->setBaseTypeName(name); + m_currentScope->setIsComposite(true); +} + +void QQmlJSImportVisitor::leaveEnvironment() +{ + m_currentScope = m_currentScope->parentScope(); +} + +QQmlJSScope::Ptr QQmlJSImportVisitor::result() const { QQmlJSScope::Ptr result = QQmlJSScope::create(); result->setIsComposite(true); - result->setInternalName(scopeName); - result->setBaseTypeName(m_rootObject->baseTypeName()); - const auto properties = m_rootObject->properties(); + result->setBaseTypeName(m_qmlRootScope->baseTypeName()); + const auto properties = m_qmlRootScope->properties(); for (auto property : properties) { if (property.isAlias()) { - const auto it = m_objects.find(property.typeName()); - if (it != m_objects.end()) + const auto it = m_scopesById.find(property.typeName()); + if (it != m_scopesById.end()) property.setType(QQmlJSScope::ConstPtr(*it)); result->addProperty(property); } else { @@ -50,34 +75,47 @@ QQmlJSScope::Ptr QQmlJSImportVisitor::result(const QString &scopeName) const } } - for (const auto &method : m_rootObject->methods()) + for (const auto &method : m_qmlRootScope->methods()) result->addMethod(method); - for (const auto &enumerator : m_rootObject->enums()) + for (const auto &enumerator : m_qmlRootScope->enums()) result->addEnum(enumerator); + result->resolveTypes(m_rootScopeImports); return result; } +bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *) +{ + m_rootScopeImports = m_importer->importBuiltins(); + + if (!m_qmltypesFiles.isEmpty()) + m_rootScopeImports.insert(m_importer->importQmltypes(m_qmltypesFiles)); + + m_rootScopeImports.insert(m_importer->importDirectory(m_implicitImportDirectory)); + + m_errors.append(m_importer->takeWarnings()); + return true; +} + bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) { - QQmlJSScope::Ptr scope = QQmlJSScope::create(); QString superType; for (auto segment = definition->qualifiedTypeNameId; segment; segment = segment->next) { if (!superType.isEmpty()) superType.append(u'.'); superType.append(segment->name.toString()); } - scope->setBaseTypeName(superType); - if (!m_rootObject) - m_rootObject = scope; - m_currentObjects.append(scope); + enterEnvironment(QQmlJSScope::QMLScope, superType); + if (!m_qmlRootScope) + m_qmlRootScope = m_currentScope; + return true; } void QQmlJSImportVisitor::endVisit(UiObjectDefinition *) { - m_currentObjects.pop_back(); + leaveEnvironment(); } bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) @@ -92,7 +130,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) method.addParameter(param->name.toString(), param->type->name.toString()); param = param->next; } - currentObject()->addMethod(method); + m_currentScope->addMethod(method); break; } case UiPublicMember::Property: { @@ -112,48 +150,81 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) isAlias, 0 }; - currentObject()->addProperty(prop); + m_currentScope->addProperty(prop); break; } } return true; } -bool QQmlJSImportVisitor::visit(UiSourceElement *sourceElement) +void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr) { - if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) { - QQmlJSMetaMethod method; - method.setMethodName(fexpr->name.toString()); - method.setMethodType(QQmlJSMetaMethod::Method); - FormalParameterList *parameters = fexpr->formals; - while (parameters) { - method.addParameter(parameters->element->bindingIdentifier.toString(), QString()); - parameters = parameters->next; + using namespace QQmlJS::AST; + auto name = fexpr->name.toString(); + if (!name.isEmpty()) { + if (m_currentScope->scopeType() == QQmlJSScope::QMLScope) { + QQmlJSMetaMethod method(name, QStringLiteral("void")); + method.setMethodType(QQmlJSMetaMethod::Method); + FormalParameterList *parameters = fexpr->formals; + while (parameters) { + method.addParameter(parameters->element->bindingIdentifier.toString(), QString()); + parameters = parameters->next; + } + m_currentScope->addMethod(method); + } else { + m_currentScope->insertJSIdentifier( + name, { + QQmlJSScope::JavaScriptIdentifier::LexicalScoped, + fexpr->firstSourceLocation() + }); } - currentObject()->addMethod(method); - } else if (ClassExpression *clexpr = sourceElement->sourceElement->asClassDefinition()) { - QQmlJSMetaProperty prop { clexpr->name.toString(), QString(), false, false, false, false, 1 }; - currentObject()->addProperty(prop); - } else if (cast<VariableStatement *>(sourceElement->sourceElement)) { - // nothing to do + enterEnvironment(QQmlJSScope::JSFunctionScope, name); } else { - const auto loc = sourceElement->firstSourceLocation(); - m_errors.append({ - QStringLiteral("unsupportedd sourceElement %1") - .arg(sourceElement->sourceElement->kind), - QtWarningMsg, - loc - }); + enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("<anon>")); } +} + +bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr) +{ + visitFunctionExpressionHelper(fexpr); + return true; +} + +void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *) +{ + leaveEnvironment(); +} + +bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl) +{ + visitFunctionExpressionHelper(fdecl); + return true; +} + +void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *) +{ + leaveEnvironment(); +} + +bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast) +{ + QQmlJSMetaProperty prop { ast->name.toString(), QString(), false, false, false, false, 1 }; + m_currentScope->addProperty(prop); + enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString()); return true; } +void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *) +{ + leaveEnvironment(); +} + bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) { if (scriptBinding->qualifiedId->name == QLatin1String("id")) { const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement); const auto *idExprension = cast<IdentifierExpression *>(statement->expression); - m_objects.insert(idExprension->name.toString(), currentObject()); + m_scopesById.insert(idExprension->name.toString(), m_currentScope); } return true; } @@ -163,7 +234,51 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) QQmlJSMetaEnum qmlEnum(uied->name.toString()); for (const auto *member = uied->members; member; member = member->next) qmlEnum.addKey(member->member.toString()); - currentObject()->addEnum(qmlEnum); + m_currentScope->addEnum(qmlEnum); + return true; +} + +bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import) +{ + // construct path + QString prefix = QLatin1String(""); + if (import->asToken.isValid()) { + prefix += import->importId; + } + auto filename = import->fileName.toString(); + if (!filename.isEmpty()) { + const QFileInfo file(filename); + const QFileInfo path(file.isRelative() ? QDir(m_implicitImportDirectory).filePath(filename) + : filename); + if (path.isDir()) { + m_rootScopeImports.insert(m_importer->importDirectory(path.canonicalFilePath(), prefix)); + } else if (path.isFile()) { + const auto scope = m_importer->importFile(path.canonicalFilePath()); + m_rootScopeImports.insert(prefix.isEmpty() ? scope->internalName() : prefix, scope); + } + + } + + QString path {}; + if (!import->importId.isEmpty()) { + // TODO: do not put imported ids into the same space as qml IDs + const QString importId = import->importId.toString(); + m_scopesById.insert(importId, m_rootScopeImports.value(importId)); + } + auto uri = import->importUri; + while (uri) { + path.append(uri->name); + path.append(u'/'); + uri = uri->next; + } + path.chop(1); + + const auto imported = m_importer->importModule( + path, prefix, import->version ? import->version->version : QTypeRevision()); + + m_rootScopeImports.insert(imported); + + m_errors.append(m_importer->takeWarnings()); return true; } diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h index 8f0d937968..1d50881441 100644 --- a/src/qmlcompiler/qqmljsimportvisitor_p.h +++ b/src/qmlcompiler/qqmljsimportvisitor_p.h @@ -43,31 +43,51 @@ #include <private/qqmljsast_p.h> #include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qqmljsimporter_p.h> QT_BEGIN_NAMESPACE class QQmlJSImportVisitor : public QQmlJS::AST::Visitor { public: - QQmlJSScope::Ptr result(const QString &scopeName) const; + QQmlJSImportVisitor(QQmlJSImporter *importer, const QString &implicitImportDirectory, + const QStringList &qmltypesFiles = QStringList()); + + QQmlJSScope::Ptr result() const; QList<QQmlJS::DiagnosticMessage> errors() const { return m_errors; } -private: +protected: + bool visit(QQmlJS::AST::UiProgram *) override; bool visit(QQmlJS::AST::UiObjectDefinition *) override; void endVisit(QQmlJS::AST::UiObjectDefinition *) override; bool visit(QQmlJS::AST::UiPublicMember *) override; - bool visit(QQmlJS::AST::UiSourceElement *) override; bool visit(QQmlJS::AST::UiScriptBinding *) override; bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; + bool visit(QQmlJS::AST::FunctionExpression *fexpr) override; + void endVisit(QQmlJS::AST::FunctionExpression *) override; + bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override; + void endVisit(QQmlJS::AST::FunctionDeclaration *) override; + bool visit(QQmlJS::AST::ClassExpression *ast) override; + void endVisit(QQmlJS::AST::ClassExpression *) override; + bool visit(QQmlJS::AST::UiImport *import) override; void throwRecursionDepthError() override; - QQmlJSScope::Ptr currentObject() const { return m_currentObjects.back(); } - - QVector<QQmlJSScope::Ptr> m_currentObjects; - QQmlJSScope::ConstPtr m_rootObject; - QHash<QString, QQmlJSScope::Ptr> m_objects; + QString m_implicitImportDirectory; + QStringList m_qmltypesFiles; + QQmlJSScope::Ptr m_currentScope; + QQmlJSScope::ConstPtr m_qmlRootScope; + QQmlJSScope::ConstPtr m_globalScope; + QHash<QString, QQmlJSScope::ConstPtr> m_scopesById; + QHash<QString, QQmlJSScope::ConstPtr> m_rootScopeImports; + QQmlJSImporter *m_importer; QList<QQmlJS::DiagnosticMessage> m_errors; + + void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name); + void leaveEnvironment(); + +private: + void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr); }; QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index e515431758..ed434c17e7 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -44,13 +44,8 @@ QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope) QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &parentScope) { QSharedPointer<QQmlJSScope> childScope(new QQmlJSScope{type, parentScope}); - if (parentScope) { - Q_ASSERT(type != QQmlJSScope::QMLScope - || !parentScope->m_parentScope - || parentScope->parentScope()->m_scopeType == QQmlJSScope::QMLScope - || parentScope->parentScope()->m_internalName == QLatin1String("global")); + if (parentScope) parentScope->m_childScopes.push_back(childScope); - } return childScope; } @@ -188,19 +183,10 @@ bool QQmlJSScope::Export::isValid() const QQmlJSScope QDeferredFactory<QQmlJSScope>::create() const { - QQmlJSTypeReader typeReader(m_filePath); + QQmlJSTypeReader typeReader(m_importer, m_filePath); QQmlJSScope::Ptr result = typeReader(); - m_importer->m_warnings.append(typeReader.errors()); - - QQmlJSImporter::AvailableTypes types; - types.qmlNames.insert(m_importer->importDirectory(QFileInfo(m_filePath).canonicalPath())); - - const auto imports = typeReader.imports(); - for (const auto &import : imports) - m_importer->importHelper(import.module, &types, import.prefix, import.version); - - result->resolveTypes(types.qmlNames); + result->setInternalName(QFileInfo(m_filePath).baseName()); return std::move(*result); } diff --git a/src/qmlcompiler/qqmljstypereader.cpp b/src/qmlcompiler/qqmljstypereader.cpp index 0e12c8de95..578b0378c3 100644 --- a/src/qmlcompiler/qqmljstypereader.cpp +++ b/src/qmlcompiler/qqmljstypereader.cpp @@ -40,34 +40,6 @@ QT_BEGIN_NAMESPACE -static QList<QQmlJSTypeReader::Import> parseHeaders(QQmlJS::AST::UiHeaderItemList *header) -{ - using namespace QQmlJS::AST; - QList<QQmlJSTypeReader::Import> imports; - - for (; header; header = header->next) { - auto import = cast<UiImport *>(header->headerItem); - if (!import) - continue; - - QString path; - auto uri = import->importUri; - while (uri) { - path.append(uri->name); - path.append(u'.'); - uri = uri->next; - } - path.chop(1); - imports.append({ - path, - import->version ? import->version->version : QTypeRevision(), - import->asToken.isValid() ? import->importId.toString() : QString() - }); - } - - return imports; -} - static QQmlJSScope::Ptr parseProgram(QQmlJS::AST::Program *program, const QString &name) { using namespace QQmlJS::AST; @@ -126,11 +98,11 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()() if (!isJavaScript) { QQmlJS::AST::UiProgram *program = parser.ast(); - m_imports = parseHeaders(program->headers); - QQmlJSImportVisitor membersVisitor; - program->members->accept(&membersVisitor); + QQmlJSImportVisitor membersVisitor(m_importer, QFileInfo(m_file).canonicalPath(), + m_qmltypesFiles); + program->accept(&membersVisitor); m_errors = membersVisitor.errors(); - return membersVisitor.result(scopeName); + return membersVisitor.result(); } // TODO: Anything special to do with ES modules here? diff --git a/src/qmlcompiler/qqmljstypereader_p.h b/src/qmlcompiler/qqmljstypereader_p.h index 4a6c2c2cd0..4e9d26c08c 100644 --- a/src/qmlcompiler/qqmljstypereader_p.h +++ b/src/qmlcompiler/qqmljstypereader_p.h @@ -40,6 +40,7 @@ // We mean it. #include "qqmljsscope_p.h" +#include "qqmljsimporter_p.h" #include <QtQml/private/qqmljsastfwd_p.h> #include <QtQml/private/qqmljsdiagnosticmessage_p.h> @@ -52,21 +53,20 @@ QT_BEGIN_NAMESPACE class QQmlJSTypeReader { public: - struct Import { - QString module; - QTypeRevision version; - QString prefix; - }; - - QQmlJSTypeReader(const QString &file) : m_file(file) {} + QQmlJSTypeReader(QQmlJSImporter *importer, const QString &file, + const QStringList &qmltypesFiles = QStringList()) + : m_importer(importer) + , m_file(file) + , m_qmltypesFiles(qmltypesFiles) + {} QQmlJSScope::Ptr operator()(); - QList<Import> imports() const { return m_imports; } QList<QQmlJS::DiagnosticMessage> errors() const { return m_errors; } private: + QQmlJSImporter *m_importer; QString m_file; - QList<Import> m_imports; + QStringList m_qmltypesFiles; QList<QQmlJS::DiagnosticMessage> m_errors; }; diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp index b7d331521c..09c39ddd63 100644 --- a/tools/qmllint/checkidentifiers.cpp +++ b/tools/qmllint/checkidentifiers.cpp @@ -373,8 +373,8 @@ bool CheckIdentifiers::operator()( printContext(m_code, m_colorOut, location); - // root(JS) --> program(qml) --> (first element) - const auto firstElement = root->childScopes()[0]->childScopes()[0]; + // root(JS) --> (first element) + const auto firstElement = root->childScopes()[0]; if (firstElement->properties().contains(memberAccessBase.m_name) || firstElement->methods().contains(memberAccessBase.m_name) || firstElement->enums().contains(memberAccessBase.m_name)) { diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp index 962ccaa1c6..6f8c64aabc 100644 --- a/tools/qmllint/findwarnings.cpp +++ b/tools/qmllint/findwarnings.cpp @@ -43,18 +43,6 @@ #include <QtCore/qdiriterator.h> #include <QtCore/qscopedvaluerollback.h> -void FindWarningVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QString &name) -{ - m_currentScope = QQmlJSScope::create(type, m_currentScope); - m_currentScope->setBaseTypeName(name); - m_currentScope->setIsComposite(true); -} - -void FindWarningVisitor::leaveEnvironment() -{ - m_currentScope = m_currentScope->parentScope(); -} - void FindWarningVisitor::importExportedNames(QQmlJSScope::ConstPtr scope) { QList<QQmlJSScope::ConstPtr> scopes; @@ -123,47 +111,10 @@ void FindWarningVisitor::flushPendingSignalParameters() void FindWarningVisitor::throwRecursionDepthError() { - m_errors.append({ - QStringLiteral("Maximum statement or expression depth exceeded"), - QtCriticalMsg, - QQmlJS::SourceLocation() - }); + QQmlJSImportVisitor::throwRecursionDepthError(); m_visitFailed = true; } -bool FindWarningVisitor::visit(QQmlJS::AST::UiProgram *) -{ - enterEnvironment(QQmlJSScope::QMLScope, "program"); - m_rootScopeImports = m_importer.importBuiltins(); - - if (!m_qmltypesFiles.isEmpty()) { - const auto baseTypes = m_importer.importQmltypes(m_qmltypesFiles); - m_rootScopeImports.insert(baseTypes); - } - - const auto imported = m_importer.importDirectory(QFileInfo(m_filePath).canonicalPath()); - m_rootScopeImports.insert(imported); - - m_errors.append(m_importer.takeWarnings()); - return true; -} - -void FindWarningVisitor::endVisit(QQmlJS::AST::UiProgram *) -{ - leaveEnvironment(); -} - -bool FindWarningVisitor::visit(QQmlJS::AST::ClassExpression *ast) -{ - enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString()); - return true; -} - -void FindWarningVisitor::endVisit(QQmlJS::AST::ClassExpression *) -{ - leaveEnvironment(); -} - bool FindWarningVisitor::visit(QQmlJS::AST::ClassDeclaration *ast) { enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString()); @@ -301,14 +252,12 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) // found id auto expstat = cast<ExpressionStatement *>(uisb->statement); auto identexp = cast<IdentifierExpression *>(expstat->expression); - m_qmlid2scope.insert(identexp->name.toString(), m_currentScope); + m_scopesById.insert(identexp->name.toString(), m_currentScope); // Figure out whether the current scope is the root scope. if (auto parentScope = m_currentScope->parentScope()) { - if (auto grandParentScope = parentScope->parentScope()) { - if (!grandParentScope->parentScope()) - m_rootId = identexp->name.toString(); - } + if (!parentScope->parentScope()) + m_rootId = identexp->name.toString(); } } else { const QString signal = signalName(name); @@ -389,21 +338,18 @@ bool FindWarningVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp) } FindWarningVisitor::FindWarningVisitor( - QStringList qmlImportPaths, QStringList qmltypesFiles, QString code, QString fileName, + QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName, bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle) - : m_rootScope(QQmlJSScope::create(QQmlJSScope::JSFunctionScope)), - m_qmltypesFiles(std::move(qmltypesFiles)), + : QQmlJSImportVisitor(importer, QFileInfo {fileName}.canonicalPath(), qmltypesFiles), m_code(std::move(code)), m_rootId(QLatin1String("<id>")), m_filePath(std::move(fileName)), m_colorOut(silent), m_warnUnqualified(warnUnqualified), m_warnWithStatement(warnWithStatement), - m_warnInheritanceCycle(warnInheritanceCycle), - m_importer(qmlImportPaths) + m_warnInheritanceCycle(warnInheritanceCycle) { - m_rootScope->setInternalName("global"); - m_currentScope = m_rootScope; + m_currentScope->setInternalName("global"); // setup color output m_colorOut.insertMapping(Error, ColorOutput::RedForeground); @@ -472,7 +418,7 @@ bool FindWarningVisitor::check() // now that all ids are known, revisit any Connections whose target were perviously unknown for (auto const &outstandingConnection: m_outstandingConnections) { - auto targetScope = m_qmlid2scope[outstandingConnection.targetName]; + auto targetScope = m_scopesById[outstandingConnection.targetName]; if (outstandingConnection.scope && !targetScope.isNull()) outstandingConnection.scope->addMethods(targetScope->methods()); QScopedValueRollback<QQmlJSScope::Ptr> rollback(m_currentScope, outstandingConnection.scope); @@ -483,7 +429,7 @@ bool FindWarningVisitor::check() return true; CheckIdentifiers check(&m_colorOut, m_code, m_rootScopeImports, m_filePath); - return check(m_qmlid2scope, m_signalHandlers, m_memberAccessChains, m_rootScope, m_rootId); + return check(m_scopesById, m_signalHandlers, m_memberAccessChains, m_globalScope, m_rootId); } bool FindWarningVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl) @@ -502,48 +448,6 @@ bool FindWarningVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl) return true; } -void FindWarningVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr) -{ - using namespace QQmlJS::AST; - auto name = fexpr->name.toString(); - if (!name.isEmpty()) { - if (m_currentScope->scopeType() == QQmlJSScope::QMLScope) { - m_currentScope->addMethod(QQmlJSMetaMethod(name, QLatin1String("void"))); - } else { - m_currentScope->insertJSIdentifier( - name, { - QQmlJSScope::JavaScriptIdentifier::LexicalScoped, - fexpr->firstSourceLocation() - }); - } - enterEnvironment(QQmlJSScope::JSFunctionScope, name); - } else { - enterEnvironment(QQmlJSScope::JSFunctionScope, QLatin1String("<anon>")); - } -} - -bool FindWarningVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr) -{ - visitFunctionExpressionHelper(fexpr); - return true; -} - -void FindWarningVisitor::endVisit(QQmlJS::AST::FunctionExpression *) -{ - leaveEnvironment(); -} - -bool FindWarningVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl) -{ - visitFunctionExpressionHelper(fdecl); - return true; -} - -void FindWarningVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *) -{ - leaveEnvironment(); -} - bool FindWarningVisitor::visit(QQmlJS::AST::FormalParameterList *fpl) { for (auto const &boundName : fpl->boundNames()) { @@ -556,50 +460,6 @@ bool FindWarningVisitor::visit(QQmlJS::AST::FormalParameterList *fpl) return true; } -bool FindWarningVisitor::visit(QQmlJS::AST::UiImport *import) -{ - // construct path - QString prefix = QLatin1String(""); - if (import->asToken.isValid()) { - prefix += import->importId; - } - auto filename = import->fileName.toString(); - if (!filename.isEmpty()) { - const QFileInfo file(filename); - const QFileInfo path(file.isRelative() ? QFileInfo(m_filePath).dir().filePath(filename) - : filename); - if (path.isDir()) { - m_rootScopeImports.insert(m_importer.importDirectory(path.canonicalFilePath(), prefix)); - } else if (path.isFile()) { - const auto scope = m_importer.importFile(path.canonicalFilePath()); - m_rootScopeImports.insert(prefix.isEmpty() ? scope->internalName() : prefix, scope); - } - - } - - QString path {}; - if (!import->importId.isEmpty()) { - // TODO: do not put imported ids into the same space as qml IDs - const QString importId = import->importId.toString(); - m_qmlid2scope.insert(importId, m_rootScopeImports.value(importId)); - } - auto uri = import->importUri; - while (uri) { - path.append(uri->name); - path.append("/"); - uri = uri->next; - } - path.chop(1); - - const auto imported = m_importer.importModule( - path, prefix, import->version ? import->version->version : QTypeRevision()); - - m_rootScopeImports.insert(imported); - - m_errors.append(m_importer.takeWarnings()); - return true; -} - bool FindWarningVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) { QQmlJSMetaEnum qmlEnum(uied->name.toString()); @@ -689,8 +549,8 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod) targetScope = m_rootScopeImports.value(scope->baseTypeName()); } else { // there was a target, check if we already can find it - auto scopeIt = m_qmlid2scope.find(target); - if (scopeIt != m_qmlid2scope.end()) { + auto scopeIt = m_scopesById.find(target); + if (scopeIt != m_scopesById.end()) { targetScope = *scopeIt; } else { m_outstandingConnections.push_back({target, m_currentScope, uiod}); @@ -727,8 +587,8 @@ void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *) auto childScope = m_currentScope; leaveEnvironment(); - if (m_currentScope->baseTypeName() == QStringLiteral("Component") - || m_currentScope->baseTypeName() == QStringLiteral("program")) { + if (m_currentScope == m_globalScope + || m_currentScope->baseTypeName() == QStringLiteral("Component")) { return; } diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h index 7d07114b32..da32f4c9d5 100644 --- a/tools/qmllint/findwarnings.h +++ b/tools/qmllint/findwarnings.h @@ -45,6 +45,7 @@ #include <QtQmlCompiler/private/qqmljstypedescriptionreader_p.h> #include <QtQmlCompiler/private/qqmljsscope_p.h> #include <QtQmlCompiler/private/qqmljsimporter_p.h> +#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h> #include <QtQml/private/qqmldirparser_p.h> #include <QtQml/private/qqmljsastvisitor_p.h> @@ -52,34 +53,27 @@ #include <QtCore/qscopedpointer.h> -class FindWarningVisitor : public QQmlJS::AST::Visitor +class FindWarningVisitor : public QQmlJSImportVisitor { Q_DISABLE_COPY_MOVE(FindWarningVisitor) public: explicit FindWarningVisitor( - QStringList qmlImportPaths, QStringList qmltypesFiles, QString code, QString fileName, + QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName, bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle); ~FindWarningVisitor() override = default; bool check(); private: - QQmlJSImporter::ImportedTypes m_rootScopeImports; - QHash<QQmlJS::SourceLocation, SignalHandler> m_signalHandlers; QQmlJS::SourceLocation m_pendingSingalHandler; MemberAccessChains m_memberAccessChains; - QQmlJSScope::Ptr m_rootScope; - QQmlJSScope::Ptr m_currentScope; QQmlJS::AST::ExpressionNode *m_fieldMemberBase = nullptr; - QStringList m_qmltypesFiles; QString m_code; - QHash<QString, QQmlJSScope::ConstPtr> m_qmlid2scope; QString m_rootId; QString m_filePath; QSet<QString> m_unknownImports; - QList<QQmlJS::DiagnosticMessage> m_errors; ColorOutput m_colorOut; bool m_visitFailed = false; @@ -96,11 +90,6 @@ private: QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered - QQmlJSImporter m_importer; - - void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name); - void leaveEnvironment(); - void importExportedNames(QQmlJSScope::ConstPtr scope); void parseHeaders(QQmlJS::AST::UiHeaderItemList *headers); @@ -110,12 +99,6 @@ private: void throwRecursionDepthError() override; // start block/scope handling - bool visit(QQmlJS::AST::UiProgram *ast) override; - void endVisit(QQmlJS::AST::UiProgram *ast) override; - - bool visit(QQmlJS::AST::ClassExpression *ast) override; - void endVisit(QQmlJS::AST::ClassExpression *ast) override; - bool visit(QQmlJS::AST::ClassDeclaration *ast) override; void endVisit(QQmlJS::AST::ClassDeclaration *ast) override; @@ -140,18 +123,11 @@ private: bool visit(QQmlJS::AST::WithStatement *withStatement) override; void endVisit(QQmlJS::AST::WithStatement *ast) override; - void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr); - bool visit(QQmlJS::AST::FunctionExpression *fexpr) override; - void endVisit(QQmlJS::AST::FunctionExpression *fexpr) override; - - bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override; - void endVisit(QQmlJS::AST::FunctionDeclaration *fdecl) override; /* --- end block handling --- */ bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override; bool visit(QQmlJS::AST::FormalParameterList *fpl) override; - bool visit(QQmlJS::AST::UiImport *import) override; bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; bool visit(QQmlJS::AST::UiObjectBinding *uiob) override; void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override; diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 1112f64d7c..a877b20335 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -86,7 +86,8 @@ static bool lint_file(const QString &filename, const bool silent, const bool war if (success && !isJavaScript) { auto root = parser.rootNode(); - FindWarningVisitor v { qmlImportPaths, qmltypesFiles, code, filename, silent, + QQmlJSImporter importer(qmlImportPaths); + FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent, warnUnqualified, warnWithStatement, warnInheritanceCycle }; root->accept(&v); success = v.check(); |