diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-11-14 17:08:15 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-11-18 13:48:17 +0100 |
commit | 355fd4bf5cd0a83b52b37c2a905cee867f9af4d6 (patch) | |
tree | bf49f58868963d75b3b9bf8b38b07405c6c1f81f /tools | |
parent | 6b1f42c72f060010f3c73f4502dcd10647f205c1 (diff) |
qmllint: Read app.qmltypes files for extra type resolution
Change-Id: I7f19b39dd65063ae26b93bf1b40b0be0b0e15c6d
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qmllint/findunqualified.cpp | 172 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.h | 15 |
2 files changed, 113 insertions, 74 deletions
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 6a4071acc7..6274238f96 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -69,8 +69,7 @@ void FindUnqualifiedIDVisitor::leaveEnvironment() enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath }; -QStringList completeImportPaths(const QString &uri, const QStringList &basePaths, - int vmaj, int vmin) +QStringList completeImportPaths(const QString &uri, const QString &basePath, int vmaj, int vmin) { static const QLatin1Char Slash('/'); static const QLatin1Char Backslash('\\'); @@ -79,7 +78,7 @@ QStringList completeImportPaths(const QString &uri, const QStringList &basePaths QStringList qmlDirPathsPaths; // fully & partially versioned parts + 1 unversioned for each base path - qmlDirPathsPaths.reserve(basePaths.count() * (2 * parts.count() + 1)); + qmlDirPathsPaths.reserve(2 * parts.count() + 1); auto versionString = [](int vmaj, int vmin, ImportVersion version) { @@ -107,96 +106,93 @@ QStringList completeImportPaths(const QString &uri, const QStringList &basePaths for (int version = FullyVersioned; version <= BasePath; ++version) { const QString ver = versionString(vmaj, vmin, static_cast<ImportVersion>(version)); - for (const QString &path : basePaths) { - QString dir = path; - if (!dir.endsWith(Slash) && !dir.endsWith(Backslash)) - dir += Slash; + QString dir = basePath; + if (!dir.endsWith(Slash) && !dir.endsWith(Backslash)) + dir += Slash; - if (version == BasePath) { - qmlDirPathsPaths += dir; - } else { - // append to the end - qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver; - } + if (version == BasePath) { + qmlDirPathsPaths += dir; + } else { + // append to the end + qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver; + } - if (version < Unversioned) { - // insert in the middle - for (int index = parts.count() - 2; index >= 0; --index) { - qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash) - + ver + Slash - + joinStringRefs(parts.mid(index + 1), Slash); - } + if (version < Unversioned) { + // insert in the middle + for (int index = parts.count() - 2; index >= 0; --index) { + qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash) + + ver + Slash + + joinStringRefs(parts.mid(index + 1), Slash); } } } return qmlDirPathsPaths; } -void FindUnqualifiedIDVisitor::importHelper(QString id, const QString &prefix, int major, int minor) -{ - QPair<QString, QString> importId { id, prefix }; - if (m_alreadySeenImports.contains(importId)) - return; +static const QLatin1String SlashQmldir = QLatin1String("/qmldir"); +static const QLatin1String SlashAppDotQmltypes = QLatin1String("/app.qmltypes"); +static const QLatin1String SlashLibDotQmltypes = QLatin1String("/lib.qmltypes"); +static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes"); - m_alreadySeenImports.insert(importId); +void FindUnqualifiedIDVisitor::readQmltypes(const QString &filename, + FindUnqualifiedIDVisitor::Import &result) +{ + auto reader = createQmltypesReaderForFile(filename); + auto succ = reader(&result.objects, &result.moduleApis, &result.dependencies); + if (!succ) + m_colorOut.writeUncolored(reader.errorMessage()); +} - id = id.replace(QLatin1String("/"), QLatin1String(".")); - auto qmltypesPaths = completeImportPaths(id, m_qmltypeDirs, major, minor); +FindUnqualifiedIDVisitor::Import FindUnqualifiedIDVisitor::readQmldir(const QString &path) +{ + Import result; + auto reader = createQmldirParserForFile(path + SlashQmldir); + const auto imports = reader.imports(); + for (const QString &import : imports) + result.dependencies.append(import); - QHash<QString, ScopeTree::ConstPtr> objects; - QList<ModuleApiInfo> moduleApis; - QStringList dependencies; - static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes"); - static const QLatin1String SlashQmldir("/qmldir"); - for (auto const &qmltypesPath : qmltypesPaths) { - if (QFile::exists(qmltypesPath + SlashQmldir)) { - auto reader = createQmldirParserForFile(qmltypesPath + SlashQmldir); - const auto imports = reader.imports(); - for (const QString &import : imports) - importHelper(import, prefix, major, minor); - - QHash<QString, ScopeTree *> qmlComponents; - const auto components = reader.components(); - for (auto it = components.begin(), end = components.end(); it != end; ++it) { - const QString filePath = qmltypesPath + QLatin1Char('/') + it->fileName; - if (!QFile::exists(filePath)) { - m_colorOut.write(QLatin1String("warning: "), Warning); - m_colorOut.write(it->fileName + QLatin1String(" is listed as component in ") - + qmltypesPath + SlashQmldir - + QLatin1String(" but does not exist.\n")); - continue; - } + QHash<QString, ScopeTree *> 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_colorOut.write(QLatin1String("warning: "), Warning); + m_colorOut.write(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(), localQmlFile2ScopeTree(filePath)); + auto mo = qmlComponents.find(it.key()); + if (mo == qmlComponents.end()) + mo = qmlComponents.insert(it.key(), localQmlFile2ScopeTree(filePath)); - (*mo)->addExport( - it.key(), reader.typeNamespace(), - ComponentVersion(it->majorVersion, it->minorVersion)); - } - for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) { - objects.insert(it.key(), - QSharedPointer<const ScopeTree>(it.value())); - } - } - if (QFile::exists(qmltypesPath + SlashPluginsDotQmltypes)) { - auto reader = createQmltypesReaderForFile(qmltypesPath + SlashPluginsDotQmltypes); - auto succ = reader(&objects, &moduleApis, &dependencies); - if (!succ) - m_colorOut.writeUncolored(reader.errorMessage()); - } + (*mo)->addExport( + it.key(), reader.typeNamespace(), + ComponentVersion(it->majorVersion, it->minorVersion)); } - for (auto const &dependency : qAsConst(dependencies)) { + for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) + result.objects.insert( it.key(), ScopeTree::ConstPtr(it.value())); + + if (!reader.plugins().isEmpty() && QFile::exists(path + SlashPluginsDotQmltypes)) + readQmltypes(path + SlashPluginsDotQmltypes, result); + + return result; +} + +void FindUnqualifiedIDVisitor::processImport(const QString &prefix, const FindUnqualifiedIDVisitor::Import &import) +{ + for (auto const &dependency : qAsConst(import.dependencies)) { auto const split = dependency.split(" "); auto const &id = split.at(0); auto const major = split.at(1).split('.').at(0).toInt(); auto const minor = split.at(1).split('.').at(1).toInt(); importHelper(id, QString(), major, minor); } + // add objects - for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) { - auto val = ob_it.value(); + for (auto it = import.objects.begin(); it != import.objects.end(); ++it) { + const auto &val = it.value(); m_exportedName2Scope[prefix + val->className()] = val; const auto exports = val->exports(); @@ -209,6 +205,38 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, const QString &prefix, i } } +void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int major, int minor) +{ + id = id.replace(QLatin1String("/"), QLatin1String(".")); + QPair<QString, QString> importId { id, prefix }; + if (m_alreadySeenImports.contains(importId)) + return; + m_alreadySeenImports.insert(importId); + + for (const QString &qmltypeDir : m_qmltypeDirs) { + auto qmltypesPaths = completeImportPaths(id, qmltypeDir, major, minor); + + for (auto const &qmltypesPath : qmltypesPaths) { + if (QFile::exists(qmltypesPath + SlashQmldir)) { + processImport(prefix, readQmldir(qmltypesPath)); + + // break so that we don't import unversioned qml components + // in addition to versioned ones + break; + } + + Import result; + if (QFile::exists(qmltypesPath + SlashAppDotQmltypes)) + readQmltypes(qmltypesPath + SlashAppDotQmltypes, result); + else if (QFile::exists(qmltypesPath + SlashLibDotQmltypes)) + readQmltypes(qmltypesPath + SlashLibDotQmltypes, result); + else + continue; + processImport(prefix, result); + } + } +} + ScopeTree *FindUnqualifiedIDVisitor::localQmlFile2ScopeTree(const QString &filePath) { using namespace QQmlJS::AST; diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index 8b79918d90..a8fa435464 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -58,6 +58,12 @@ public: bool check(); private: + struct Import { + QHash<QString, ScopeTree::ConstPtr> objects; + QList<ModuleApiInfo> moduleApis; + QStringList dependencies; + }; + QScopedPointer<ScopeTree> m_rootScope; ScopeTree *m_currentScope = nullptr; QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope; @@ -82,8 +88,13 @@ private: void enterEnvironment(ScopeType type, QString name); void leaveEnvironment(); - void importHelper(QString id, const QString &prefix, int major, int minor); - ScopeTree* localQmlFile2ScopeTree(const QString &filePath); + void importHelper(QString id, QString prefix, int major, int minor); + + void readQmltypes(const QString &filename, Import &result); + Import readQmldir(const QString &dirname); + void processImport(const QString &prefix, const Import &import); + + ScopeTree *localQmlFile2ScopeTree(const QString &filePath); void importDirectory(const QString &directory, const QString &prefix); void importExportedNames(const QStringRef &prefix, QString name); |