qmllint: Separate logic by import type in QQmlJSImportVisitor::visit
This patch reorganizes the logic of the import visitor to deal with each
import type (paths, qrc: urls, file: urls) separately.
This reorganisation fixes QTBUG-108803 which happened because "qrc:"
imports were being treated as paths leading to things like
":/untitled/qrc:/untitled/components".
Fixes: QTBUG-108803
Change-Id: I5af20d10c533455215895be66b5cd98a977fd18a
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit 5860c9c12c
)
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
8f45b7c7e1
commit
9c62538e5f
|
@ -2181,63 +2181,81 @@ void QQmlJSImportVisitor::addImportWithLocation(const QString &name,
|
|||
m_importLocations.insert(loc);
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::importFromHost(const QString &path, const QString &prefix,
|
||||
const QQmlJS::SourceLocation &location)
|
||||
{
|
||||
QFileInfo fileInfo(path);
|
||||
if (fileInfo.isFile()) {
|
||||
const auto scope = m_importer->importFile(path);
|
||||
const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
|
||||
m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
|
||||
addImportWithLocation(actualPrefix, location);
|
||||
} else if (fileInfo.isDir()) {
|
||||
const auto scopes = m_importer->importDirectory(path, prefix);
|
||||
m_rootScopeImports.addTypes(scopes);
|
||||
for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
|
||||
addImportWithLocation(*it, location);
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::importFromQrc(const QString &path, const QString &prefix,
|
||||
const QQmlJS::SourceLocation &location)
|
||||
{
|
||||
if (const auto &mapper = m_importer->resourceFileMapper()) {
|
||||
if (mapper->isFile(path)) {
|
||||
const auto entry = m_importer->resourceFileMapper()->entry(
|
||||
QQmlJSResourceFileMapper::resourceFileFilter(path));
|
||||
const auto scope = m_importer->importFile(entry.filePath);
|
||||
const QString actualPrefix =
|
||||
prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
|
||||
m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
|
||||
addImportWithLocation(actualPrefix, location);
|
||||
} else {
|
||||
const auto scopes = m_importer->importDirectory(path, prefix);
|
||||
m_rootScopeImports.addTypes(scopes);
|
||||
for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
|
||||
addImportWithLocation(*it, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
|
||||
{
|
||||
auto addImportLocation = [this, import](const QString &name) {
|
||||
addImportWithLocation(name, import->firstSourceLocation());
|
||||
};
|
||||
// construct path
|
||||
QString prefix = QLatin1String("");
|
||||
if (import->asToken.isValid()) {
|
||||
prefix += import->importId;
|
||||
}
|
||||
|
||||
auto filename = import->fileName.toString();
|
||||
if (!filename.isEmpty()) {
|
||||
const QFileInfo file(filename);
|
||||
const QString absolute = file.isRelative()
|
||||
? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
|
||||
: filename;
|
||||
|
||||
if (absolute.startsWith(u':')) {
|
||||
if (m_importer->resourceFileMapper()) {
|
||||
if (m_importer->resourceFileMapper()->isFile(absolute.mid(1))) {
|
||||
const auto entry = m_importer->resourceFileMapper()->entry(
|
||||
QQmlJSResourceFileMapper::resourceFileFilter(absolute.mid(1)));
|
||||
const auto scope = m_importer->importFile(entry.filePath);
|
||||
const QString actualPrefix = prefix.isEmpty()
|
||||
? QFileInfo(entry.resourcePath).baseName()
|
||||
: prefix;
|
||||
m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
|
||||
|
||||
addImportLocation(actualPrefix);
|
||||
} else {
|
||||
const auto scopes = m_importer->importDirectory(absolute, prefix);
|
||||
m_rootScopeImports.addTypes(scopes);
|
||||
for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end;
|
||||
it++)
|
||||
addImportLocation(*it);
|
||||
}
|
||||
const QUrl url(filename);
|
||||
const QString scheme = url.scheme();
|
||||
const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
|
||||
if (scheme == ""_L1) {
|
||||
QFileInfo fileInfo(url.path());
|
||||
QString absolute = fileInfo.isRelative()
|
||||
? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
|
||||
: filename;
|
||||
if (absolute.startsWith(u':')) {
|
||||
importFromQrc(absolute, prefix, importLocation);
|
||||
} else {
|
||||
importFromHost(absolute, prefix, importLocation);
|
||||
}
|
||||
|
||||
processImportWarnings(QStringLiteral("URL \"%1\"").arg(absolute), import->firstSourceLocation());
|
||||
processImportWarnings("path \"%1\""_L1.arg(url.path()), importLocation);
|
||||
return true;
|
||||
} else if (scheme == "file"_L1) {
|
||||
importFromHost(url.path(), prefix, importLocation);
|
||||
processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
|
||||
return true;
|
||||
} else if (scheme == "qrc"_L1) {
|
||||
importFromQrc(":"_L1 + url.path(), prefix, importLocation);
|
||||
processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
|
||||
return true;
|
||||
} else {
|
||||
m_logger->log("Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
|
||||
qmlImport, import->firstSourceLocation());
|
||||
}
|
||||
|
||||
QFileInfo path(absolute);
|
||||
if (path.isDir()) {
|
||||
const auto scopes = m_importer->importDirectory(path.canonicalFilePath(), prefix);
|
||||
m_rootScopeImports.addTypes(scopes);
|
||||
for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
|
||||
addImportLocation(*it);
|
||||
} else if (path.isFile()) {
|
||||
const auto scope = m_importer->importFile(path.canonicalFilePath());
|
||||
const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
|
||||
m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
|
||||
addImportLocation(actualPrefix);
|
||||
}
|
||||
|
||||
processImportWarnings(QStringLiteral("path \"%1\"").arg(path.canonicalFilePath()), import->firstSourceLocation());
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString path = buildName(import->importUri);
|
||||
|
@ -2249,7 +2267,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
|
|||
&staticModulesProvided);
|
||||
m_rootScopeImports.addTypes(imported);
|
||||
for (auto it = imported.types().keyBegin(), end = imported.types().keyEnd(); it != end; it++)
|
||||
addImportLocation(*it);
|
||||
addImportWithLocation(*it, import->firstSourceLocation());
|
||||
|
||||
if (prefix.isEmpty()) {
|
||||
for (const QString &staticModule : staticModulesProvided) {
|
||||
|
|
|
@ -335,7 +335,12 @@ private:
|
|||
void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name,
|
||||
const QQmlJS::SourceLocation &location);
|
||||
void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
|
||||
const QQmlJS::SourceLocation &location);
|
||||
const QQmlJS::SourceLocation &location);
|
||||
|
||||
void importFromHost(const QString &path, const QString &prefix,
|
||||
const QQmlJS::SourceLocation &location);
|
||||
void importFromQrc(const QString &path, const QString &prefix,
|
||||
const QQmlJS::SourceLocation &location);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import QtQuick
|
||||
|
||||
Text {
|
||||
text: "Here I am!"
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
pragma Strict
|
||||
|
||||
import QtQuick
|
||||
import 'qrc:/untitled/components' as C
|
||||
|
||||
Window {
|
||||
id: root
|
||||
C.Foo {}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/untitled/">
|
||||
<file alias="main.qml">main.qml</file>
|
||||
<file alias="components/Foo.qml">components/Foo.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -82,6 +82,8 @@ private Q_SLOTS:
|
|||
|
||||
void additionalImplicitImport();
|
||||
|
||||
void qrcUrlImport();
|
||||
|
||||
void attachedPropertyReuse();
|
||||
|
||||
void missingBuiltinsNoCrash();
|
||||
|
@ -1687,7 +1689,16 @@ void TestQmllint::additionalImplicitImport()
|
|||
const auto guard = qScopeGuard([this]() {m_linter.clearCache(); });
|
||||
runTest("additionalImplicitImport.qml", Result::clean(), {}, {},
|
||||
{ testFile("implicitImportResource.qrc") });
|
||||
}
|
||||
|
||||
void TestQmllint::qrcUrlImport()
|
||||
{
|
||||
const auto guard = qScopeGuard([this]() { m_linter.clearCache(); });
|
||||
|
||||
QJsonArray warnings;
|
||||
callQmllint(testFile("untitled/main.qml"), true, &warnings, {}, {},
|
||||
{ testFile("untitled/qrcUrlImport.qrc") });
|
||||
checkResult(warnings, Result::clean());
|
||||
}
|
||||
|
||||
void TestQmllint::attachedPropertyReuse()
|
||||
|
|
Loading…
Reference in New Issue