diff options
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<Utils::SmallString>( aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId != -1) + aliasPropertyNameTail = selectPropertyNameStatement.template value<Utils::SmallString>( + 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<Storage::ExportedTypeView>( @@ -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<PropertyDeclarationTraits>(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 |