From 739b32e54acaac3b1506f5fe862a684963f6483f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Apr 2022 17:10:17 +0200 Subject: [PATCH] QmlDesigner: Adapt to QmlDom changes Task-number: QDS-6799 Change-Id: Ic37d8549c9a6fd8bca770b07b0dcb0f84892841b Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.h | 7 +- .../projectstorage/qmldocumentparser.cpp | 76 ++++++++----- .../projectstorage/qmldocumentparser.h | 3 +- .../qmldocumentparserinterface.h | 6 +- .../projectstorage/qmltypesparser.cpp | 14 +-- .../unittest/projectstorageupdater-test.cpp | 100 ++++++++++-------- .../unit/unittest/qmldocumentparser-test.cpp | 78 +++++++++++--- tests/unit/unittest/qmldocumentparsermock.h | 3 +- 8 files changed, 194 insertions(+), 93 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 8da6567cf2e..8bd9664b272 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -97,7 +97,7 @@ private: const QList &qmldirDependencies, const QList &qmldirImports, SourceId qmldirSourceId, - SourceContextId directoryId, + Utils::SmallStringView directoryPath, ModuleId moduleId, Storage::SynchronizationPackage &package, SourceIds ¬UpdatedFileStatusSourceIds, @@ -115,11 +115,13 @@ private: SourceId qmldirSourceId, SourceContextId directoryId, ModuleId moduleId, + ModuleId pathModuleId, Storage::SynchronizationPackage &package, SourceIds ¬UpdatedFileStatusSourceIds); void parseQmlComponents(const Storage::ProjectDatas &projectDatas, Storage::SynchronizationPackage &package, - SourceIds ¬UpdatedFileStatusSourceIds); + SourceIds ¬UpdatedFileStatusSourceIds, + Utils::SmallStringView directoryPath); void parseQmlComponent(Utils::SmallStringView fileName, Utils::SmallStringView directory, Storage::ExportedTypes exportedTypes, @@ -129,6 +131,7 @@ private: SourceIds ¬UpdatedFileStatusSourceIds); void parseQmlComponent(Utils::SmallStringView fileName, Utils::SmallStringView filePath, + Utils::SmallStringView directoryPath, SourceId sourceId, Storage::SynchronizationPackage &package, SourceIds ¬UpdatedFileStatusSourceIds); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index b0d9496861d..5070387865e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -1,3 +1,4 @@ + /**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. @@ -54,16 +55,20 @@ Storage::Version convertVersion(QmlDom::Version version) convertVersionNumber(version.minorVersion)}; } -Utils::PathString convertUri(const QString &uri) +Utils::PathString createNormalizedPath(Utils::SmallStringView directoryPath, + const QString &relativePath) { - QStringView localPath{uri.begin() + 7, uri.end()}; + std::filesystem::path modulePath{std::string_view{directoryPath}, + std::filesystem::path::format::generic_format}; - std::filesystem::path path{ - std::u16string_view{localPath.utf16(), static_cast(localPath.size())}}; + modulePath /= relativePath.toStdString(); - auto x = std::filesystem::weakly_canonical(path); + Utils::PathString normalizedPath = modulePath.lexically_normal().generic_string(); - return Utils::PathString{x.generic_string()}; + if (normalizedPath[normalizedPath.size() - 1] == '/') + normalizedPath.resize(normalizedPath.size() - 1); + + return normalizedPath; } Storage::Import createImport(const QmlDom::Import &qmlImport, @@ -71,21 +76,26 @@ Storage::Import createImport(const QmlDom::Import &qmlImport, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { - if (qmlImport.uri == u"file://.") { - auto moduleId = storage.moduleId(directoryPath); + using QmlUriKind = QQmlJS::Dom::QmlUri::Kind; + + auto &&uri = qmlImport.uri; + + if (uri.kind() == QmlUriKind::RelativePath) { + auto path = createNormalizedPath(directoryPath, uri.localPath()); + auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); return Storage::Import(moduleId, Storage::Version{}, sourceId); } - if (qmlImport.uri.startsWith(u"file://")) { - auto moduleId = storage.moduleId(convertUri(qmlImport.uri)); - return Storage::Import(moduleId, Storage::Version{}, sourceId); + if (uri.kind() == QmlUriKind::ModuleUri) { + auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } - auto moduleId = storage.moduleId(Utils::SmallString{qmlImport.uri}); + auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}); return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } -QualifiedImports filterQualifiedImports(const QList &qmlImports, +QualifiedImports createQualifiedImports(const QList &qmlImports, SourceId sourceId, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) @@ -93,7 +103,7 @@ QualifiedImports filterQualifiedImports(const QList &qmlImports, QualifiedImports qualifiedImports; for (const QmlDom::Import &qmlImport : qmlImports) { - if (!qmlImport.importId.isEmpty()) + if (!qmlImport.importId.isEmpty() && !qmlImport.implicit) qualifiedImports.try_emplace(qmlImport.importId, createImport(qmlImport, sourceId, directoryPath, storage)); } @@ -107,11 +117,24 @@ void addImports(Storage::Imports &imports, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { - for (const QmlDom::Import &qmlImport : qmlImports) - imports.push_back(createImport(qmlImport, sourceId, directoryPath, storage)); + int importCount = 0; + for (const QmlDom::Import &qmlImport : qmlImports) { + if (!qmlImport.implicit) { + imports.push_back(createImport(qmlImport, sourceId, directoryPath, storage)); + ++importCount; + } + } + + auto localDirectoryModuleId = storage.moduleId(directoryPath); + imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); + ++importCount; + + auto qmlModuleId = storage.moduleId("QML"); + imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); + ++importCount; auto end = imports.end(); - auto begin = std::prev(end, qmlImports.size()); + auto begin = std::prev(end, importCount); std::sort(begin, end); imports.erase(std::unique(begin, end), end); @@ -196,14 +219,17 @@ void addEnumeraton(Storage::Type &type, const QmlDom::Component &component) Storage::Type QmlDocumentParser::parse(const QString &sourceContent, Storage::Imports &imports, - SourceId sourceId) + SourceId sourceId, + Utils::SmallStringView directoryPath) { Storage::Type type; - QmlDom::DomItem environment = QmlDom::DomEnvironment::create( - {}, - QmlDom::DomEnvironment::Option::SingleThreaded - | QmlDom::DomEnvironment::Option::NoDependencies); + using Option = QmlDom::DomEnvironment::Option; + + QmlDom::DomItem environment = QmlDom::DomEnvironment::create({}, + Option::SingleThreaded + | Option::NoDependencies + | Option::WeakLoad); QmlDom::DomItem items; @@ -238,9 +264,11 @@ Storage::Type QmlDocumentParser::parse(const QString &sourceContent, const QmlDom::QmlObject &qmlObject = objects.front(); const auto qmlImports = qmlFile->imports(); - auto directoryPath{m_pathCache.sourceContextPath(m_pathCache.sourceContextId(sourceId))}; - const auto qualifiedImports = filterQualifiedImports(qmlImports, sourceId, directoryPath, m_storage); + const auto qualifiedImports = createQualifiedImports(qmlImports, + sourceId, + directoryPath, + m_storage); type.prototype = createImportedTypeName(qmlObject.name(), qualifiedImports); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index 26600baeb02..4e92bd2a5bd 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -53,7 +53,8 @@ public: Storage::Type parse(const QString &sourceContent, Storage::Imports &imports, - SourceId sourceId) override; + SourceId sourceId, + Utils::SmallStringView directoryPath) override; private: ProjectStorage &m_storage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h index 47603ad9953..fc985178f2f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h @@ -34,7 +34,11 @@ namespace QmlDesigner { class QmlDocumentParserInterface { public: - virtual Storage::Type parse(const QString &sourceContent, Storage::Imports &imports, SourceId sourceId) = 0; + virtual Storage::Type parse(const QString &sourceContent, + Storage::Imports &imports, + SourceId sourceId, + Utils::SmallStringView directoryPath) + = 0; protected: ~QmlDocumentParserInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index cd879d33c12..56203621fb5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -47,7 +47,7 @@ namespace { using ComponentWithoutNamespaces = QMap; ComponentWithoutNamespaces createComponentNameWithoutNamespaces( - const QHash &objects) + const QHash &objects) { ComponentWithoutNamespaces componentWithoutNamespaces; @@ -409,15 +409,17 @@ EnumerationTypes addEnumerationTypes(Storage::Types &types, void addType(Storage::Types &types, SourceId sourceId, ModuleId cppModuleId, - const QQmlJSScope &component, + const QQmlJSExportedScope &exportScope, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespace) { + const auto &component = *exportScope.scope; + auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals( component.ownMethods(), componentNameWithoutNamespace); TypeNameString typeName{component.internalName()}; auto enumerations = component.ownEnumerations(); - auto exports = component.exports(); + auto exports = exportScope.exports; auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations); types.emplace_back(Utils::SmallStringView{typeName}, @@ -435,7 +437,7 @@ void addType(Storage::Types &types, void addTypes(Storage::Types &types, const Storage::ProjectData &projectData, - const QHash &objects, + const QHash &objects, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) { @@ -445,7 +447,7 @@ void addTypes(Storage::Types &types, addType(types, projectData.sourceId, projectData.moduleId, - *object.get(), + object, storage, componentNameWithoutNamespaces); } @@ -458,7 +460,7 @@ void QmlTypesParser::parse(const QString &sourceContent, const Storage::ProjectData &projectData) { QQmlJSTypeDescriptionReader reader({}, sourceContent); - QHash components; + QHash components; QStringList dependencies; bool isValid = reader(&components, &dependencies); if (!isValid) diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp index 10167352805..e9ab6ea4d11 100644 --- a/tests/unit/unittest/projectstorageupdater-test.cpp +++ b/tests/unit/unittest/projectstorageupdater-test.cpp @@ -183,7 +183,7 @@ public: ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml")))) .WillByDefault(Return(qmlDocument1)); - ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml")))) + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml")))) .WillByDefault(Return(qmlDocument2)); ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))) .WillByDefault(Return(qmlDocument3)); @@ -192,18 +192,18 @@ public: ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/types/example2.qmltypes")))) .WillByDefault(Return(qmltypes2)); - ON_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _)) - .WillByDefault([&](auto, auto &imports, auto) { + ON_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _, _)) + .WillByDefault([&](auto, auto &imports, auto, auto) { imports.push_back(import1); return firstType; }); - ON_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _)) - .WillByDefault([&](auto, auto &imports, auto) { + ON_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _)) + .WillByDefault([&](auto, auto &imports, auto, auto) { imports.push_back(import2); return secondType; }); - ON_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _)) - .WillByDefault([&](auto, auto &imports, auto) { + ON_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _, _)) + .WillByDefault([&](auto, auto &imports, auto, auto) { imports.push_back(import3); return thirdType; }); @@ -239,7 +239,7 @@ protected: SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/types/example2.qmltypes"); SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir"); SourceId qmlDocumentSourceId1 = sourcePathCache.sourceId("/path/First.qml"); - SourceId qmlDocumentSourceId2 = sourcePathCache.sourceId("/path/First.2.qml"); + SourceId qmlDocumentSourceId2 = sourcePathCache.sourceId("/path/First2.qml"); SourceId qmlDocumentSourceId3 = sourcePathCache.sourceId("/path/Second.qml"); ModuleId qmlModuleId{storage.moduleId("Qml")}; ModuleId qmlCppNativeModuleId{storage.moduleId("Qml-cppnative")}; @@ -249,6 +249,8 @@ protected: ModuleId builtinCppNativeModuleId{storage.moduleId("QML-cppnative")}; ModuleId quickModuleId{storage.moduleId("Quick")}; ModuleId quickCppNativeModuleId{storage.moduleId("Quick-cppnative")}; + ModuleId pathModuleId{storage.moduleId("/path")}; + ModuleId subPathQmlModuleId{storage.moduleId("/path/qml")}; Storage::Type objectType{"QObject", Storage::ImportedType{}, Storage::TypeAccessSemantics::Reference, @@ -397,6 +399,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"))); EXPECT_CALL(projectStorageMock, moduleId(Eq("Example-cppnative"))); + EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"))); EXPECT_CALL(projectStorageMock, synchronize( AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)), @@ -445,14 +448,14 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlDocuments) .WillByDefault(Return(qmlDocument3)); QString qmldir{R"(module Example FirstType 1.0 First.qml - FirstTypeV2 2.2 First.2.qml + FirstTypeV2 2.2 First2.qml SecondType 2.1 OldSecond.qml SecondType 2.2 Second.qml)"}; EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))) .WillRepeatedly(Return(qmldir)); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml")))); - EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml")))); + EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/OldSecond.qml")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))); @@ -463,7 +466,7 @@ TEST_F(ProjectStorageUpdater, ParseQmlDocuments) { QString qmldir{R"(module Example FirstType 1.0 First.qml - FirstTypeV2 2.2 First.2.qml + FirstTypeV2 2.2 First2.qml SecondType 2.2 Second.qml)"}; QString qmlDocument1{"First{}"}; QString qmlDocument2{"Second{}"}; @@ -471,14 +474,14 @@ TEST_F(ProjectStorageUpdater, ParseQmlDocuments) ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml")))) .WillByDefault(Return(qmlDocument1)); - ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml")))) + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml")))) .WillByDefault(Return(qmlDocument2)); ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))) .WillByDefault(Return(qmlDocument3)); - EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _)); - EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _)); - EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _)); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _, _)); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _)); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _, _)); updater.update(qmlDirs, {}); } @@ -496,7 +499,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments) { QString qmldir{R"(module Example FirstType 1.0 First.qml - FirstType 2.2 First.2.qml + FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); @@ -512,21 +515,24 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments) qmlDocumentSourceId1, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), - AllOf(IsStorageType("First.2.qml", + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", Storage::ImportedType{"Object2"}, TypeAccessSemantics::Reference, qmlDocumentSourceId2, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))), + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::ImportedType{"Object3"}, TypeAccessSemantics::Reference, qmlDocumentSourceId3, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))), + ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, @@ -565,7 +571,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemoved) { QString qmldir{R"(module Example FirstType 1.0 First.qml - FirstType 2.2 First.2.qml + FirstType 2.2 First2.qml typeinfo example.qmltypes typeinfo types/example2.qmltypes )"}; @@ -595,14 +601,16 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemoved) qmlDocumentSourceId1, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), - AllOf(IsStorageType("First.2.qml", + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", Storage::ImportedType{}, TypeAccessSemantics::Reference, qmlDocumentSourceId2, Storage::ChangeLevel::Minimal), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))))), + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, @@ -645,7 +653,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate) { QString qmldir{R"(module Example FirstType 1.0 First.qml - FirstType 2.2 First.2.qml + FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3))) @@ -663,21 +671,24 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate) qmlDocumentSourceId1, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), - AllOf(IsStorageType("First.2.qml", + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", Storage::ImportedType{"Object2"}, TypeAccessSemantics::Reference, qmlDocumentSourceId2, Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))), + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::ImportedType{}, TypeAccessSemantics::Reference, qmlDocumentSourceId3, Storage::ChangeLevel::Minimal), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))), + ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, @@ -712,7 +723,7 @@ TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments) { QString qmldir{R"(module Example FirstType 1.1 First.qml - FirstType 2.2 First.2.qml + FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3))) @@ -767,7 +778,7 @@ TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged) qmlDocumentSourceId1, Storage::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Type::exportedTypes, IsEmpty())), - AllOf(IsStorageType("First.2.qml", + AllOf(IsStorageType("First2.qml", Storage::ImportedType{"Object2"}, TypeAccessSemantics::Reference, qmlDocumentSourceId2, @@ -921,7 +932,8 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithDifferentVersionButSame UnorderedElementsAre( IsExportedType(exampleModuleId, "FirstType", 1, 0), IsExportedType(exampleModuleId, "FirstType", 1, 1), - IsExportedType(exampleModuleId, "FirstType", 6, 0)))))), + IsExportedType(exampleModuleId, "FirstType", 6, 0), + IsExportedType(pathModuleId, "First", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)), Field(&SynchronizationPackage::updatedFileStatusSourceIds, @@ -951,16 +963,17 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithDifferentTypeNameButSam synchronize( AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)), Field(&SynchronizationPackage::types, - UnorderedElementsAre(AllOf( - IsStorageType("First.qml", - Storage::ImportedType{"Object"}, - TypeAccessSemantics::Reference, - qmlDocumentSourceId1, - Storage::ChangeLevel::Full), - Field(&Storage::Type::exportedTypes, - UnorderedElementsAre( - IsExportedType(exampleModuleId, "FirstType", 1, 0), - IsExportedType(exampleModuleId, "FirstType2", 1, 0)))))), + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::ImportedType{"Object"}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId1, + Storage::ChangeLevel::Full), + Field(&Storage::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(exampleModuleId, "FirstType2", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)), Field(&SynchronizationPackage::updatedFileStatusSourceIds, @@ -1032,7 +1045,8 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithRelativeFilePath) Field(&Storage::Type::exportedTypes, UnorderedElementsAre( IsExportedType(exampleModuleId, "FirstType", 1, 0), - IsExportedType(exampleModuleId, "FirstType2", 1, 0)))))), + IsExportedType(exampleModuleId, "FirstType2", 1, 0), + IsExportedType(subPathQmlModuleId, "First", -1, -1)))))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId)), Field(&SynchronizationPackage::updatedFileStatusSourceIds, diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp index e9fa41100bc..9835745cc33 100644 --- a/tests/unit/unittest/qmldocumentparser-test.cpp +++ b/tests/unit/unittest/qmldocumentparser-test.cpp @@ -133,12 +133,13 @@ protected: Storage::Imports imports; SourceId qmlFileSourceId{sourcePathCache.sourceId("/path/to/qmlfile.qml")}; SourceContextId qmlFileSourceContextId{sourcePathCache.sourceContextId(qmlFileSourceId)}; - ModuleId directoryModuleId{storage.moduleId("/path/to")}; + Utils::PathString directoryPath{sourcePathCache.sourceContextPath(qmlFileSourceContextId)}; + ModuleId directoryModuleId{storage.moduleId(directoryPath)}; }; TEST_F(QmlDocumentParser, Prototype) { - auto type = parser.parse("Example{}", imports, qmlFileSourceId); + auto type = parser.parse("Example{}", imports, qmlFileSourceId, directoryPath); ASSERT_THAT(type, HasPrototype(Storage::ImportedType("Example"))); } @@ -149,7 +150,7 @@ TEST_F(QmlDocumentParser, QualifiedPrototype) QString text = R"(import Example 2.1 as Example Example.Item{})"; - auto type = parser.parse(text, imports, qmlFileSourceId); + auto type = parser.parse(text, imports, qmlFileSourceId, directoryPath); ASSERT_THAT(type, HasPrototype(Storage::QualifiedImportedType("Item", @@ -160,7 +161,7 @@ TEST_F(QmlDocumentParser, QualifiedPrototype) TEST_F(QmlDocumentParser, Properties) { - auto type = parser.parse(R"(Example{ property int foo })", imports, qmlFileSourceId); + auto type = parser.parse(R"(Example{ property int foo })", imports, qmlFileSourceId, directoryPath); ASSERT_THAT(type.propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration("foo", @@ -175,7 +176,8 @@ TEST_F(QmlDocumentParser, QualifiedProperties) auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Foo foo})", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration( @@ -192,7 +194,8 @@ TEST_F(QmlDocumentParser, EnumerationInProperties) auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Enumeration.Foo foo})", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration("foo", @@ -207,7 +210,8 @@ TEST_F(QmlDocumentParser, QualifiedEnumerationInProperties) auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Enumeration.Foo foo})", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration( @@ -223,30 +227,71 @@ TEST_F(QmlDocumentParser, Imports) { ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQmlModuleId = storage.moduleId("QtQml"); ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); auto type = parser.parse(R"(import QtQuick import "../foo" Example{})", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(imports, UnorderedElementsAre( Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, Storage::Import{fooDirectoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qmlModuleId, Storage::Version{1, 0}, qmlFileSourceId}, - Storage::Import{qtQmlModuleId, Storage::Version{6, 0}, qmlFileSourceId}, + Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); } +TEST_F(QmlDocumentParser, ImportsWithVersion) +{ + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); + ModuleId qmlModuleId = storage.moduleId("QML"); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + + auto type = parser.parse(R"(import QtQuick 2.1 + import "../foo" + Example{})", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(imports, + UnorderedElementsAre( + Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, + Storage::Import{fooDirectoryModuleId, Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, Storage::Version{2, 1}, qmlFileSourceId})); +} + +TEST_F(QmlDocumentParser, ImportsWithExplictDirectory) +{ + ModuleId qmlModuleId = storage.moduleId("QML"); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + + auto type = parser.parse(R"(import QtQuick + import "../to" + import "." + Example{})", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT( + imports, + UnorderedElementsAre(Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); +} + TEST_F(QmlDocumentParser, Functions) { auto type = parser.parse( "Example{\n function someScript(x, y) {}\n function otherFunction() {}\n}", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.functionDeclarations, UnorderedElementsAre(AllOf(IsFunctionDeclaration("otherFunction", ""), @@ -261,7 +306,8 @@ TEST_F(QmlDocumentParser, Signals) { auto type = parser.parse("Example{\n signal someSignal(int x, real y)\n signal signal2()\n}", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.signalDeclarations, UnorderedElementsAre(AllOf(IsSignalDeclaration("someSignal"), @@ -277,7 +323,8 @@ TEST_F(QmlDocumentParser, Enumeration) auto type = parser.parse("Example{\n enum Color{red, green, blue=10, white}\n enum " "State{On,Off}\n}", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(type.enumerationDeclarations, UnorderedElementsAre( @@ -308,7 +355,8 @@ TEST_F(QmlDocumentParser, DISABLED_DuplicateImportsAreRemoved) Example{})", imports, - qmlFileSourceId); + qmlFileSourceId, + directoryPath); ASSERT_THAT(imports, UnorderedElementsAre( diff --git a/tests/unit/unittest/qmldocumentparsermock.h b/tests/unit/unittest/qmldocumentparsermock.h index 31178f58aba..2b81dfd1bef 100644 --- a/tests/unit/unittest/qmldocumentparsermock.h +++ b/tests/unit/unittest/qmldocumentparsermock.h @@ -36,6 +36,7 @@ public: parse, (const QString &sourceContent, QmlDesigner::Storage::Imports &imports, - QmlDesigner::SourceId sourceId), + QmlDesigner::SourceId sourceId, + Utils::SmallStringView directoryPath), (override)); };