aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFawzi Mohamed <fawzi.mohamed@qt.io>2020-11-26 07:32:29 +0100
committerFawzi Mohamed <fawzi.mohamed@qt.io>2020-11-26 11:14:26 +0000
commit5ce8cf70e8c1a7434f744f304edd3e5ea3fae943 (patch)
tree3ae052e2c6d2c767b41bb3d3c12cd25cc2d3165e
parent23501b427182e47d3bc36431f2603e5840f5f5cd (diff)
qmljs: Fix parsing and and loading of qmldir imports
Add most changes to the qmldir parser of Qt6. This is not a direct application of the changes because they rely on changes to QtBase that are Q6 only. Ignore load errors of optional dependencies. Fixes: QTCREATORBUG-24772 Change-Id: I0b85818b602c8c7c1712e52318b4ca3f15364cc5 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r--src/libs/qmljs/parser/qmldirparser.cpp126
-rw-r--r--src/libs/qmljs/parser/qmldirparser_p.h36
-rw-r--r--src/libs/qmljs/qmljsdocument.cpp4
-rw-r--r--src/libs/qmljs/qmljsdocument.h6
-rw-r--r--src/libs/qmljs/qmljslink.cpp33
-rw-r--r--src/libs/qmljs/qmljsplugindumper.cpp9
6 files changed, 157 insertions, 57 deletions
diff --git a/src/libs/qmljs/parser/qmldirparser.cpp b/src/libs/qmljs/parser/qmldirparser.cpp
index ffe5edcf20..e15d467b8f 100644
--- a/src/libs/qmljs/parser/qmldirparser.cpp
+++ b/src/libs/qmljs/parser/qmldirparser.cpp
@@ -25,10 +25,14 @@
#include "qmldirparser_p.h"
+#include <utils/qtcassert.h>
+
#include <QtCore/QtDebug>
QT_QML_BEGIN_NAMESPACE
+using namespace LanguageUtils;
+
static int parseInt(const QStringView &str, bool *ok)
{
int pos = 0;
@@ -60,6 +64,33 @@ static bool parseVersion(const QString &str, int *major, int *minor)
return false;
}
+static ComponentVersion parseImportVersion(const QString &str)
+{
+ int minor = -1;
+ int major = -1;
+ const int dotIndex = str.indexOf(QLatin1Char('.'));
+ bool ok = false;
+ if (dotIndex != -1 && str.indexOf(QLatin1Char('.'), dotIndex + 1) == -1) {
+ major = parseInt(QStringView(str.constData(), dotIndex), &ok);
+ if (ok) {
+ if (str.length() > dotIndex + 1) {
+ minor = parseInt(QStringView(str.constData() + dotIndex + 1, str.length() - dotIndex - 1),
+ &ok);
+ if (!ok)
+ minor = ComponentVersion::NoVersion;
+ } else {
+ minor = ComponentVersion::MaxVersion;
+ }
+ }
+ } else if (str.length() > 0) {
+ QTC_ASSERT(str != QLatin1String("auto"), return ComponentVersion(-1, -1));
+ major = parseInt(QStringView(str.constData(), str.length()),
+ &ok);
+ minor = ComponentVersion::MaxVersion;
+ }
+ return ComponentVersion(major, minor);
+}
+
void QmlDirParser::clear()
{
_errors.clear();
@@ -97,6 +128,50 @@ bool QmlDirParser::parse(const QString &source)
quint16 lineNumber = 0;
bool firstLine = true;
+ auto readImport = [&](const QString *sections, int sectionCount, Import::Flags flags) {
+ Import import;
+ if (sectionCount == 2) {
+ import = Import(sections[1], ComponentVersion(), flags);
+ } else if (sectionCount == 3) {
+ if (sections[2] == QLatin1String("auto")) {
+ import = Import(sections[1], ComponentVersion(), flags | Import::Auto);
+ } else {
+ const auto version = parseImportVersion(sections[2]);
+ if (version.isValid()) {
+ import = Import(sections[1], version, flags);
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("invalid version %1, expected <major>.<minor>")
+ .arg(sections[2]));
+ return false;
+ }
+ }
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("%1 requires 1 or 2 arguments, but %2 were provided")
+ .arg(sections[0]).arg(sectionCount - 1));
+ return false;
+ }
+ if (sections[0] == QStringLiteral("import"))
+ _imports.append(import);
+ else
+ _dependencies.append(import);
+ return true;
+ };
+
+ auto readPlugin = [&](const QString *sections, int sectionCount, bool isOptional) {
+ if (sectionCount < 2 || sectionCount > 3) {
+ reportError(lineNumber, 0, QStringLiteral("plugin directive requires one or two "
+ "arguments, but %1 were provided")
+ .arg(sectionCount - 1));
+ return false;
+ }
+
+ const Plugin entry(sections[1], sections[2], isOptional);
+ _plugins.append(entry);
+ return true;
+ };
+
const QChar *ch = source.constData();
while (!ch->isNull()) {
++lineNumber;
@@ -163,16 +238,26 @@ bool QmlDirParser::parse(const QString &source)
_typeNamespace = sections[1];
} else if (sections[0] == QLatin1String("plugin")) {
- if (sectionCount < 2 || sectionCount > 3) {
- reportError(lineNumber, 0,
- QStringLiteral("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1));
-
+ if (!readPlugin(sections, sectionCount, false))
+ continue;
+ } else if (sections[0] == QLatin1String("optional")) {
+ if (sectionCount < 2) {
+ reportError(lineNumber, 0, QStringLiteral("optional directive requires further "
+ "arguments, but none were provided."));
continue;
}
- const Plugin entry(sections[1], sections[2]);
-
- _plugins.append(entry);
+ if (sections[1] == QStringLiteral("plugin")) {
+ if (!readPlugin(sections + 1, sectionCount - 1, true))
+ continue;
+ } else if (sections[1] == QLatin1String("import")) {
+ if (!readImport(sections + 1, sectionCount - 1, Import::Optional))
+ continue;
+ } else {
+ reportError(lineNumber, 0, QStringLiteral("only import and plugin can be optional, "
+ "not %1.").arg(sections[1]));
+ continue;
+ }
} else if (sections[0] == QLatin1String("classname")) {
if (sectionCount < 2) {
@@ -233,28 +318,9 @@ bool QmlDirParser::parse(const QString &source)
reportError(lineNumber, 0, QStringLiteral("designersupported does not expect any argument"));
else
_designerSupported = true;
- } else if (sections[0] == QLatin1String("depends")) {
- if (sectionCount != 3) {
- reportError(lineNumber, 0,
- QStringLiteral("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1));
- continue;
- }
-
- int major, minor;
- if (parseVersion(sections[2], &major, &minor)) {
- Component entry(sections[1], QString(), major, minor);
- entry.internal = true;
- _dependencies.insert(entry.typeName, entry);
- } else {
- reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[2]));
- }
- } else if (sections[0] == QLatin1String("import")) {
- if (sectionCount != 2) {
- reportError(lineNumber, 0,
- QStringLiteral("import requires 2 arguments, but %1 were provided").arg(sectionCount - 1));
+ } else if (sections[0] == QLatin1String("depends") || sections[0] == QLatin1String("import")) {
+ if (!readImport(sections, sectionCount, Import::Default))
continue;
- }
- _imports << sections[1];
} else if (sectionCount == 2) {
// No version specified (should only be used for relative qmldir files)
const Component entry(sections[0], sections[1], -1, -1);
@@ -342,12 +408,12 @@ QMultiHash<QString, QmlDirParser::Component> QmlDirParser::components() const
return _components;
}
-QHash<QString, QmlDirParser::Component> QmlDirParser::dependencies() const
+QList<QmlDirParser::Import> QmlDirParser::dependencies() const
{
return _dependencies;
}
-QStringList QmlDirParser::imports() const
+QList<QmlDirParser::Import> QmlDirParser::imports() const
{
return _imports;
}
diff --git a/src/libs/qmljs/parser/qmldirparser_p.h b/src/libs/qmljs/parser/qmldirparser_p.h
index 9b26d02351..5576cfa515 100644
--- a/src/libs/qmljs/parser/qmldirparser_p.h
+++ b/src/libs/qmljs/parser/qmldirparser_p.h
@@ -39,6 +39,9 @@
#include <QtCore/QUrl>
#include <QtCore/QHash>
#include <QtCore/QDebug>
+
+#include <languageutils/componentversion.h>
+
#include "qmljs/parser/qmljsglobal_p.h"
#include "qmljs/parser/qmljsengine_p.h"
#include "qmljs/parser/qmljsdiagnosticmessage_p.h"
@@ -72,14 +75,15 @@ public:
{
Plugin() = default;
- Plugin(const QString &name, const QString &path)
- : name(name), path(path)
+ Plugin(const QString &name, const QString &path, bool optional)
+ : name(name), path(path), optional(optional)
{
checkNonRelative("Plugin", name, path);
}
QString name;
QString path;
+ bool optional = false;
};
struct Component
@@ -117,9 +121,29 @@ public:
int minorVersion = 0;
};
+ struct Import
+ {
+ enum Flag {
+ Default = 0x0,
+ Auto = 0x1, // forward the version of the importing module
+ Optional = 0x2 // is not automatically imported but only a tooling hint
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ Import() = default;
+ Import(QString module, LanguageUtils::ComponentVersion version, Flags flags)
+ : module(module), version(version), flags(flags)
+ {
+ }
+
+ QString module;
+ LanguageUtils::ComponentVersion version; // invalid version is latest version, unless Flag::Auto
+ Flags flags;
+ };
+
QMultiHash<QString,Component> components() const;
- QHash<QString,Component> dependencies() const;
- QStringList imports() const;
+ QList<Import> dependencies() const;
+ QList<Import> imports() const;
QList<Script> scripts() const;
QList<Plugin> plugins() const;
bool designerSupported() const;
@@ -145,8 +169,8 @@ private:
QList<QmlJS::DiagnosticMessage> _errors;
QString _typeNamespace;
QMultiHash<QString,Component> _components;
- QHash<QString,Component> _dependencies;
- QStringList _imports;
+ QList<Import> _dependencies;
+ QList<Import> _imports;
QList<Script> _scripts;
QList<Plugin> _plugins;
bool _designerSupported = false;
diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp
index 79840e7bb6..ce8b7ba7fe 100644
--- a/src/libs/qmljs/qmljsdocument.cpp
+++ b/src/libs/qmljs/qmljsdocument.cpp
@@ -447,8 +447,8 @@ QByteArray LibraryInfo::calculateFingerprint() const
len = _imports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- foreach (const QString &import, _imports)
- hash.addData(import.toUtf8()); // import order matters, keep order-dependent
+ foreach (const QmlDirParser::Import &import, _imports)
+ hash.addData(import.module.toUtf8()); // import order matters, keep order-dependent
QByteArray res(hash.result());
res.append('L');
diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h
index 7a6b0624ff..c43ed03400 100644
--- a/src/libs/qmljs/qmljsdocument.h
+++ b/src/libs/qmljs/qmljsdocument.h
@@ -158,7 +158,7 @@ private:
FakeMetaObjectList _metaObjects;
QList<ModuleApiInfo> _moduleApis;
QStringList _dependencies; // from qmltypes "dependencies: [...]"
- QStringList _imports; // from qmldir "import" commands
+ QList<QmlDirParser::Import> _imports; // from qmldir "import" commands
QByteArray _fingerprint;
PluginTypeInfoStatus _dumpStatus = NoTypeInfo;
@@ -204,10 +204,10 @@ public:
void setDependencies(const QStringList &deps)
{ _dependencies = deps; }
- QStringList imports() const
+ QList<QmlDirParser::Import> imports() const
{ return _imports; }
- void setImports(const QStringList &imports)
+ void setImports(const QList<QmlDirParser::Import> &imports)
{ _imports = imports; }
bool isValid() const
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 2d91324f64..3f3c267f95 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -94,7 +94,8 @@ public:
bool importLibrary(const Document::Ptr &doc,
const QString &libraryPath,
Import *import, ObjectValue *targetObject,
- const QString &importPath = QString());
+ const QString &importPath = QString(),
+ bool optional = false);
void loadQmldirComponents(ObjectValue *import,
LanguageUtils::ComponentVersion version,
const LibraryInfo &libraryInfo,
@@ -465,7 +466,9 @@ bool LinkPrivate::importLibrary(const Document::Ptr &doc,
const QString &libraryPath,
Import *import,
ObjectValue *targetObject,
- const QString &importPath)
+ const QString &importPath,
+ bool optional
+ )
{
const ImportInfo &importInfo = import->info;
@@ -486,18 +489,24 @@ bool LinkPrivate::importLibrary(const Document::Ptr &doc,
// Note: Since this works on the same targetObject, the ModuleApi setPrototype()
// logic will not work. But ModuleApi isn't used in Qt versions that use import
// commands in qmldir files, and is pending removal in Qt 6.
- for (const auto &importName : libraryInfo.imports()) {
+ for (const auto &toImport : libraryInfo.imports()) {
+ QString importName = toImport.module;
+ ComponentVersion vNow = toImport.version;
+ // there was a period in which no version == auto, should we add || !vNow.isValid() to the if?
+ if (toImport.flags & QmlDirParser::Import::Auto)
+ vNow = version;
Import subImport;
subImport.valid = true;
- subImport.info = ImportInfo::moduleImport(importName, version, importInfo.as(), importInfo.ast());
- subImport.libraryPath = modulePath(importName, version.toString(), m_importPaths);
- bool subImportFound = importLibrary(doc, subImport.libraryPath, &subImport, targetObject, importPath);
+ subImport.info = ImportInfo::moduleImport(importName, vNow, importInfo.as(), importInfo.ast());
+ subImport.libraryPath = modulePath(importName, vNow.toString(), m_importPaths);
+ bool subImportFound = importLibrary(doc, subImport.libraryPath, &subImport, targetObject, importPath, true);
if (!subImportFound && errorLoc.isValid()) {
import->valid = false;
- error(doc, errorLoc,
- Link::tr(
- "Implicit import '%1' of QML module '%2' not found.\n\n"
+ if (!toImport.flags & QmlDirParser::Import::Optional)
+ error(doc, errorLoc,
+ Link::tr(
+ "Implicit import '%1' of QML module '%2' not found.\n\n"
"Import paths:\n"
"%3\n\n"
"For qmake projects, use the QML_IMPORT_PATH variable to add import paths.\n"
@@ -529,11 +538,11 @@ bool LinkPrivate::importLibrary(const Document::Ptr &doc,
QString(), version.toString());
}
}
- if (errorLoc.isValid()) {
+ if (!optional && errorLoc.isValid()) {
appendDiagnostic(doc, DiagnosticMessage(
Severity::ReadingTypeInfoWarning, errorLoc,
Link::tr("QML module contains C++ plugins, "
- "currently reading type information...")));
+ "currently reading type information... %1").arg(import->info.name())));
import->valid = false;
}
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError
@@ -541,7 +550,7 @@ bool LinkPrivate::importLibrary(const Document::Ptr &doc,
// Only underline import if package isn't described in .qmltypes anyway
// and is not a private package
QString packageName = importInfo.name();
- if (errorLoc.isValid()
+ if (!optional && errorLoc.isValid()
&& (packageName.isEmpty()
|| !m_valueOwner->cppQmlTypes().hasModule(packageName))
&& !packageName.endsWith(QLatin1String("private"), Qt::CaseInsensitive)) {
diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp
index a1c084a59a..c9d7979892 100644
--- a/src/libs/qmljs/qmljsplugindumper.cpp
+++ b/src/libs/qmljs/qmljsplugindumper.cpp
@@ -554,11 +554,12 @@ static void applyQt515MissingImportWorkaround(const QString &path, LibraryInfo &
return;
if (isQtQuick) {
- info.setImports(QStringList(QStringLiteral("QtQml")));
+ info.setImports(QList<QmlDirParser::Import>(
+ {QmlDirParser::Import(QLatin1String("QtQml"), ComponentVersion(), 0)}));
} else if (isQtQml) {
- info.setImports(QStringList(
- { QStringLiteral("QtQml.Models"),
- QStringLiteral("QtQml.WorkerScript") }));
+ info.setImports(QList<QmlDirParser::Import>(
+ { QmlDirParser::Import(QLatin1String("QtQml.Models"), ComponentVersion(), 0),
+ QmlDirParser::Import(QLatin1String("QtQml.WorkerScript"), ComponentVersion(), 0) }));
}
}