From 48426aa705cb7ed88489200864c2fc0143058671 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 29 Sep 2020 15:49:16 +0200 Subject: Move the import handling code out of qmllint We want to re-use this logic in other places. Change-Id: I63cbee86a83265ddd241a4fae9ce8c48f38b5f18 Reviewed-by: Fabian Kosmale --- tools/qmllint/.prev_CMakeLists.txt | 1 + tools/qmllint/CMakeLists.txt | 1 + tools/qmllint/checkidentifiers.cpp | 2 +- tools/qmllint/checkidentifiers.h | 4 +- tools/qmllint/findwarnings.cpp | 277 +------------------------------------ tools/qmllint/findwarnings.h | 66 +-------- 6 files changed, 9 insertions(+), 342 deletions(-) (limited to 'tools/qmllint') diff --git a/tools/qmllint/.prev_CMakeLists.txt b/tools/qmllint/.prev_CMakeLists.txt index 8e4affe73c..cf5dc7e28e 100644 --- a/tools/qmllint/.prev_CMakeLists.txt +++ b/tools/qmllint/.prev_CMakeLists.txt @@ -11,6 +11,7 @@ qt_add_tool(${target_name} ../shared/componentversion.cpp ../shared/componentversion.h ../shared/importedmembersvisitor.cpp ../shared/importedmembersvisitor.h ../shared/metatypes.h + ../shared/qmljsimporter.cpp ../shared/qmljsimporter.h ../shared/qmljstypereader.cpp ../shared/qmljstypereader.h ../shared/scopetree.cpp ../shared/scopetree.h ../shared/typedescriptionreader.cpp ../shared/typedescriptionreader.h diff --git a/tools/qmllint/CMakeLists.txt b/tools/qmllint/CMakeLists.txt index 663bcb9cbf..ba4cd62e65 100644 --- a/tools/qmllint/CMakeLists.txt +++ b/tools/qmllint/CMakeLists.txt @@ -12,6 +12,7 @@ qt_add_tool(${target_name} ../shared/componentversion.cpp ../shared/componentversion.h ../shared/importedmembersvisitor.cpp ../shared/importedmembersvisitor.h ../shared/metatypes.h + ../shared/qmljsimporter.cpp ../shared/qmljsimporter.h ../shared/qmljstypereader.cpp ../shared/qmljstypereader.h ../shared/scopetree.cpp ../shared/scopetree.h ../shared/typedescriptionreader.cpp ../shared/typedescriptionreader.h diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp index a75c9c9b83..9a6495310d 100644 --- a/tools/qmllint/checkidentifiers.cpp +++ b/tools/qmllint/checkidentifiers.cpp @@ -84,7 +84,7 @@ void CheckIdentifiers::printContext(const QQmlJS::SourceLocation &location) cons } static bool walkViaParentAndAttachedScopes(ScopeTree::ConstPtr rootType, - const FindWarningVisitor::ImportedTypes &allTypes, + const QmlJSImporter::ImportedTypes &allTypes, std::function visit) { if (rootType == nullptr) diff --git a/tools/qmllint/checkidentifiers.h b/tools/qmllint/checkidentifiers.h index 653b398301..bdd83f1e57 100644 --- a/tools/qmllint/checkidentifiers.h +++ b/tools/qmllint/checkidentifiers.h @@ -38,7 +38,7 @@ class CheckIdentifiers { public: CheckIdentifiers(ColorOutput *colorOut, const QString &code, - const FindWarningVisitor::ImportedTypes &types, const QString &fileName) : + const QmlJSImporter::ImportedTypes &types, const QString &fileName) : m_colorOut(colorOut), m_code(code), m_types(types), m_fileName(fileName) {} @@ -53,7 +53,7 @@ private: ColorOutput *m_colorOut = nullptr; QString m_code; - FindWarningVisitor::ImportedTypes m_types; + QmlJSImporter::ImportedTypes m_types; QString m_fileName; }; diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp index 09ef63d4bd..b28a4ee695 100644 --- a/tools/qmllint/findwarnings.cpp +++ b/tools/qmllint/findwarnings.cpp @@ -43,21 +43,6 @@ #include #include -static const QString prefixedName(const QString &prefix, const QString &name) -{ - Q_ASSERT(!prefix.endsWith('.')); - return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name); -} - -static QQmlDirParser createQmldirParserForFile(const QString &filename) -{ - QFile f(filename); - f.open(QFile::ReadOnly); - QQmlDirParser parser; - parser.parse(f.readAll()); - return parser; -} - void FindWarningVisitor::enterEnvironment(ScopeType type, const QString &name) { m_currentScope = ScopeTree::create(type, m_currentScope); @@ -70,266 +55,6 @@ void FindWarningVisitor::leaveEnvironment() m_currentScope = m_currentScope->parentScope(); } -static const QLatin1String SlashQmldir = QLatin1String("/qmldir"); -static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes"); - -void FindWarningVisitor::Importer::readQmltypes( - const QString &filename, QHash *objects) -{ - const QFileInfo fileInfo(filename); - if (!fileInfo.exists()) { - m_warnings.append(QLatin1String("QML types file does not exist: ") + filename); - return; - } - - if (fileInfo.isDir()) { - m_warnings.append(QLatin1String("QML types file cannot be a directory: ") + filename); - return; - } - - QFile file(filename); - file.open(QFile::ReadOnly); - TypeDescriptionReader reader { filename, file.readAll() }; - QStringList dependencies; - auto succ = reader(objects, &dependencies); - if (!succ) - m_warnings.append(reader.errorMessage()); -} - -FindWarningVisitor::Importer::Import FindWarningVisitor::Importer::readQmldir(const QString &path) -{ - Import result; - auto reader = createQmldirParserForFile(path + SlashQmldir); - result.imports.append(reader.imports()); - result.dependencies.append(reader.dependencies()); - - QHash qmlComponents; - const auto components = reader.components(); - for (auto it = components.begin(), end = components.end(); it != end; ++it) { - const QString filePath = path + QLatin1Char('/') + it->fileName; - if (!QFile::exists(filePath)) { - m_warnings.append(it->fileName + QLatin1String(" is listed as component in ") - + path + SlashQmldir - + QLatin1String(" but does not exist.\n")); - continue; - } - - auto mo = qmlComponents.find(it.key()); - if (mo == qmlComponents.end()) - mo = qmlComponents.insert(it.key(), localFile2ScopeTree(filePath)); - - (*mo)->addExport( - it.key(), reader.typeNamespace(), - ComponentVersion(it->version)); - } - for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) - result.objects.insert(it.key(), it.value()); - - if (!reader.plugins().isEmpty() && QFile::exists(path + SlashPluginsDotQmltypes)) - readQmltypes(path + SlashPluginsDotQmltypes, &result.objects); - - const auto scripts = reader.scripts(); - for (const auto &script : scripts) { - const QString filePath = path + QLatin1Char('/') + script.fileName; - result.scripts.insert(script.nameSpace, localFile2ScopeTree(filePath)); - } - return result; -} - -void FindWarningVisitor::Importer::importDependencies( - const FindWarningVisitor::Importer::Import &import, - FindWarningVisitor::ImportedTypes *types, const QString &prefix, QTypeRevision version) -{ - // Import the dependencies with an invalid prefix. The prefix will never be matched by actual - // QML code but the C++ types will be visible. - const QString invalidPrefix = QString::fromLatin1("$dependency$"); - for (auto const &dependency : qAsConst(import.dependencies)) - importHelper(dependency.module, types, invalidPrefix, dependency.version); - - for (auto const &import : qAsConst(import.imports)) { - importHelper(import.module, types, prefix, - import.isAutoImport ? version : import.version); - } -} - -void FindWarningVisitor::Importer::processImport( - const FindWarningVisitor::Importer::Import &import, - FindWarningVisitor::ImportedTypes *types, - const QString &prefix) -{ - for (auto it = import.scripts.begin(); it != import.scripts.end(); ++it) { - types->importedQmlNames.insert(prefixedName(prefix, it.key()), it.value()); - types->exportedQmlNames.insert(it.key(), it.value()); - } - - // add objects - for (auto it = import.objects.begin(); it != import.objects.end(); ++it) { - const auto &val = it.value(); - types->cppNames.insert(val->internalName(), val); - - const auto exports = val->exports(); - for (const auto &valExport : exports) { - types->importedQmlNames.insert(prefixedName(prefix, valExport.type()), val); - types->exportedQmlNames.insert(valExport.type(), val); - } - } - - for (auto it = import.objects.begin(); it != import.objects.end(); ++it) { - const auto &val = it.value(); - if (!val->isComposite()) // Otherwise we have already done it in localFile2ScopeTree() - val->resolveTypes(types->cppNames); - } -} - -FindWarningVisitor::ImportedTypes FindWarningVisitor::Importer::importBareQmlTypes( - const QStringList &qmltypesFiles) -{ - ImportedTypes types; - - for (auto const &dir : m_importPaths) { - Import result; - QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter, - QDirIterator::Subdirectories }; - while (it.hasNext()) - readQmltypes(it.next(), &result.objects); - importDependencies(result, &types); - processImport(result, &types); - } - - if (qmltypesFiles.isEmpty()) { - for (auto const &qmltypesPath : m_importPaths) { - if (QFile::exists(qmltypesPath + SlashQmldir)) - continue; - Import result; - QDirIterator it { - qmltypesPath, QStringList { QLatin1String("*.qmltypes") }, QDir::Files }; - - while (it.hasNext()) { - const QString name = it.next(); - if (!name.endsWith(QLatin1String("/builtins.qmltypes"))) - readQmltypes(name, &result.objects); - } - - importDependencies(result, &types); - processImport(result, &types); - } - } else { - Import result; - - for (const auto &qmltypeFile : qmltypesFiles) - readQmltypes(qmltypeFile, &result.objects); - - importDependencies(result, &types); - processImport(result, &types); - } - - return types; -} - -FindWarningVisitor::ImportedTypes FindWarningVisitor::Importer::importModule( - const QString &module, const QString &prefix, QTypeRevision version) -{ - ImportedTypes result; - importHelper(module, &result, prefix, version); - return result; -} - -void FindWarningVisitor::Importer::importHelper(const QString &module, ImportedTypes *types, - const QString &prefix, QTypeRevision version) -{ - - const QPair importId { module, version }; - const auto it = m_seenImports.find(importId); - if (it != m_seenImports.end()) { - importDependencies(*it, types, prefix, version); - processImport(*it, types, prefix); - return; - } - - const auto qmltypesPaths = qQmlResolveImportPaths(module, m_importPaths, version); - for (auto const &qmltypesPath : qmltypesPaths) { - const QFileInfo file(qmltypesPath + SlashQmldir); - if (file.exists()) { - const auto import = readQmldir(file.canonicalPath()); - m_seenImports.insert(importId, import); - importDependencies(import, types, prefix, version); - processImport(import, types, prefix); - return; - } - } - - m_seenImports.insert(importId, {}); -} - -ScopeTree::Ptr FindWarningVisitor::Importer::localFile2ScopeTree(const QString &filePath) -{ - const auto seen = m_importedFiles.find(filePath); - if (seen != m_importedFiles.end()) - return *seen; - - QmlJSTypeReader typeReader(filePath); - ScopeTree::Ptr result = typeReader(); - m_importedFiles.insert(filePath, result); - - const QStringList errors = typeReader.errors(); - for (const QString &error : errors) - m_warnings.append(error); - - ImportedTypes types; - - QDirIterator it { - QFileInfo(filePath).canonicalPath(), - QStringList() << QLatin1String("*.qml"), - QDir::NoFilter - }; - while (it.hasNext()) { - ScopeTree::Ptr scope(localFile2ScopeTree(it.next())); - if (!scope->internalName().isEmpty()) - types.importedQmlNames.insert(scope->internalName(), scope); - } - - const auto imports = typeReader.imports(); - for (const auto &import : imports) - importHelper(import.module, &types, import.prefix, import.version); - - result->resolveTypes(types.importedQmlNames); - return result; -} - -FindWarningVisitor::ImportedTypes FindWarningVisitor::Importer::importFileOrDirectory( - const QString &fileOrDirectory, const QString &prefix) -{ - ImportedTypes result; - - QString name = fileOrDirectory; - - if (QFileInfo(name).isRelative()) - name = QDir(m_currentDir).filePath(name); - - QFileInfo fileInfo(name); - if (fileInfo.isFile()) { - ScopeTree::Ptr scope(localFile2ScopeTree(fileInfo.canonicalFilePath())); - result.importedQmlNames.insert(prefix.isEmpty() ? scope->internalName() : prefix, scope); - result.exportedQmlNames.insert(scope->internalName(), scope); - return result; - } - - QDirIterator it { - fileInfo.canonicalFilePath(), - QStringList() << QLatin1String("*.qml"), - QDir::NoFilter - }; - while (it.hasNext()) { - ScopeTree::Ptr scope(localFile2ScopeTree(it.next())); - if (!scope->internalName().isEmpty()) { - result.importedQmlNames.insert(prefixedName(prefix, scope->internalName()), scope); - result.exportedQmlNames.insert(scope->internalName(), scope); - } - } - - return result; -} - void FindWarningVisitor::importExportedNames(ScopeTree::ConstPtr scope) { QList scopes; @@ -388,7 +113,7 @@ void FindWarningVisitor::throwRecursionDepthError() bool FindWarningVisitor::visit(QQmlJS::AST::UiProgram *) { enterEnvironment(ScopeType::QMLScope, "program"); - m_rootScopeImports = m_importer.importBareQmlTypes(m_qmltypesFiles); + m_rootScopeImports = m_importer.importBaseQmlTypes(m_qmltypesFiles); // add "self" (as we only ever check the first part of a qualified identifier, we get away with // using an empty ScopeTree diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h index 2a0929b379..a8673d54e8 100644 --- a/tools/qmllint/findwarnings.h +++ b/tools/qmllint/findwarnings.h @@ -42,6 +42,7 @@ #include "typedescriptionreader.h" #include "scopetree.h" #include "qcoloroutput.h" +#include "qmljsimporter.h" #include #include @@ -53,18 +54,6 @@ class FindWarningVisitor : public QQmlJS::AST::Visitor { Q_DISABLE_COPY_MOVE(FindWarningVisitor) public: - struct ImportedTypes - { - // C++ names used in qmltypes files for non-composite types - QHash cppNames; - - // Names a component intends to export, without prefix - QHash exportedQmlNames; - - // Names the importing component sees, possibly adding a prefix - QHash importedQmlNames; - }; - explicit FindWarningVisitor( QStringList qmltypeDirs, QStringList qmltypesFiles, QString code, QString fileName, bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle); @@ -72,56 +61,7 @@ public: bool check(); private: - class Importer - { - public: - Importer(const QString ¤tDir, const QStringList &importPaths) : - m_currentDir(currentDir), m_importPaths(importPaths) {} - - ImportedTypes importBareQmlTypes(const QStringList &qmltypesFiles); - ImportedTypes importFileOrDirectory( - const QString &fileOrDirectory, const QString &prefix = QString()); - ImportedTypes importModule( - const QString &module, const QString &prefix = QString(), - QTypeRevision version = QTypeRevision()); - - QStringList takeWarnings() - { - QStringList result = std::move(m_warnings); - m_warnings.clear(); - return result; - } - - private: - struct Import { - QHash objects; - QHash scripts; - QList imports; - QList dependencies; - }; - - void importHelper(const QString &module, ImportedTypes *types, - const QString &prefix = QString(), - QTypeRevision version = QTypeRevision()); - void processImport(const Import &import, ImportedTypes *types, - const QString &prefix = QString()); - void importDependencies(const FindWarningVisitor::Importer::Import &import, - FindWarningVisitor::ImportedTypes *types, - const QString &prefix = QString(), - QTypeRevision version = QTypeRevision()); - void readQmltypes(const QString &filename, QHash *objects); - Import readQmldir(const QString &dirname); - ScopeTree::Ptr localFile2ScopeTree(const QString &filePath); - - QString m_currentDir; - QStringList m_importPaths; - QHash, Import> m_seenImports; - QHash m_importedFiles; - QStringList m_warnings; - - }; - - ImportedTypes m_rootScopeImports; + QmlJSImporter::ImportedTypes m_rootScopeImports; ScopeTree::Ptr m_rootScope; ScopeTree::Ptr m_currentScope; @@ -148,7 +88,7 @@ private: QVarLengthArray m_outstandingConnections; // Connections whose target we have not encountered - Importer m_importer; + QmlJSImporter m_importer; void enterEnvironment(ScopeType type, const QString &name); void leaveEnvironment(); -- cgit v1.2.3