aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <[email protected]>2025-03-17 17:16:52 +0100
committerSami Shalayel <[email protected]>2025-03-19 12:00:29 +0100
commit5b5888587eb5f8f0d143d36c854eaec9a7f63768 (patch)
treeab30f6988cf752a57676d7e80caa2ef923f65ea2
parent17ac86aa6b0c12cd47d42fc828484395a4464757 (diff)
qmlls: don't mix up the in-memory and on-disk file
Fix QQmlJSImportVisitor to register the scope into its QQmlJSImporter. This is important to avoid scope duplication. Before this patch, the implicit directory import that QQmlJSImportVisitor processes via QQmlJSImporter would create a duplicate "lazy" scope that reads from the file on disk during population. With this patch, QQmlJSImporter will know the current scope and therefore won't duplicate it during the implicit directory import. Add a test to qmlls to make sure that it can lint enums correctly, even if the enum declaration does not exist on the on-disk version. Avoid warnings while resolving scopes by marking the currently to be populated scope with the '$InProcess$' baseTypeName. It will be populated (including with its real base type name) after the import were processed. Pick-to: 6.9 6.8 Fixes: QTBUG-134781 Change-Id: I05cc8f8cab9279ee07c4a48a1b467738354e11dd Reviewed-by: Fabian Kosmale <[email protected]>
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp24
-rw-r--r--src/qmlcompiler/qqmljsimporter_p.h1
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp21
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h1
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.cpp21
5 files changed, 68 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 54fd36bcc6..fcb02ad108 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -597,6 +597,11 @@ void QQmlJSImporter::processImport(
// Otherwise we have already done it in localFile2ScopeTree()
if (!val.scope.factory() && val.scope->baseType().isNull()) {
+ // ignore the scope currently analyzed by QQmlJSImportVisitor, as its only populated
+ // after importing the implicit directory.
+ if (val.scope->baseTypeName() == "$InProcess$"_L1)
+ continue;
+
// Composite types use QML names, and we should have resolved those already.
// ... except that old qmltypes files might specify composite types with C++ names.
// Warn about those.
@@ -887,6 +892,25 @@ QQmlJSScope::Ptr QQmlJSImporter::localFile2ScopeTree(const QString &filePath)
this, sourceFolderFile)) });
}
+/*!
+\internal
+Add scopes manually created and QQmlJSImportVisited to QQmlJSImporter.
+This allows theses scopes to not get loaded twice during linting, for example.
+
+Returns false if the importer contains a scope different than \a scope for the same
+QQmlJSScope::filePath.
+*/
+bool QQmlJSImporter::registerScope(const QQmlJSScope::Ptr &scope)
+{
+ Q_ASSERT(!scope.factory());
+
+ QQmlJSScope::Ptr &existing = m_importedFiles[scope->filePath()];
+ if (existing)
+ return existing == scope;
+ existing = scope;
+ return true;
+}
+
QQmlJSScope::Ptr QQmlJSImporter::importFile(const QString &file)
{
return localFile2ScopeTree(file);
diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h
index 7a02874d23..08017635e5 100644
--- a/src/qmlcompiler/qqmljsimporter_p.h
+++ b/src/qmlcompiler/qqmljsimporter_p.h
@@ -139,6 +139,7 @@ public:
ImportedTypes importHardCodedBuiltins();
QList<QQmlJS::DiagnosticMessage> importQmldirs(const QStringList &qmltypesFiles);
+ bool registerScope(const QQmlJSScope::Ptr &scope);
QQmlJSScope::Ptr importFile(const QString &file);
ImportedTypes importDirectory(const QString &directory, const QString &prefix = QString());
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index f20bdb0b8c..1a1f2b7576 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -38,6 +38,8 @@ static const QLatin1StringView wasNotFound
static const QLatin1StringView didYouAddAllImports
= "Did you add all imports and dependencies?"_L1;
+Q_STATIC_LOGGING_CATEGORY(lcImportVisitor, "qt.qml.importVisitor", QtWarningMsg);
+
/*!
\internal
Returns if assigning \a assignedType to \a property would require an
@@ -120,6 +122,23 @@ QString buildName(const Node *node)
return result;
}
+/*!
+ \internal
+ Make sure that the importer does not recreate the target scope when trying to import it via
+ implicit directory import.
+*/
+void QQmlJSImportVisitor::registerTargetIntoImporter(const QQmlJSScope::Ptr &target)
+{
+ target->setScopeType(QQmlSA::ScopeType::QMLScope);
+ target->setBaseTypeName("$InProcess$"_L1);
+ target->setFilePath(m_logger->filePath());
+ target->setIsComposite(true);
+ if (!m_importer->registerScope(target)) {
+ qCDebug(lcImportVisitor)
+ << "Couldn't register scope into importer: scope will be created multiple times.";
+ }
+}
+
QQmlJSImportVisitor::QQmlJSImportVisitor(
const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory, const QStringList &qmldirFiles)
@@ -135,6 +154,8 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(
importer->builtinInternalNames().contextualTypes().arrayType()),
{})
{
+ registerTargetIntoImporter(target);
+
m_currentScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
Q_ASSERT(logger); // must be valid
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index fb309f1cdb..072530d885 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -380,6 +380,7 @@ protected:
QStringList m_seenModuleQualifiers;
private:
+ void registerTargetIntoImporter(const QQmlJSScope::Ptr &target);
void checkSignal(
const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
const QString &handlerName, const QStringList &handlerParameters);
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
index 6aa5e1994d..a3fd0d9784 100644
--- a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
@@ -1191,6 +1191,23 @@ static EditingRecorder propertyTypoScenario(const QByteArray &fileUri)
return propertyTypo;
}
+static EditingRecorder inMemoryEnumScenario(const QByteArray &fileUri)
+{
+ EditingRecorder scenario;
+ scenario.lastFileUri = fileUri;
+
+ const QString enumWithTypo =
+ u"enum Hello { World, Qt, Kitty }\nproperty var xxx: SimpleItem.Q"_s;
+ // expect an error because of the missing "t"
+ scenario.changeText(5, 1, 5, 1, enumWithTypo);
+ scenario.setCurrentExpectedDiagnostic("Member \"Q\" not found");
+
+ // fix the typo and expect no more error, despite the enum not existing on the file on disk
+ scenario.changeText(6, 31, 6, 31, u"t"_s);
+
+ return scenario;
+}
+
void tst_qmlls_modules::linting_data()
{
QTest::addColumn<QString>("filePath");
@@ -1199,6 +1216,10 @@ void tst_qmlls_modules::linting_data()
QTest::addRow("property-typo")
<< u"linting/SimpleItem.qml"_s
<< propertyTypoScenario(testFileUrl(u"linting/SimpleItem.qml"_s).toEncoded());
+
+ QTest::addRow("in-memory-enum")
+ << u"linting/SimpleItem.qml"_s
+ << inMemoryEnumScenario(testFileUrl(u"linting/SimpleItem.qml"_s).toEncoded());
}
void tst_qmlls_modules::linting()