From 9194300ebe76eaf0864919c40f7086e7a760e88e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 24 May 2022 18:07:17 +0200 Subject: QmlDesigner: Support alias on value types in project storage There can be an alias on value types like font.size. For that case there is an alias stem and an alias tail. This patch is introducing support for it but still not implementing all of the dependency management. Task-number: QDS-7116 Change-Id: I83d570c5b954cc0378f25ce91609d809bb41a963 Reviewed-by: Thomas Hartmann --- .../designercore/projectstorage/projectstorage.h | 66 ++- .../projectstorage/projectstoragetypes.h | 21 +- tests/unit/unittest/gtest-creator-printing.cpp | 4 +- tests/unit/unittest/projectstorage-test.cpp | 576 +++++++++++++++++++++ tests/unit/unittest/qmltypesparser-test.cpp | 16 + 5 files changed, 667 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 210e22004b..f0e307cbcc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -399,11 +399,13 @@ private: PropertyDeclarationId propertyDeclarationId, ImportedTypeNameId aliasImportedTypeNameId, Utils::SmallString aliasPropertyName, + Utils::SmallString aliasPropertyNameTail, PropertyDeclarationId aliasPropertyDeclarationId = PropertyDeclarationId{}) : typeId{typeId} , propertyDeclarationId{propertyDeclarationId} , aliasImportedTypeNameId{aliasImportedTypeNameId} , aliasPropertyName{std::move(aliasPropertyName)} + , aliasPropertyNameTail{std::move(aliasPropertyNameTail)} , aliasPropertyDeclarationId{aliasPropertyDeclarationId} {} @@ -419,6 +421,7 @@ private: PropertyDeclarationId propertyDeclarationId; ImportedTypeNameId aliasImportedTypeNameId; Utils::SmallString aliasPropertyName; + Utils::SmallString aliasPropertyNameTail; PropertyDeclarationId aliasPropertyDeclarationId; }; @@ -783,15 +786,21 @@ private: auto callback = [&](long long typeId, long long propertyDeclarationId, long long propertyImportedTypeNameId, - long long aliasPropertyDeclarationId) { + long long aliasPropertyDeclarationId, + long long aliasPropertyDeclarationTailId) { auto aliasPropertyName = selectPropertyNameStatement.template value( aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId != -1) + aliasPropertyNameTail = selectPropertyNameStatement.template value( + aliasPropertyDeclarationTailId); relinkableAliasPropertyDeclarations .emplace_back(TypeId{typeId}, PropertyDeclarationId{propertyDeclarationId}, ImportedTypeNameId{propertyImportedTypeNameId}, - std::move(aliasPropertyName)); + std::move(aliasPropertyName), + std::move(aliasPropertyNameTail)); updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); @@ -966,6 +975,20 @@ private: relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); } + PropertyDeclarationId fetchAliasId(TypeId aliasTypeId, + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail) + { + if (aliasPropertyNameTail.empty()) + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + + auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, + aliasPropertyName); + + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, + aliasPropertyNameTail); + } + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) { for (const auto &aliasDeclaration : aliasDeclarations) { @@ -974,8 +997,9 @@ private: if (!aliasTypeId) throw TypeNameDoesNotExists{}; - auto aliasId = fetchPropertyDeclarationIdByTypeIdAndNameUngarded( - aliasTypeId, aliasDeclaration.aliasPropertyName); + auto aliasId = fetchAliasId(aliasTypeId, + aliasDeclaration.aliasPropertyName, + aliasDeclaration.aliasPropertyNameTail); updatePropertyDeclarationAliasIdAndTypeNameIdStatement .write(&aliasDeclaration.propertyDeclarationId, @@ -1020,8 +1044,19 @@ private: Prototypes &relinkablePrototypes) { std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { - return std::tie(first.moduleId, first.name, first.version) - < std::tie(second.moduleId, second.name, second.version); + if (first.moduleId < second.moduleId) + return true; + else if (first.moduleId > second.moduleId) + return false; + + auto nameCompare = Sqlite::compare(first.name, second.name); + + if (nameCompare < 0) + return true; + else if (nameCompare > 0) + return false; + + return first.version < second.version; }); auto range = selectExportedTypesForSourceIdsStatement.template range( @@ -1105,7 +1140,8 @@ private: PropertyDeclarationId{propertyDeclarationId}, fetchImportedTypeNameId(value.typeName, sourceId), - std::move(value.aliasPropertyName)); + value.aliasPropertyName, + value.aliasPropertyNameTail); return Sqlite::CallbackControl::Abort; }; @@ -1147,6 +1183,7 @@ private: fetchImportedTypeNameId(value.typeName, sourceId), value.aliasPropertyName, + value.aliasPropertyNameTail, view.aliasId); } @@ -2065,10 +2102,17 @@ private: propertyDeclarationTable, Sqlite::ForeignKeyAction::NoAction, Sqlite::ForeignKeyAction::Restrict); + auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "aliasPropertyDeclarationTailId", + propertyDeclarationTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, "aliasPropertyDeclarationId IS NOT NULL"); + propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, + "aliasPropertyDeclarationTailId IS NOT NULL"); propertyDeclarationTable.initialize(database); } @@ -2571,10 +2615,12 @@ public: "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT " "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", database}; - ReadStatement<4, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ + ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - "target.propertyDeclarationId FROM propertyDeclarations AS alias JOIN propertyDeclarations " - "AS target ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId WHERE " + "alias.aliasPropertyDeclarationId, ifnull(alias.aliasPropertyDeclarationTailId, -1) FROM " + "propertyDeclarations AS alias JOIN propertyDeclarations AS target ON " + "alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + "alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId WHERE " "alias.propertyTypeId=?1 OR target.typeId=?1 OR alias.propertyImportedTypeNameId IN " "(SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " "WHERE typeId=?1)", diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index f025babdb0..dd36e15921 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -640,10 +640,13 @@ public: explicit PropertyDeclaration(Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName) + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(typeName)} , aliasPropertyName{aliasPropertyName} + , aliasPropertyNameTail{aliasPropertyNameTail} + , traits{traits} , kind{PropertyKind::Property} {} @@ -651,9 +654,11 @@ public: explicit PropertyDeclaration(Utils::SmallStringView name, TypeId propertyTypeId, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName) + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , aliasPropertyName{aliasPropertyName} + , aliasPropertyNameTail{aliasPropertyNameTail} , traits{traits} , propertyTypeId{propertyTypeId} , kind{PropertyKind::Property} @@ -662,9 +667,12 @@ public: explicit PropertyDeclaration(Utils::SmallStringView name, long long propertyTypeId, int traits, - Utils::SmallStringView aliasPropertyName) + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , aliasPropertyName{aliasPropertyName} + , aliasPropertyNameTail{aliasPropertyNameTail} + , traits{static_cast(traits)} , propertyTypeId{propertyTypeId} , kind{PropertyKind::Property} @@ -672,10 +680,13 @@ public: explicit PropertyDeclaration(Utils::SmallStringView name, ImportedTypeName aliasTypeName, - Utils::SmallStringView aliasPropertyName) + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(aliasTypeName)} , aliasPropertyName{aliasPropertyName} + , aliasPropertyNameTail{aliasPropertyNameTail} + , kind{PropertyKind::Alias} {} @@ -683,6 +694,7 @@ public: { return first.name == second.name && first.typeName == second.typeName && first.aliasPropertyName == second.aliasPropertyName + && first.aliasPropertyNameTail == second.aliasPropertyNameTail && first.traits == second.traits && first.kind == second.kind; } @@ -690,6 +702,7 @@ public: Utils::SmallString name; ImportedTypeName typeName; Utils::SmallString aliasPropertyName; + Utils::SmallString aliasPropertyNameTail; PropertyDeclarationTraits traits = {}; TypeId propertyTypeId; TypeId typeId; diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 24690730ae..bc187ac62f 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -1188,8 +1188,8 @@ std::ostream &operator<<(std::ostream &out, const PropertyDeclaration &propertyD using Utils::operator<<; return out << "(\"" << propertyDeclaration.name << "\", " << propertyDeclaration.typeName << ", " << propertyDeclaration.typeId << ", " << propertyDeclaration.traits << ", " - << propertyDeclaration.typeId << ", \"" << propertyDeclaration.aliasPropertyName - << "\")"; + << propertyDeclaration.propertyTypeId << ", \"" + << propertyDeclaration.aliasPropertyName << "\")"; } std::ostream &operator<<(std::ostream &out, PropertyDeclarationTraits traits) diff --git a/tests/unit/unittest/projectstorage-test.cpp b/tests/unit/unittest/projectstorage-test.cpp index ebc60ff22e..e0ad752f74 100644 --- a/tests/unit/unittest/projectstorage-test.cpp +++ b/tests/unit/unittest/projectstorage-test.cpp @@ -334,6 +334,160 @@ protected: return package; } + auto createSynchronizationPackageWithIndirectAliases() + { + SynchronizationPackage package; + + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); + + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); + + package.updatedModuleDependencySourceIds.push_back(sourceId1); + package.updatedModuleDependencySourceIds.push_back(sourceId2); + + importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); + moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); + + importsSourceId2.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); + moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); + + package.types.push_back(Storage::Type{ + "QQuickItem", + Storage::ImportedType{"QObject"}, + TypeAccessSemantics::Reference, + sourceId1, + {Storage::ExportedType{qtQuickModuleId, "Item"}, + Storage::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}, + {Storage::PropertyDeclaration{"children", + Storage::ImportedType{"QChildren"}, + Storage::PropertyDeclarationTraits::IsReadOnly}, + Storage::PropertyDeclaration{"kids", + Storage::ImportedType{"QChildren2"}, + Storage::PropertyDeclarationTraits::IsReadOnly}}}); + + package.types.push_back( + Storage::Type{"QObject", + Storage::ImportedType{}, + TypeAccessSemantics::Reference, + sourceId2, + {Storage::ExportedType{qmlModuleId, "Object", Storage::Version{2}}, + Storage::ExportedType{qmlModuleId, "Obj", Storage::Version{2}}, + Storage::ExportedType{qmlNativeModuleId, "QObject"}}}); + + package.updatedSourceIds = {sourceId1, sourceId2}; + + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); + package.updatedModuleDependencySourceIds.push_back(sourceId3); + + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId4); + package.updatedModuleDependencySourceIds.push_back(sourceId4); + + importsSourceId3.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + importsSourceId3.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); + moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); + + importsSourceId4.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + importsSourceId4.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); + moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); + moduleDependenciesSourceId4.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId4); + + package.types[1].propertyDeclarations.push_back( + Storage::PropertyDeclaration{"objects", + Storage::ImportedType{"QObject"}, + Storage::PropertyDeclarationTraits::IsList}); + package.types.push_back( + Storage::Type{"QAliasItem", + Storage::ImportedType{"Item"}, + TypeAccessSemantics::Reference, + sourceId3, + {Storage::ExportedType{qtQuickModuleId, "AliasItem"}, + Storage::ExportedType{qtQuickNativeModuleId, "QAliasItem"}}}); + package.types.back().propertyDeclarations.push_back( + Storage::PropertyDeclaration{"items", Storage::ImportedType{"Item"}, "children", "items"}); + package.types.back().propertyDeclarations.push_back(Storage::PropertyDeclaration{ + "objects", Storage::ImportedType{"Item"}, "children", "objects"}); + + package.types.push_back(Storage::Type{"QObject2", + Storage::ImportedType{}, + TypeAccessSemantics::Reference, + sourceId4, + {Storage::ExportedType{pathToModuleId, "Object2"}, + Storage::ExportedType{pathToModuleId, "Obj2"}}}); + package.types[3].propertyDeclarations.push_back( + Storage::PropertyDeclaration{"children", + Storage::ImportedType{"QChildren2"}, + Storage::PropertyDeclarationTraits::IsReadOnly}); + + package.updatedSourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId4); + + package.types.push_back(Storage::Type{ + "QChildren", + Storage::ImportedType{}, + TypeAccessSemantics::Reference, + sourceId5, + {Storage::ExportedType{qtQuickModuleId, "Children", Storage::Version{2}}, + Storage::ExportedType{qtQuickNativeModuleId, "QChildren"}}, + {Storage::PropertyDeclaration{"items", + Storage::ImportedType{"QQuickItem"}, + Storage::PropertyDeclarationTraits::IsList}, + Storage::PropertyDeclaration{"objects", + Storage::ImportedType{"QObject"}, + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly}}}); + + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId5); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId5); + importsSourceId5.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); + importsSourceId5.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); + moduleDependenciesSourceId5.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId5); + moduleDependenciesSourceId5.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId5); + package.updatedModuleDependencySourceIds.push_back(sourceId5); + package.updatedSourceIds.push_back(sourceId5); + + package.types.push_back(Storage::Type{ + "QChildren2", + Storage::ImportedType{}, + TypeAccessSemantics::Reference, + sourceId6, + {Storage::ExportedType{qtQuickModuleId, "Children2", Storage::Version{2}}, + Storage::ExportedType{qtQuickNativeModuleId, "QChildren2"}}, + {Storage::PropertyDeclaration{"items", + Storage::ImportedType{"QQuickItem"}, + Storage::PropertyDeclarationTraits::IsList}, + Storage::PropertyDeclaration{"objects", + Storage::ImportedType{"Object2"}, + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly}}}); + + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId6); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId6); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId6); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId6); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId6); + package.updatedModuleDependencySourceIds.push_back(sourceId6); + package.updatedSourceIds.push_back(sourceId6); + + return package; + } + auto createSynchronizationPackageWithAliases2() { auto package{createSynchronizationPackageWithAliases()}; @@ -485,11 +639,13 @@ protected: QmlDesigner::SourcePathView path3{"/path3/to"}; QmlDesigner::SourcePathView path4{"/path4/to"}; QmlDesigner::SourcePathView path5{"/path5/to"}; + QmlDesigner::SourcePathView path6{"/path6/to"}; SourceId sourceId1{sourcePathCache.sourceId(path1)}; SourceId sourceId2{sourcePathCache.sourceId(path2)}; SourceId sourceId3{sourcePathCache.sourceId(path3)}; SourceId sourceId4{sourcePathCache.sourceId(path4)}; SourceId sourceId5{sourcePathCache.sourceId(path5)}; + SourceId sourceId6{sourcePathCache.sourceId(path6)}; SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")}; SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")}; ModuleId qmlModuleId{storage.moduleId("Qml")}; @@ -503,10 +659,12 @@ protected: Storage::Imports importsSourceId2; Storage::Imports importsSourceId3; Storage::Imports importsSourceId4; + Storage::Imports importsSourceId5; Storage::Imports moduleDependenciesSourceId1; Storage::Imports moduleDependenciesSourceId2; Storage::Imports moduleDependenciesSourceId3; Storage::Imports moduleDependenciesSourceId4; + Storage::Imports moduleDependenciesSourceId5; }; TEST_F(ProjectStorage, FetchSourceContextIdReturnsAlwaysTheSameIdForTheSamePath) @@ -4309,4 +4467,422 @@ TEST_F(ProjectStorage, ModuleExportedImportWithQualifiedImportedType) UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } +TEST_F(ProjectStorage, SynchronizeTypesAddIndirectAliasDeclarations) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesAddIndirectAliasDeclarationsAgain) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesRemoveIndirectAliasDeclaration) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations.pop_back(); + + storage.synchronize(SynchronizationPackage{importsSourceId3, {package.types[2]}, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre(IsPropertyDeclaration( + "items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesAddIndirectAliasDeclarationsThrowsForWrongTypeName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].typeName = Storage::ImportedType{"QQuickItemWrong"}; + + ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, + {package.types[2]}, + {sourceId3}, + moduleDependenciesSourceId3, + {sourceId3}}), + QmlDesigner::TypeNameDoesNotExists); +} + +TEST_F(ProjectStorage, SynchronizeTypesAddIndirectAliasDeclarationsThrowsForWrongPropertyName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong"; + + ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, + {package.types[2]}, + {sourceId3}, + moduleDependenciesSourceId3, + {sourceId3}}), + QmlDesigner::PropertyNameDoesNotExists); +} + +TEST_F(ProjectStorage, SynchronizeTypesAddIndirectAliasDeclarationsThrowsForWrongPropertyNameTail) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objectsWrong"; + + ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, + {package.types[2]}, + {sourceId3}, + moduleDependenciesSourceId3, + {sourceId3}}), + QmlDesigner::PropertyNameDoesNotExists); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationTypeName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].typeName = Storage::ImportedType{"Obj2"}; + importsSourceId3.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationTailsTypeName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[4].propertyDeclarations[1].typeName = Storage::ImportedType{"Obj2"}; + importsSourceId5.emplace_back(pathToModuleId, Storage::Version{}, sourceId5); + + storage.synchronize(SynchronizationPackage{ + importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationsPropertyName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].aliasPropertyName = "kids"; + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationsPropertyNameTail) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "items"; + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf( + IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationsToPropertyDeclaration) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations.pop_back(); + package.types[2].propertyDeclarations.push_back( + Storage::PropertyDeclaration{"objects", + Storage::ImportedType{"QQuickItem"}, + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly}); + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangePropertyDeclarationsToIndirectAliasDeclaration) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + auto packageChanged = package; + packageChanged.types[2].propertyDeclarations.pop_back(); + packageChanged.types[2].propertyDeclarations.push_back( + Storage::PropertyDeclaration{"objects", + Storage::ImportedType{"QQuickItem"}, + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly}); + storage.synchronize(SynchronizationPackage{importsSourceId3, + {packageChanged.types[2]}, + {sourceId3}, + moduleDependenciesSourceId3, + {sourceId3}}); + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasTargetPropertyDeclarationTraits) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[4].propertyDeclarations[0].traits = Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly; + + storage.synchronize(SynchronizationPackage{ + importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); + + ASSERT_THAT( + storage.fetchTypes(), + Contains(AllOf( + IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasTargetPropertyDeclarationTypeName) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[4].propertyDeclarations[1].typeName = Storage::ImportedType{"Item"}; + + storage.synchronize(SynchronizationPackage{ + importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesRemovePropertyDeclarationWithAnIndirectAliasThrows) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[4].propertyDeclarations.pop_back(); + + ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId5, + {package.types[4]}, + {sourceId5}, + moduleDependenciesSourceId5, + {sourceId5}}), + Sqlite::ConstraintPreventsModification); +} + +TEST_F(ProjectStorage, SynchronizeTypesRemoveStemPropertyDeclarationWithAnIndirectAliasThrows) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[0].propertyDeclarations.erase(package.types[0].propertyDeclarations.begin()); + + ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId1, + {package.types[0]}, + {sourceId1}, + moduleDependenciesSourceId1, + {sourceId1}}), + Sqlite::ConstraintPreventsModification); +} + +TEST_F(ProjectStorage, SynchronizeTypesRemovePropertyDeclarationAndIndirectAlias) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations.pop_back(); + package.types[4].propertyDeclarations.pop_back(); + + storage.synchronize(SynchronizationPackage{importsSourceId3 + importsSourceId5, + {package.types[2], package.types[4]}, + {sourceId3, sourceId5}, + moduleDependenciesSourceId3 + moduleDependenciesSourceId5, + {sourceId3, sourceId5}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, + UnorderedElementsAre(IsPropertyDeclaration( + "items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList)))))); +} + +TEST_F(ProjectStorage, SynchronizeTypesRemovePropertyDeclarationAndIndirectAliasSteam) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[0].propertyDeclarations.clear(); + package.types[2].propertyDeclarations.clear(); + + storage.synchronize(SynchronizationPackage{importsSourceId1 + importsSourceId3, + {package.types[0], package.types[2]}, + {sourceId1, sourceId3}, + moduleDependenciesSourceId1 + moduleDependenciesSourceId3, + {sourceId1, sourceId3}}); + + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeAccessSemantics::Reference), + Field(&Storage::Type::propertyDeclarations, IsEmpty())))); +} } // namespace diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp index 9169d6b29a..89b50ebe1f 100644 --- a/tests/unit/unittest/qmltypesparser-test.cpp +++ b/tests/unit/unittest/qmltypesparser-test.cpp @@ -366,6 +366,22 @@ TEST_F(QmlTypesParser, Functions) Field(&Storage::FunctionDeclaration::parameters, IsEmpty())))))); } +TEST_F(QmlTypesParser, SkipJavaScriptFunctions) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + Method { + name: "do" + isJavaScriptFunction: true + } + }})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(Field(&Storage::Type::functionDeclarations, IsEmpty()))); +} + TEST_F(QmlTypesParser, FunctionsWithQualifiedTypes) { QString source{R"(import QtQuick.tooling 1.2 -- cgit v1.2.3