aboutsummaryrefslogtreecommitdiffstats
path: root/tools
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
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')
-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
-rw-r--r--tools/shared/qmljsimporter.cpp315
-rw-r--r--tools/shared/qmljsimporter.h105
-rw-r--r--tools/shared/shared.pri2
9 files changed, 431 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();
diff --git a/tools/shared/qmljsimporter.cpp b/tools/shared/qmljsimporter.cpp
new file mode 100644
index 0000000000..28b7e7950f
--- /dev/null
+++ b/tools/shared/qmljsimporter.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmljsimporter.h"
+#include "typedescriptionreader.h"
+#include "qmljstypereader.h"
+
+#include <QtQml/private/qqmlimportresolver_p.h>
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdiriterator.h>
+
+static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
+static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
+
+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 QmlJSImporter::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());
+}
+
+QmlJSImporter::Import QmlJSImporter::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 QmlJSImporter::importDependencies(
+ const QmlJSImporter::Import &import,
+ QmlJSImporter::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 QmlJSImporter::processImport(
+ const QmlJSImporter::Import &import,
+ QmlJSImporter::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);
+ }
+}
+
+/*!
+ * Imports builtins.qmltypes and the given \a qmltypesFiles. If \a qmltypesFiles
+ * is empty, imports any .qmltypes files without accompanying qmldir found in
+ * any of the import base paths.
+ */
+QmlJSImporter::ImportedTypes QmlJSImporter::importBaseQmlTypes(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;
+}
+
+QmlJSImporter::ImportedTypes QmlJSImporter::importModule(
+ const QString &module, const QString &prefix, QTypeRevision version)
+{
+ ImportedTypes result;
+ importHelper(module, &result, prefix, version);
+ return result;
+}
+
+void QmlJSImporter::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 QmlJSImporter::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;
+}
+
+QmlJSImporter::ImportedTypes QmlJSImporter::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;
+}
diff --git a/tools/shared/qmljsimporter.h b/tools/shared/qmljsimporter.h
new file mode 100644
index 0000000000..44536acc51
--- /dev/null
+++ b/tools/shared/qmljsimporter.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLJSIMPORTER_H
+#define QMLJSIMPORTER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "scopetree.h"
+#include <QtQml/private/qqmldirparser_p.h>
+
+class QmlJSImporter
+{
+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;
+ };
+
+ QmlJSImporter(const QString &currentDir, const QStringList &importPaths) :
+ m_currentDir(currentDir), m_importPaths(importPaths) {}
+
+ ImportedTypes importBaseQmlTypes(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 QmlJSImporter::Import &import,
+ 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;
+};
+
+#endif // QMLJSIMPORTER_H
diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri
index 4ee3704bfb..b7eb2ec6c1 100644
--- a/tools/shared/shared.pri
+++ b/tools/shared/shared.pri
@@ -12,6 +12,7 @@ RESOURCEFILEMAPPER_HEADERS = \
METATYPEREADER_SOURCES = \
$$PWD/componentversion.cpp \
$$PWD/importedmembersvisitor.cpp \
+ $$PWD/qmljsimporter.cpp \
$$PWD/qmljstypereader.cpp \
$$PWD/scopetree.cpp \
$$PWD/typedescriptionreader.cpp
@@ -19,6 +20,7 @@ METATYPEREADER_SOURCES = \
METATYPEREADER_HEADERS = \
$$PWD/componentversion.h \
$$PWD/importedmembersvisitor.h \
+ $$PWD/qmljsimporter.h \
$$PWD/qmljstypereader.h \
$$PWD/metatypes.h \
$$PWD/scopetree.h \