aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-09-24 10:45:58 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-09-25 09:55:26 +0200
commit7b9c2b487a1ff75b034693f2f8281aa4c1fc758f (patch)
tree03c18e287de7fe4724285bb2b779e152a5b6707a /tools
parentc8207296e6ccfb2d5b3c84e5869098e67635f000 (diff)
qmllint: Move import handling code into separate class
This still messes up the association between scopes and imports, but at least it encapsulates the issue better. Change-Id: I67239495e61ee10d18e6c4b3e837560a42650710 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmllint/findwarnings.cpp126
-rw-r--r--tools/qmllint/findwarnings.h64
2 files changed, 126 insertions, 64 deletions
diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp
index 1e0edf1f49..715da12ca1 100644
--- a/tools/qmllint/findwarnings.cpp
+++ b/tools/qmllint/findwarnings.cpp
@@ -71,19 +71,17 @@ void FindWarningVisitor::leaveEnvironment()
static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
-void FindWarningVisitor::readQmltypes(const QString &filename,
+void FindWarningVisitor::Importer::readQmltypes(const QString &filename,
QHash<QString, ScopeTree::ConstPtr> *objects)
{
const QFileInfo fileInfo(filename);
if (!fileInfo.exists()) {
- m_colorOut.write(QLatin1String("warning: "), Warning);
- m_colorOut.writeUncolored(QLatin1String("QML types file does not exist: ") + filename);
+ m_warnings.append(QLatin1String("QML types file does not exist: ") + filename);
return;
}
if (fileInfo.isDir()) {
- m_colorOut.write(QLatin1String("warning: "), Warning);
- m_colorOut.writeUncolored(QLatin1String("QML types file cannot be a directory: ") + filename);
+ m_warnings.append(QLatin1String("QML types file cannot be a directory: ") + filename);
return;
}
@@ -93,10 +91,10 @@ void FindWarningVisitor::readQmltypes(const QString &filename,
QStringList dependencies;
auto succ = reader(objects, &dependencies);
if (!succ)
- m_colorOut.writeUncolored(reader.errorMessage());
+ m_warnings.append(reader.errorMessage());
}
-FindWarningVisitor::Import FindWarningVisitor::readQmldir(const QString &path)
+FindWarningVisitor::Importer::Import FindWarningVisitor::Importer::readQmldir(const QString &path)
{
Import result;
auto reader = createQmldirParserForFile(path + SlashQmldir);
@@ -108,10 +106,9 @@ FindWarningVisitor::Import FindWarningVisitor::readQmldir(const QString &path)
for (auto it = components.begin(), end = components.end(); it != end; ++it) {
const QString filePath = path + QLatin1Char('/') + it->fileName;
if (!QFile::exists(filePath)) {
- m_colorOut.write(QLatin1String("warning: "), Warning);
- m_colorOut.write(it->fileName + QLatin1String(" is listed as component in ")
- + path + SlashQmldir
- + QLatin1String(" but does not exist.\n"));
+ m_warnings.append(it->fileName + QLatin1String(" is listed as component in ")
+ + path + SlashQmldir
+ + QLatin1String(" but does not exist.\n"));
continue;
}
@@ -138,8 +135,9 @@ FindWarningVisitor::Import FindWarningVisitor::readQmldir(const QString &path)
return result;
}
-void FindWarningVisitor::processImport(
- const QString &prefix, const FindWarningVisitor::Import &import, QTypeRevision version)
+void FindWarningVisitor::Importer::processImport(
+ const QString &prefix, const FindWarningVisitor::Importer::Import &import,
+ 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.
@@ -167,9 +165,13 @@ void FindWarningVisitor::processImport(
}
}
-void FindWarningVisitor::importBareQmlTypes()
+QHash<QString, ScopeTree::ConstPtr> FindWarningVisitor::Importer::importBareQmlTypes(
+ const QStringList &qmltypesFiles)
{
- for (auto const &dir : m_qmltypesDirs) {
+ QHash<QString, ScopeTree::ConstPtr> result;
+ result.swap(m_exportedName2Scope);
+
+ for (auto const &dir : m_importPaths) {
Import result;
QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
QDirIterator::Subdirectories };
@@ -178,8 +180,8 @@ void FindWarningVisitor::importBareQmlTypes()
processImport("", result, QTypeRevision());
}
- if (m_qmltypesFiles.isEmpty()) {
- for (auto const &qmltypesPath : m_qmltypesDirs) {
+ if (qmltypesFiles.isEmpty()) {
+ for (auto const &qmltypesPath : m_importPaths) {
if (QFile::exists(qmltypesPath + SlashQmldir))
continue;
Import result;
@@ -197,22 +199,35 @@ void FindWarningVisitor::importBareQmlTypes()
} else {
Import result;
- for (const auto &qmltypeFile : m_qmltypesFiles)
+ for (const auto &qmltypeFile : qmltypesFiles)
readQmltypes(qmltypeFile, &result.objects);
processImport("", result, QTypeRevision());
}
+
+ result.swap(m_exportedName2Scope);
+ return result;
}
-void FindWarningVisitor::importHelper(const QString &module, const QString &prefix,
- QTypeRevision version)
+QHash<QString, ScopeTree::ConstPtr> FindWarningVisitor::Importer::importModule(
+ const QString &module, const QString &prefix, QTypeRevision version)
+{
+ QHash<QString, ScopeTree::ConstPtr> result;
+ result.swap(m_exportedName2Scope);
+ importHelper(module, prefix, version);
+ result.swap(m_exportedName2Scope);
+ return result;
+}
+
+void FindWarningVisitor::Importer::importHelper(
+ const QString &module, const QString &prefix, QTypeRevision version)
{
const QPair<QString, QString> importId { module, prefix };
- if (m_alreadySeenImports.contains(importId))
+ if (m_seenImports.contains(importId))
return;
- m_alreadySeenImports.insert(importId);
+ m_seenImports.insert(importId);
- const auto qmltypesPaths = qQmlResolveImportPaths(module, m_qmltypesDirs, version);
+ const auto qmltypesPaths = qQmlResolveImportPaths(module, m_importPaths, version);
for (auto const &qmltypesPath : qmltypesPaths) {
if (QFile::exists(qmltypesPath + SlashQmldir)) {
processImport(prefix, readQmldir(qmltypesPath), version);
@@ -221,14 +236,14 @@ void FindWarningVisitor::importHelper(const QString &module, const QString &pref
}
}
-ScopeTree::Ptr FindWarningVisitor::localFile2ScopeTree(const QString &filePath)
+ScopeTree::Ptr FindWarningVisitor::Importer::localFile2ScopeTree(const QString &filePath)
{
QmlJSTypeReader typeReader(filePath);
ScopeTree::Ptr result = typeReader();
const QStringList errors = typeReader.errors();
for (const QString &error : errors)
- m_colorOut.write(error, Error);
+ m_warnings.append(error);
const auto imports = typeReader.imports();
for (const auto &import : imports)
@@ -237,17 +252,21 @@ ScopeTree::Ptr FindWarningVisitor::localFile2ScopeTree(const QString &filePath)
return result;
}
-void FindWarningVisitor::importFileOrDirectory(const QString &fileOrDirectory,
- const QString &prefix)
+QHash<QString, ScopeTree::ConstPtr> FindWarningVisitor::Importer::importFileOrDirectory(
+ const QString &fileOrDirectory, const QString &prefix)
{
+ QHash<QString, ScopeTree::ConstPtr> result;
+ result.swap(m_exportedName2Scope);
+
QString name = fileOrDirectory;
if (QFileInfo(name).isRelative())
- name = QDir(QFileInfo { m_filePath }.path()).filePath(name);
+ name = QDir(m_currentDir).filePath(name);
if (QFileInfo(name).isFile()) {
m_exportedName2Scope.insert(prefix, ScopeTree::ConstPtr(localFile2ScopeTree(name)));
- return;
+ result.swap(m_exportedName2Scope);
+ return result;
}
QDirIterator it { name, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
@@ -256,15 +275,17 @@ void FindWarningVisitor::importFileOrDirectory(const QString &fileOrDirectory,
if (!scope->className().isEmpty())
m_exportedName2Scope.insert(prefixedName(prefix, scope->className()), scope);
}
+
+ result.swap(m_exportedName2Scope);
+ return result;
}
void FindWarningVisitor::importExportedNames(QStringView prefix, QString name)
{
QList<ScopeTree::ConstPtr> scopes;
for (;;) {
- ScopeTree::ConstPtr scope = m_exportedName2Scope.value(m_exportedName2Scope.contains(name)
- ? name
- : prefix + QLatin1Char('.') + name);
+ ScopeTree::ConstPtr scope = m_rootScopeImports.value(
+ m_rootScopeImports.contains(name) ? name : prefix + QLatin1Char('.') + name);
if (scope) {
if (scopes.contains(scope)) {
QString inheritenceCycle = name;
@@ -288,7 +309,7 @@ void FindWarningVisitor::importExportedNames(QStringView prefix, QString name)
scopes.append(scope);
const auto properties = scope->properties();
for (auto property : properties) {
- property.setType(m_exportedName2Scope.value(property.typeName()));
+ property.setType(m_rootScopeImports.value(property.typeName()));
m_currentScope->insertPropertyIdentifier(property);
}
@@ -317,13 +338,19 @@ void FindWarningVisitor::throwRecursionDepthError()
bool FindWarningVisitor::visit(QQmlJS::AST::UiProgram *)
{
enterEnvironment(ScopeType::QMLScope, "program");
- importBareQmlTypes();
+ m_rootScopeImports = m_importer.importBareQmlTypes(m_qmltypesFiles);
// add "self" (as we only ever check the first part of a qualified identifier, we get away with
// using an empty ScopeTree
- m_exportedName2Scope.insert(QFileInfo { m_filePath }.baseName(), {});
+ m_rootScopeImports.insert(QFileInfo { m_filePath }.baseName(), {});
+ m_rootScopeImports.insert(m_importer.importFileOrDirectory(".", QString()));
+
+ const QStringList warnings = m_importer.takeWarnings();
+ for (const QString &warning : warnings) {
+ m_colorOut.write(QStringLiteral("warning: "), Warning);
+ m_colorOut.writeUncolored(warning);
+ }
- importFileOrDirectory(".", QString());
return true;
}
@@ -520,7 +547,7 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
uipm->memberType ? uipm->memberType->name.toString() : QString(),
uipm->typeModifier == QLatin1String("list"), !uipm->isReadonlyMember, false,
uipm->memberType ? (uipm->memberType->name == QLatin1String("alias")) : false, 0);
- property.setType(m_exportedName2Scope.value(property.typeName()));
+ property.setType(m_rootScopeImports.value(property.typeName()));
m_currentScope->insertPropertyIdentifier(property);
}
return true;
@@ -538,7 +565,6 @@ FindWarningVisitor::FindWarningVisitor(
QStringList qmltypeDirs, QStringList qmltypesFiles, QString code, QString fileName,
bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle)
: m_rootScope(ScopeTree::create(ScopeType::JSFunctionScope, "global")),
- m_qmltypesDirs(std::move(qmltypeDirs)),
m_qmltypesFiles(std::move(qmltypesFiles)),
m_code(std::move(code)),
m_rootId(QLatin1String("<id>")),
@@ -546,7 +572,8 @@ FindWarningVisitor::FindWarningVisitor(
m_colorOut(silent),
m_warnUnqualified(warnUnqualified),
m_warnWithStatement(warnWithStatement),
- m_warnInheritanceCycle(warnInheritanceCycle)
+ m_warnInheritanceCycle(warnInheritanceCycle),
+ m_importer(QFileInfo(m_filePath).path(), qmltypeDirs)
{
m_currentScope = m_rootScope;
@@ -595,7 +622,7 @@ bool FindWarningVisitor::check()
if (!m_warnUnqualified)
return true;
- CheckIdentifiers check(&m_colorOut, m_code, m_exportedName2Scope, m_filePath);
+ CheckIdentifiers check(&m_colorOut, m_code, m_rootScopeImports, m_filePath);
return check(m_qmlid2scope, m_rootScope, m_rootId);
}
@@ -665,13 +692,13 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiImport *import)
}
auto dirname = import->fileName.toString();
if (!dirname.isEmpty())
- importFileOrDirectory(dirname, prefix);
+ m_rootScopeImports.insert(m_importer.importFileOrDirectory(dirname, prefix));
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_exportedName2Scope.value(importId));
+ m_qmlid2scope.insert(importId, m_rootScopeImports.value(importId));
}
auto uri = import->importUri;
while (uri) {
@@ -681,7 +708,16 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiImport *import)
}
path.chop(1);
- importHelper(path, prefix, import->version ? import->version->version : QTypeRevision());
+ m_rootScopeImports.insert(
+ m_importer.importModule(
+ path, prefix, import->version ? import->version->version : QTypeRevision()));
+
+ const QStringList warnings = m_importer.takeWarnings();
+ for (const QString &warning : warnings) {
+ m_colorOut.write(QStringLiteral("warning: "), Warning);
+ m_colorOut.writeUncolored(warning);
+ }
+
return true;
}
@@ -709,7 +745,7 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
MetaProperty prop(uiob->qualifiedId->name.toString(), name, false, true, true,
name == QLatin1String("alias"), 0);
- prop.setType(m_exportedName2Scope.value(uiob->qualifiedTypeNameId->name.toString()));
+ prop.setType(m_rootScopeImports.value(uiob->qualifiedTypeNameId->name.toString()));
m_currentScope->addProperty(prop);
enterEnvironment(ScopeType::QMLScope, name);
@@ -774,7 +810,7 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
do {
scope = scope->parentScope(); // TODO: rename method
} while (scope->scopeType() != ScopeType::QMLScope);
- targetScope = m_exportedName2Scope.value(scope->name());
+ targetScope = m_rootScopeImports.value(scope->name());
} else {
// there was a target, check if we already can find it
auto scopeIt = m_qmlid2scope.find(target);
diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h
index be5bc87b53..315c72aa47 100644
--- a/tools/qmllint/findwarnings.h
+++ b/tools/qmllint/findwarnings.h
@@ -60,24 +60,59 @@ public:
bool check();
private:
- struct Import {
- QHash<QString, ScopeTree::ConstPtr> objects;
- QList<QQmlDirParser::Import> imports;
- QList<QQmlDirParser::Component> dependencies;
- QList<QPair<QString, ScopeTree::ConstPtr>> scripts;
+ class Importer
+ {
+ public:
+ Importer(const QString &currentDir, const QStringList &importPaths) :
+ m_currentDir(currentDir), m_importPaths(importPaths) {}
+
+ QHash<QString, ScopeTree::ConstPtr> importBareQmlTypes(const QStringList &qmltypesFiles);
+ QHash<QString, ScopeTree::ConstPtr> importFileOrDirectory(
+ const QString &fileOrDirectory, const QString &prefix);
+ QHash<QString, ScopeTree::ConstPtr> 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::ConstPtr> objects;
+ QList<QQmlDirParser::Import> imports;
+ QList<QQmlDirParser::Component> dependencies;
+ QList<QPair<QString, ScopeTree::ConstPtr>> scripts;
+ };
+
+ void importHelper(const QString &module, const QString &prefix = QString(),
+ QTypeRevision version = QTypeRevision());
+ void processImport(const QString &prefix, const Import &import, QTypeRevision version);
+ void readQmltypes(const QString &filename, QHash<QString, ScopeTree::ConstPtr> *objects);
+ Import readQmldir(const QString &dirname);
+ ScopeTree::Ptr localFile2ScopeTree(const QString &filePath);
+
+ QString m_currentDir;
+ QStringList m_importPaths;
+ QSet<QPair<QString, QString>> m_seenImports;
+ QStringList m_warnings;
+
+ QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope;
};
+ QHash<QString, ScopeTree::ConstPtr> m_rootScopeImports;
+
ScopeTree::Ptr m_rootScope;
ScopeTree::Ptr m_currentScope;
QQmlJS::AST::ExpressionNode *m_fieldMemberBase = nullptr;
- QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope;
- QStringList m_qmltypesDirs;
QStringList m_qmltypesFiles;
QString m_code;
QHash<QString, ScopeTree::ConstPtr> m_qmlid2scope;
QString m_rootId;
QString m_filePath;
- QSet<QPair<QString, QString>> m_alreadySeenImports;
QSet<QString> m_unknownImports;
ColorOutput m_colorOut;
bool m_visitFailed = false;
@@ -95,20 +130,11 @@ private:
QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
+ Importer m_importer;
+
void enterEnvironment(ScopeType type, const QString &name);
void leaveEnvironment();
- void importBareQmlTypes();
- void importHelper(const QString &module, const QString &prefix = QString(),
- QTypeRevision version = QTypeRevision());
-
- void readQmltypes(const QString &filename, QHash<QString, ScopeTree::ConstPtr> *objects);
- Import readQmldir(const QString &dirname);
- void processImport(const QString &prefix, const Import &import, QTypeRevision version);
-
- ScopeTree::Ptr localFile2ScopeTree(const QString &filePath);
-
- void importFileOrDirectory(const QString &directory, const QString &prefix);
void importExportedNames(QStringView prefix, QString name);
void parseHeaders(QQmlJS::AST::UiHeaderItemList *headers);