aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2024-08-09 14:16:24 +0200
committerSami Shalayel <sami.shalayel@qt.io>2024-09-10 12:01:28 +0200
commitb1ff6073a7247c921e20cd98b2e3297e7368681b (patch)
treeb59a386d739ddb203889758272a3d727b8048ebf
parent75603bfe1af69ca8a9ce45ee37dee722be8b800e (diff)
qmlls: find and use resource files from .qt/rcc
Search for resource files in .qt/rcc instead of only looking in the .rcc folder, and update the resource file mapper in setLoadPaths to actually use the resource files. Amends d0fcb75aab1b5a91260fb378d761257e1f2e4787 to: a) work on more sophisticated project structures with "prefer"-directives b) use resource files for all qmlls modules (previously only the linting module was using resource files) Also move the methods to find .qrc files into qqmljsutils_p.h and add a new test file. This patch fixes the spurious warning about SettingsData being not found and the missing completion suggestion for the SettingsData type in qml/popups/AdminDialog.qml on Ekke's project attached at QTBUG-126504. The build folder structure was mimicked in sophisticatedBuildFolder for the test. Also change DomEnvironment::semanticAnalysis() to return a value instead of a reference, and rename SemanticAnalysis::setLoadPaths to SemanticAnalysis::updateLoadPaths because it will set the loadpaths on all copies of semanticAnalysis instead of just setting it on the current one. Pick-to: 6.8 6.7 Task-number: QTBUG-127661 Task-number: QTBUG-126680 Change-Id: I47299711ced6cb952c4d3c1893495cb17a8eec7f Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qmlcompiler/qqmljsutils.cpp37
-rw-r--r--src/qmlcompiler/qqmljsutils_p.h2
-rw-r--r--src/qmldom/qqmldom_utils.cpp18
-rw-r--r--src/qmldom/qqmldom_utils_p.h2
-rw-r--r--src/qmldom/qqmldomtop.cpp25
-rw-r--r--src/qmldom/qqmldomtop_p.h5
-rw-r--r--src/qmlls/qqmllintsuggestions.cpp3
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmljsutils/CMakeLists.txt29
-rw-r--r--tests/auto/qml/qqmljsutils/data/buildfolder/.qt/rcc/appuntitled72_raw_qml_0.qrc6
-rw-r--r--tests/auto/qml/qqmljsutils/data/buildfolder/HelloWorld.qml3
-rw-r--r--tests/auto/qml/qqmljsutils/tst_qqmljsutils.cpp17
-rw-r--r--tests/auto/qml/qqmljsutils/tst_qqmljsutils.h21
-rw-r--r--tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/.qt/rcc/someQrc.qrc5
-rw-r--r--tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/HelloWorld.qml6
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.h19
-rw-r--r--tests/auto/qmlls/utils/data/AdminDialogFromSource.qml5
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0.qrc5
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0_extra_qmldirs.qrc7
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/qmake_projectname.qrc5
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/Main.qml3
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/LabelTitle.qml3
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/qmldir1
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/AdminDialog.qml5
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/qmldir1
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/qmldir1
-rw-r--r--tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qmldir6
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp8
28 files changed, 217 insertions, 32 deletions
diff --git a/src/qmlcompiler/qqmljsutils.cpp b/src/qmlcompiler/qqmljsutils.cpp
index 6de8741f1b..fcf56d4b96 100644
--- a/src/qmlcompiler/qqmljsutils.cpp
+++ b/src/qmlcompiler/qqmljsutils.cpp
@@ -5,6 +5,10 @@
#include "qqmljstyperesolver_p.h"
#include "qqmljsscopesbyid_p.h"
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+
#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -29,8 +33,7 @@ resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property,
// TODO: one could optimize the generated alias code for aliases pointing to aliases
// e.g., if idA.myAlias -> idB.myAlias2 -> idC.myProp, then one could directly generate
- // idA.myProp as pointing to idC.myProp.
- // This gets complicated when idB.myAlias is in a different Component than where the
+ // idA.myProp as pointing to idC.myProp. // This gets complicated when idB.myAlias is in a different Component than where the
// idA.myAlias is defined: scopeForId currently only contains the ids of the current
// component and alias resolution on the ids of a different component fails then.
if (QQmlJSMetaProperty nextProperty = property; nextProperty.isAlias()) {
@@ -248,4 +251,34 @@ bool canCompareWithQUrl(
&& typeResolver->equals(rhsType, typeResolver->urlType());
}
+static QVarLengthArray<QString, 2> resourceFoldersFromBuildFolder(const QString &buildFolder)
+{
+ QVarLengthArray<QString, 2> result;
+ const QDir dir(buildFolder);
+ if (dir.exists(u".rcc"_s)) {
+ result.append(dir.filePath(u".rcc"_s));
+ }
+ if (dir.exists(u".qt/rcc"_s)) {
+ result.append(dir.filePath(u".qt/rcc"_s));
+ }
+ return result;
+}
+
+
+QStringList QQmlJSUtils::resourceFilesFromBuildFolders(const QStringList &buildFolders)
+{
+ QStringList result;
+ for (const QString &path : buildFolders) {
+ for (const QString &resourceFolder : resourceFoldersFromBuildFolder(path)) {
+ QDirIterator it(resourceFolder, QStringList{ u"*.qrc"_s }, QDir::Files,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ result.append(it.next());
+ }
+ }
+ }
+ return result;
+}
+
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h
index c23399e9ae..1d6a642724 100644
--- a/src/qmlcompiler/qqmljsutils_p.h
+++ b/src/qmlcompiler/qqmljsutils_p.h
@@ -381,6 +381,8 @@ struct Q_QMLCOMPILER_EXPORT QQmlJSUtils
path = QDir::cleanPath(path);
return std::move(paths);
}
+
+ static QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders);
};
bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(
diff --git a/src/qmldom/qqmldom_utils.cpp b/src/qmldom/qqmldom_utils.cpp
index a7c985644a..a3e73ac3ef 100644
--- a/src/qmldom/qqmldom_utils.cpp
+++ b/src/qmldom/qqmldom_utils.cpp
@@ -7,6 +7,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qmetaobject.h>
#include <QtCore/qcbormap.h>
+#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
@@ -17,23 +18,6 @@ namespace Dom {
using namespace Qt::StringLiterals;
-QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders)
-{
- QStringList result;
- for (const QString &path : buildFolders) {
- QDir dir(path);
- if (!dir.cd(u".rcc"_s))
- continue;
-
- QDirIterator it(dir.canonicalPath(), QStringList{ u"*.qrc"_s }, QDir::Files,
- QDirIterator::Subdirectories);
- while (it.hasNext()) {
- result.append(it.next());
- }
- }
- return result;
-}
-
static QMetaEnum regionEnum = QMetaEnum::fromType<FileLocationRegion>();
QString fileLocationRegionName(FileLocationRegion region)
diff --git a/src/qmldom/qqmldom_utils_p.h b/src/qmldom/qqmldom_utils_p.h
index 6fcdb5fe10..c4cb8b0c16 100644
--- a/src/qmldom/qqmldom_utils_p.h
+++ b/src/qmldom/qqmldom_utils_p.h
@@ -38,8 +38,6 @@ qOverloadedVisitor(Ts...) -> qOverloadedVisitor<Ts...>;
namespace QQmlJS {
namespace Dom {
-QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders);
-
QString fileLocationRegionName(FileLocationRegion region);
FileLocationRegion fileLocationRegionValue(QStringView region);
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index cfc20d50ef..d76f09fe00 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -17,6 +17,8 @@
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
+
#include <QtCore/QBasicMutex>
#include <QtCore/QCborArray>
#include <QtCore/QDebug>
@@ -1823,13 +1825,13 @@ DomEnvironment::DomEnvironment(const QStringList &loadPaths, Options options,
Do not call this method inside of DomEnvironment's constructor! It requires weak_from_this() that
only works after the constructor call finished.
*/
-DomEnvironment::SemanticAnalysis &DomEnvironment::semanticAnalysis()
+DomEnvironment::SemanticAnalysis DomEnvironment::semanticAnalysis()
{
// QTBUG-124799: do not create a SemanticAnalysis in a temporary DomEnvironment, and use the one
// from the base environment instead.
if (m_base) {
- auto &result = m_base->semanticAnalysis();
- result.setLoadPaths(m_loadPaths);
+ auto result = m_base->semanticAnalysis();
+ result.updateLoadPaths(m_loadPaths);
return result;
}
@@ -1843,17 +1845,26 @@ DomEnvironment::SemanticAnalysis &DomEnvironment::semanticAnalysis()
DomEnvironment::SemanticAnalysis::SemanticAnalysis(const QStringList &loadPaths)
: m_mapper(
- std::make_shared<QQmlJSResourceFileMapper>(resourceFilesFromBuildFolders(loadPaths))),
+ std::make_shared<QQmlJSResourceFileMapper>(QQmlJSUtils::resourceFilesFromBuildFolders(loadPaths))),
m_importer(std::make_shared<QQmlJSImporter>(loadPaths, m_mapper.get(), true))
{
}
-void DomEnvironment::SemanticAnalysis::setLoadPaths(const QStringList &loadPaths)
+/*!
+\internal
+
+Sets the new load paths in the importer and recreate the mapper.
+
+This affects all copies of SemanticAnalysis that use the same QQmlJSImporter and QQmlJSMapper
+pointers.
+*/
+void DomEnvironment::SemanticAnalysis::updateLoadPaths(const QStringList &loadPaths)
{
if (loadPaths == m_importer->importPaths())
return;
m_importer->setImportPaths(loadPaths);
+ *m_mapper = QQmlJSResourceFileMapper(QQmlJSUtils::resourceFilesFromBuildFolders(loadPaths));
}
std::shared_ptr<DomEnvironment> DomEnvironment::create(const QStringList &loadPaths,
@@ -2153,7 +2164,7 @@ void DomEnvironment::setLoadPaths(const QStringList &v)
m_loadPaths = v;
if (m_semanticAnalysis)
- m_semanticAnalysis->setLoadPaths(v);
+ m_semanticAnalysis->updateLoadPaths(v);
}
QStringList DomEnvironment::loadPaths() const
@@ -2222,7 +2233,7 @@ void DomEnvironment::populateFromQmlFile(MutableDomItem &&qmlFile)
};
if (m_domCreationOptions.testFlag(DomCreationOption::WithSemanticAnalysis)) {
- auto &analysis = semanticAnalysis();
+ SemanticAnalysis analysis = semanticAnalysis();
auto scope = analysis.m_importer->importFile(qmlFile.canonicalFilePath());
auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(
scope, qmlFile, logger.get(), analysis.m_importer.get());
diff --git a/src/qmldom/qqmldomtop_p.h b/src/qmldom/qqmldomtop_p.h
index afdea6b311..0b6dadb19d 100644
--- a/src/qmldom/qqmldomtop_p.h
+++ b/src/qmldom/qqmldomtop_p.h
@@ -1116,13 +1116,14 @@ private:
struct SemanticAnalysis
{
SemanticAnalysis(const QStringList &loadPaths);
- void setLoadPaths(const QStringList &loadPaths);
+ void updateLoadPaths(const QStringList &loadPaths);
std::shared_ptr<QQmlJSResourceFileMapper> m_mapper;
std::shared_ptr<QQmlJSImporter> m_importer;
};
std::optional<SemanticAnalysis> m_semanticAnalysis;
- SemanticAnalysis &semanticAnalysis();
+public:
+ SemanticAnalysis semanticAnalysis();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)
diff --git a/src/qmlls/qqmllintsuggestions.cpp b/src/qmlls/qqmllintsuggestions.cpp
index 506b7b9171..491f15ae9c 100644
--- a/src/qmlls/qqmllintsuggestions.cpp
+++ b/src/qmlls/qqmllintsuggestions.cpp
@@ -6,6 +6,7 @@
#include <QtLanguageServer/private/qlanguageserverspec_p.h>
#include <QtQmlCompiler/private/qqmljslinter_p.h>
#include <QtQmlCompiler/private/qqmljslogger_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
#include <QtQmlDom/private/qqmldom_utils_p.h>
#include <QtQmlDom/private/qqmldomtop_p.h>
#include <QtCore/qdebug.h>
@@ -316,7 +317,7 @@ void QmlLintSuggestions::diagnoseHelper(const QByteArray &url,
bool silent = true;
const QString fileContents = doc.field(Fields::code).value().toString();
const QStringList qmltypesFiles;
- const QStringList resourceFiles = resourceFilesFromBuildFolders(imports);
+ const QStringList resourceFiles = QQmlJSUtils::resourceFilesFromBuildFolders(imports);
QList<QQmlJS::LoggerCategory> categories = QQmlJSLogger::defaultCategories();
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 6b81f4c616..f0d0fb49db 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -165,4 +165,5 @@ if(QT_FEATURE_private_tests)
endif()
if(NOT CMAKE_CROSSCOMPILING)
add_subdirectory(qmltyperegistrar)
+ add_subdirectory(qqmljsutils)
endif()
diff --git a/tests/auto/qml/qqmljsutils/CMakeLists.txt b/tests/auto/qml/qqmljsutils/CMakeLists.txt
new file mode 100644
index 0000000000..cff91f5b8b
--- /dev/null
+++ b/tests/auto/qml/qqmljsutils/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmljsutils LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qqmljsutils
+ SOURCES
+ tst_qqmljsutils.h tst_qqmljsutils.cpp
+ LIBRARIES
+ Qt::QmlCompilerPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+ DEFINES
+ QT_QQMLJSUTILS_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
+qt_internal_extend_target(tst_qqmljsutils CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QQMLJSUTILS_DATADIR=":/data"
+)
diff --git a/tests/auto/qml/qqmljsutils/data/buildfolder/.qt/rcc/appuntitled72_raw_qml_0.qrc b/tests/auto/qml/qqmljsutils/data/buildfolder/.qt/rcc/appuntitled72_raw_qml_0.qrc
new file mode 100644
index 0000000000..176ee061cf
--- /dev/null
+++ b/tests/auto/qml/qqmljsutils/data/buildfolder/.qt/rcc/appuntitled72_raw_qml_0.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/qt/qml/MyModule/">
+ <file alias="qml/HelloWorld.qml">../../HelloWorld.qml</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/auto/qml/qqmljsutils/data/buildfolder/HelloWorld.qml b/tests/auto/qml/qqmljsutils/data/buildfolder/HelloWorld.qml
new file mode 100644
index 0000000000..3052615aef
--- /dev/null
+++ b/tests/auto/qml/qqmljsutils/data/buildfolder/HelloWorld.qml
@@ -0,0 +1,3 @@
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qml/qqmljsutils/tst_qqmljsutils.cpp b/tests/auto/qml/qqmljsutils/tst_qqmljsutils.cpp
new file mode 100644
index 0000000000..e16ffe46f5
--- /dev/null
+++ b/tests/auto/qml/qqmljsutils/tst_qqmljsutils.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qqmljsutils.h"
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
+
+using namespace Qt::StringLiterals;
+
+void tst_qqmljsutils::findResourceFilesFromBuildFolders()
+{
+ const QString buildFolder = testFile("buildfolder");
+ const QStringList resourceFiles = QQmlJSUtils::resourceFilesFromBuildFolders({ buildFolder});
+ QCOMPARE(resourceFiles,
+ QStringList{ testFile(u"buildfolder/.qt/rcc/appuntitled72_raw_qml_0.qrc"_s) });
+};
+
+QTEST_MAIN(tst_qqmljsutils)
diff --git a/tests/auto/qml/qqmljsutils/tst_qqmljsutils.h b/tests/auto/qml/qqmljsutils/tst_qqmljsutils.h
new file mode 100644
index 0000000000..8637feab17
--- /dev/null
+++ b/tests/auto/qml/qqmljsutils/tst_qqmljsutils.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QQMLJSUTILS_P_H
+#define TST_QQMLJSUTILS_P_H
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtTest/QtTest>
+
+class tst_qqmljsutils: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qqmljsutils(): QQmlDataTest(QT_QQMLJSUTILS_DATADIR) { };
+
+private slots:
+ void findResourceFilesFromBuildFolders();
+};
+
+#endif // TST_QQMLJSUTILS_P_H
diff --git a/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/.qt/rcc/someQrc.qrc b/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/.qt/rcc/someQrc.qrc
new file mode 100644
index 0000000000..f09b9490b7
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/.qt/rcc/someQrc.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt/qml/MyModule/">
+ <file alias="qml/HelloWorld.qml">../../HelloWorld.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/HelloWorld.qml b/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/HelloWorld.qml
new file mode 100644
index 0000000000..a1bf0dd1e9
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/buildFolderWithQrc/HelloWorld.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.h b/tests/auto/qmldom/domitem/tst_qmldomitem.h
index 5744b65be6..9f30dd1768 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.h
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.h
@@ -4316,6 +4316,25 @@ private slots:
}
}
+ void environmentSetLoadPaths()
+ {
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr = DomEnvironment::create(
+ QStringList{},
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ auto semanticAnalysis = envPtr->semanticAnalysis();
+ QVERIFY(semanticAnalysis.m_mapper->isEmpty());
+ envPtr->setLoadPaths(QStringList { baseDir + u"/buildFolderWithQrc"_s });
+ QVERIFY(!semanticAnalysis.m_mapper->isEmpty());
+ QVERIFY(semanticAnalysis.m_mapper->isFile(u"/qt/qml/MyModule/qml/HelloWorld.qml"_s));
+ }
+
private:
QString baseDir;
QStringList qmltypeDirs;
diff --git a/tests/auto/qmlls/utils/data/AdminDialogFromSource.qml b/tests/auto/qmlls/utils/data/AdminDialogFromSource.qml
new file mode 100644
index 0000000000..4d57e38419
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/AdminDialogFromSource.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property LabelTitle title: LabelTitle {}
+}
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0.qrc b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0.qrc
new file mode 100644
index 0000000000..d496c59e1d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt/qml/Me/Apps/Main/">
+ <file alias="qml/popups/AdminDialog.qml">../../../AdminDialogFromSource.qml</file>
+ </qresource>
+</RCC> \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0_extra_qmldirs.qrc b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0_extra_qmldirs.qrc
new file mode 100644
index 0000000000..19eb738225
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/projectname_raw_qml_0_extra_qmldirs.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/qt/qml/Me/Apps/Main/">
+ <file alias="qml/qmldir">../../Me/Apps/Main/qml/qmldir</file>
+ <file alias="qml/common/qmldir">../../Me/Apps/Main/qml/common/qmldir</file>
+ <file alias="qml/popups/qmldir">../../Me/Apps/Main/qml/popups/qmldir</file>
+ </qresource>
+</RCC> \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/qmake_projectname.qrc b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/qmake_projectname.qrc
new file mode 100644
index 0000000000..68036107a3
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/.qt/rcc/qmake_projectname.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt/qml/Me/Apps/Main">
+ <file alias="qmldir">../../Me/Apps/Main/qmldir</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/Main.qml b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/Main.qml
new file mode 100644
index 0000000000..3052615aef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/Main.qml
@@ -0,0 +1,3 @@
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/LabelTitle.qml b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/LabelTitle.qml
new file mode 100644
index 0000000000..3052615aef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/LabelTitle.qml
@@ -0,0 +1,3 @@
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/qmldir b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/qmldir
new file mode 100644
index 0000000000..06128366a7
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/common/qmldir
@@ -0,0 +1 @@
+prefer :/qt/qml/Me/Apps/Main/
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/AdminDialog.qml b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/AdminDialog.qml
new file mode 100644
index 0000000000..a1b846292d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/AdminDialog.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property LabelTitle label: LabelTitle {}
+}
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/qmldir b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/qmldir
new file mode 100644
index 0000000000..06128366a7
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/popups/qmldir
@@ -0,0 +1 @@
+prefer :/qt/qml/Me/Apps/Main/
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/qmldir b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/qmldir
new file mode 100644
index 0000000000..06128366a7
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qml/qmldir
@@ -0,0 +1 @@
+prefer :/qt/qml/Me/Apps/Main/
diff --git a/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qmldir b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qmldir
new file mode 100644
index 0000000000..edf90d9cc3
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/sophisticatedBuildFolder/Me/Apps/Main/qmldir
@@ -0,0 +1,6 @@
+module Me.Apps.Main
+prefer :/qt/qml/Me/Apps/Main/
+Main 254.0 qml/Main.qml
+AdminDialog 254.0 qml/popups/AdminDialog.qml
+LabelTitle 254.0 qml/common/LabelTitle.qml
+
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
index 2787adc4a3..7c59ffaa26 100644
--- a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
@@ -44,7 +44,8 @@ tst_qmlls_utils::createEnvironmentAndLoadFile(const QString &filePath)
};
QStringList qmltypeDirs =
- QStringList({ dataDirectory(), QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+ QStringList({ dataDirectory(), QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath),
+ dataDirectory() + u"/sophisticatedBuildFolder"_s });
// This should be exactly the same options as qmlls uses in qqmlcodemodel.
// Otherwise, this test will not test the codepaths also used by qmlls and will be useless.
@@ -4324,6 +4325,11 @@ void tst_qmlls_utils::completions_data()
<< ExpectedCompletions{ { u"World"_s, CompletionItemKind::EnumMember },
{ u"Hello"_s, CompletionItemKind::Enum } }
<< QStringList{};
+
+ QTest::newRow("viaResourceFile")
+ << testFile("AdminDialogFromSource.qml") << 4 << 33
+ << ExpectedCompletions{ { u"LabelTitle"_s, CompletionItemKind::Constructor } }
+ << QStringList{};
}
void tst_qmlls_utils::completions()