aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmllint
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-09-29 15:49:16 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-10-02 15:32:15 +0200
commit48426aa705cb7ed88489200864c2fc0143058671 (patch)
tree964b8588157dc122f7cf744ca8501ca4276dbeba /tools/qmllint
parentfebb6ba891616b71476b736d4c583054685c7766 (diff)
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 <fabian.kosmale@qt.io>
Diffstat (limited to 'tools/qmllint')
-rw-r--r--tools/qmllint/.prev_CMakeLists.txt1
-rw-r--r--tools/qmllint/CMakeLists.txt1
-rw-r--r--tools/qmllint/checkidentifiers.cpp2
-rw-r--r--tools/qmllint/checkidentifiers.h4
-rw-r--r--tools/qmllint/findwarnings.cpp277
-rw-r--r--tools/qmllint/findwarnings.h66
6 files changed, 9 insertions, 342 deletions
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<bool(ScopeTree::ConstPtr)> 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 <QtCore/qdiriterator.h>
#include <QtCore/qscopedvaluerollback.h>
-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<QString, ScopeTree::Ptr> *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<QString, ScopeTree::Ptr> 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<QString, QTypeRevision> 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<ScopeTree::ConstPtr> 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 <QtQml/private/qqmldirparser_p.h>
#include <QtQml/private/qqmljsastvisitor_p.h>
@@ -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<QString, ScopeTree::Ptr> cppNames;
-
- // Names a component intends to export, without prefix
- QHash<QString, ScopeTree::Ptr> exportedQmlNames;
-
- // Names the importing component sees, possibly adding a prefix
- QHash<QString, ScopeTree::Ptr> 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 &currentDir, 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<QString, ScopeTree::Ptr> objects;
- QHash<QString, ScopeTree::Ptr> scripts;
- QList<QQmlDirParser::Import> imports;
- QList<QQmlDirParser::Import> 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<QString, ScopeTree::Ptr> *objects);
- Import readQmldir(const QString &dirname);
- ScopeTree::Ptr localFile2ScopeTree(const QString &filePath);
-
- QString m_currentDir;
- QStringList m_importPaths;
- QHash<QPair<QString, QTypeRevision>, Import> m_seenImports;
- QHash<QString, ScopeTree::Ptr> 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<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
- Importer m_importer;
+ QmlJSImporter m_importer;
void enterEnvironment(ScopeType type, const QString &name);
void leaveEnvironment();