From 4046f4717477ba6429bc39d21b59ca5b20833c11 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 29 May 2024 14:37:07 +0200 Subject: [PATCH] QmlDesigner: Use error notifier for property type name resolving We can polish it later. But so far the architecture works and the problematics cases of missing types is handled. Task-number: QDS-12761 Change-Id: Ifb55a5772fddfa7719a4d690b24b0539679f79ec Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorage.cpp | 436 +++++++--- .../projectstorage/projectstorage.h | 83 +- .../projectstorage/projectstoragetypes.h | 24 +- .../tests/printers/gtest-creator-printing.cpp | 3 +- .../projectstorage/projectstorage-test.cpp | 765 +++++++++++++++--- 5 files changed, 1048 insertions(+), 263 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 2283b64945a..e0aac2defbf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -64,8 +64,11 @@ struct ProjectStorage::Statements "ORDER BY minorVersion DESC " "LIMIT 1", database}; - mutable Sqlite::ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ - "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " + mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ + "SELECT propertyImportedTypeNameId, " + " propertyTypeId, " + " propertyDeclarationId, " + " propertyTraits " "FROM propertyDeclarations " "WHERE propertyDeclarationId=?1 " "LIMIT 1", @@ -141,25 +144,63 @@ struct ProjectStorage::Statements Sqlite::WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{ "DELETE FROM signalDeclarations WHERE typeId=?", database}; Sqlite::WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; - mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{ - "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM " - "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " - "propertyDeclarations AS pd WHERE typeId=?", + mutable Sqlite::ReadStatement<6, 1> selectPropertyDeclarationsByTypeIdStatement{ + "SELECT " + " propertyDeclarationId, " + " name, " + " propertyTypeId, " + " propertyTraits, " + " (SELECT name " + " FROM propertyDeclarations " + " WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId), " + " typeId " + "FROM propertyDeclarations AS pd " + "WHERE typeId=?", database}; Sqlite::ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{ - "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " - "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? ORDER BY name", + "SELECT " + " name, " + " propertyTraits, " + " propertyTypeId, " + " propertyImportedTypeNameId, " + " propertyDeclarationId, " + " aliasPropertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=? " + "ORDER BY name", database}; Sqlite::ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, " - "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) " + "INSERT INTO propertyDeclarations(" + " typeId, " + " name, " + " propertyTypeId, " + " propertyTraits, " + " propertyImportedTypeNameId, " + " aliasPropertyDeclarationId) " + "VALUES(?1, ?2, ?3, ?4, ?5, NULL) " "RETURNING propertyDeclarationId", database}; Sqlite::WriteStatement<4> updatePropertyDeclarationStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE " - "propertyDeclarationId=?1", + "UPDATE propertyDeclarations " + "SET " + " propertyTypeId=?2, " + " propertyTraits=?3, " + " propertyImportedTypeNameId=?4, " + " aliasPropertyImportedTypeNameId=NULL, " + " aliasPropertyDeclarationName=NULL, " + " aliasPropertyDeclarationTailName=NULL, " + " aliasPropertyDeclarationId=NULL, " + " aliasPropertyDeclarationTailId=NULL " + "WHERE propertyDeclarationId=?1", + database}; + Sqlite::WriteStatement<2> resetAliasPropertyDeclarationStatement{ + "UPDATE propertyDeclarations " + "SET propertyTypeId=NULL, " + " propertyTraits=?2, " + " propertyImportedTypeNameId=NULL, " + " aliasPropertyDeclarationId=NULL, " + " aliasPropertyDeclarationTailId=NULL " + "WHERE propertyDeclarationId=?1", database}; Sqlite::WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{ "WITH RECURSIVE " @@ -193,17 +234,30 @@ struct ProjectStorage::Statements Sqlite::WriteStatement<1> deletePropertyDeclarationStatement{ "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; Sqlite::ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{ - "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name", + "SELECT name, " + " propertyDeclarationId, " + " aliasPropertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL " + "ORDER BY name", database}; Sqlite::WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE " - "propertyDeclarationId=?1", + "UPDATE propertyDeclarations " + "SET propertyTypeId=?2, " + " propertyTraits=?3, " + " propertyImportedTypeNameId=?4, " + " aliasPropertyDeclarationId=?5 " + "WHERE propertyDeclarationId=?1", database}; - Sqlite::ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING " - "propertyDeclarationId", + Sqlite::ReadWriteStatement<1, 5> insertAliasPropertyDeclarationStatement{ + "INSERT INTO propertyDeclarations(" + " typeId, " + " name, " + " aliasPropertyImportedTypeNameId, " + " aliasPropertyDeclarationName, " + " aliasPropertyDeclarationTailName) " + "VALUES(?1, ?2, ?3, ?4, ?5) " + "RETURNING propertyDeclarationId", database}; mutable Sqlite::ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{ "SELECT name, returnTypeName, signature, functionDeclarationId FROM " @@ -334,26 +388,26 @@ struct ProjectStorage::Statements "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", database}; Sqlite::ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.aliasPropertyImportedTypeNameId, " " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " "WHERE alias.propertyTypeId=?1 " "UNION ALL " - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.aliasPropertyImportedTypeNameId, " " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " "WHERE target.typeId=?1 " "UNION ALL " - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.aliasPropertyImportedTypeNameId, " " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " - "WHERE alias.propertyImportedTypeNameId IN " + "WHERE alias.aliasPropertyImportedTypeNameId IN " " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " " WHERE typeId=?1)", database}; @@ -376,6 +430,30 @@ struct ProjectStorage::Statements "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, " "propertyImportedTypeNameId", database}; + Sqlite::ReadWriteStatement<3, 2> selectPropertyDeclarationForPrototypeIdAndTypeNameStatement{ + "SELECT typeId, propertyDeclarationId, propertyImportedTypeNameId " + "FROM propertyDeclarations " + "WHERE propertyTypeId IS ?2 " + " AND propertyImportedTypeNameId IN (SELECT importedTypeNameId " + " FROM " + " importedTypeNames WHERE name=?1)", + database}; + Sqlite::ReadWriteStatement<5, 2> selectAliasPropertyDeclarationForPrototypeIdAndTypeNameStatement{ + "SELECT alias.typeId, " + " alias.propertyDeclarationId, " + " alias.aliasPropertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, " + " alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias " + " JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId " + " OR alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyTypeId IS ?2 " + " AND target.propertyImportedTypeNameId IN " + " (SELECT importedTypeNameId " + " FROM importedTypeNames " + " WHERE name=?1)", + database}; mutable Sqlite::ReadStatement<1, 1> selectPropertyNameStatement{ "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ @@ -418,7 +496,7 @@ struct ProjectStorage::Statements "RETURNING typeId, prototypeNameId, extensionNameId", database}; Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{ - "SELECT typeId , prototypeNameId " + "SELECT typeId , extensionNameId " "FROM types " "WHERE extensionNameId IN ( " " SELECT importedTypeNameId " @@ -443,11 +521,12 @@ struct ProjectStorage::Statements "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL", database}; Sqlite::WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " - "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND " - "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)", + "UPDATE propertyDeclarations " + "SET aliasPropertyDeclarationId=?2, " + " propertyImportedTypeNameId=?3 " + "WHERE propertyDeclarationId=?1", database}; - Sqlite::WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{ + Sqlite::WriteStatement<1> updatePropertiesDeclarationValuesOfAliasStatement{ "WITH RECURSIVE " " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( " " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM " @@ -794,6 +873,20 @@ struct ProjectStorage::Statements " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" "SELECT typeId FROM typeSelection", database}; + mutable Sqlite::ReadStatement<6, 0> selectBrokenAliasPropertyDeclarationsStatement{ + "SELECT typeId, " + " propertyDeclarationId, " + " aliasPropertyImportedTypeNameId, " + " aliasPropertyDeclarationName, " + " aliasPropertyDeclarationTailName, " + " sourceId " + "FROM propertyDeclarations JOIN types USING(typeId) " + "WHERE " + " aliasPropertyImportedTypeNameId IS NOT NULL " + " AND " + " propertyImportedTypeNameId IS NULL " + "LIMIT 1", + database}; }; class ProjectStorage::Initializer @@ -893,14 +986,17 @@ public: {Sqlite::PrimaryKey{}}); auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); auto &nameColumn = propertyDeclarationTable.addColumn("name"); - auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "propertyTypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); + auto &propertyTypeIdColumn = propertyDeclarationTable.addColumn( + "propertyTypeId", Sqlite::StrictColumnType::Integer); propertyDeclarationTable.addColumn("propertyTraits", Sqlite::StrictColumnType::Integer); - propertyDeclarationTable.addColumn("propertyImportedTypeNameId", - Sqlite::StrictColumnType::Integer); + auto &propertyImportedTypeNameIdColumn = propertyDeclarationTable.addColumn( + "propertyImportedTypeNameId", Sqlite::StrictColumnType::Integer); + auto &aliasPropertyImportedTypeNameIdColumn = propertyDeclarationTable.addColumn( + "aliasPropertyImportedTypeNameId", Sqlite::StrictColumnType::Integer); + propertyDeclarationTable.addColumn("aliasPropertyDeclarationName", + Sqlite::StrictColumnType::Text); + propertyDeclarationTable.addColumn("aliasPropertyDeclarationTailName", + Sqlite::StrictColumnType::Text); auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn( "aliasPropertyDeclarationId", propertyDeclarationTable, @@ -913,7 +1009,9 @@ public: Sqlite::ForeignKeyAction::Restrict); propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); - propertyDeclarationTable.addIndex({propertyTypeIdColumn}); + propertyDeclarationTable.addIndex({propertyTypeIdColumn, propertyImportedTypeNameIdColumn}); + propertyDeclarationTable.addIndex( + {aliasPropertyImportedTypeNameIdColumn, propertyImportedTypeNameIdColumn}); propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, "aliasPropertyDeclarationId IS NOT NULL"); propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, @@ -1217,8 +1315,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag TypeIds deletedTypeIds; Sqlite::withImmediateTransaction(database, [&] { - AliasPropertyDeclarations insertedAliasPropertyDeclarations; - AliasPropertyDeclarations updatedAliasPropertyDeclarations; + AliasPropertyDeclarations aliasPropertyDeclarationsToLink; AliasPropertyDeclarations relinkableAliasPropertyDeclarations; PropertyDeclarations relinkablePropertyDeclarations; @@ -1243,8 +1340,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag relinkableExtensions); synchronizeTypes(package.types, updatedTypeIds, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, + aliasPropertyDeclarationsToLink, relinkableAliasPropertyDeclarations, relinkablePropertyDeclarations, relinkablePrototypes, @@ -1269,7 +1365,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag relinkableExtensions, deletedTypeIds); - linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); + repairBrokenAliasPropertyDeclarations(); + + linkAliases(aliasPropertyDeclarationsToLink, RaiseError::Yes); synchronizeDirectoryInfos(package.directoryInfos, package.updatedDirectoryInfoSourceIds); @@ -2001,6 +2099,7 @@ Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) type.functionDeclarations = fetchFunctionDeclarations(type.typeId); type.signalDeclarations = fetchSignalDeclarations(type.typeId); type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + type.typeId = typeId; return type; }); @@ -2530,8 +2629,7 @@ void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type & void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, @@ -2584,10 +2682,7 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); resetDefaultPropertiesIfChanged(types); resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); - syncDeclarations(types, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - relinkablePropertyDeclarations); + syncDeclarations(types, aliasPropertyDeclarationsToLink, relinkablePropertyDeclarations); syncDefaultProperties(types); } @@ -2869,7 +2964,7 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( auto callback = [&](TypeId typeId_, PropertyDeclarationId propertyDeclarationId, - ImportedTypeNameId propertyImportedTypeNameId, + ImportedTypeNameId aliasPropertyImportedTypeNameId, PropertyDeclarationId aliasPropertyDeclarationId, PropertyDeclarationId aliasPropertyDeclarationTailId) { auto aliasPropertyName = s->selectPropertyNameStatement.value( @@ -2880,10 +2975,11 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( aliasPropertyDeclarationTailId); relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, - PropertyDeclarationId{propertyDeclarationId}, - ImportedTypeNameId{propertyImportedTypeNameId}, + propertyDeclarationId, + aliasPropertyImportedTypeNameId, std::move(aliasPropertyName), - std::move(aliasPropertyNameTail)); + std::move(aliasPropertyNameTail), + fetchTypeSourceId(typeId_)); s->updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); }; @@ -2905,6 +3001,62 @@ void ProjectStorage::handlePropertyDeclarationWithPropertyType( typeId); } +void ProjectStorage::handlePropertyDeclarationsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, + TypeId typeId, + PropertyDeclarations &relinkablePropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle property declarations with exported type name and type id"_t, + projectStorageCategory(), + keyValue("type name", exportedTypeName), + keyValue("type id", typeId), + keyValue("relinkable property declarations", + relinkablePropertyDeclarations)}; + + s->selectPropertyDeclarationForPrototypeIdAndTypeNameStatement.readTo(relinkablePropertyDeclarations, + exportedTypeName, + typeId); +} + +void ProjectStorage::handleAliasPropertyDeclarationsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, + TypeId typeId, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle alias property declarations with exported type name and type id"_t, + projectStorageCategory(), + keyValue("type name", exportedTypeName), + keyValue("type id", typeId), + keyValue("relinkable alias property declarations", + relinkableAliasPropertyDeclarations)}; + + auto callback = [&](TypeId typeId_, + PropertyDeclarationId propertyDeclarationId, + ImportedTypeNameId aliasPropertyImportedTypeNameId, + PropertyDeclarationId aliasPropertyDeclarationId, + PropertyDeclarationId aliasPropertyDeclarationTailId) { + auto aliasPropertyName = s->selectPropertyNameStatement.value( + aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId) + aliasPropertyNameTail = s->selectPropertyNameStatement.value( + aliasPropertyDeclarationTailId); + + relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, + propertyDeclarationId, + aliasPropertyImportedTypeNameId, + std::move(aliasPropertyName), + std::move(aliasPropertyNameTail), + fetchTypeSourceId(typeId_)); + }; + + s->selectAliasPropertyDeclarationForPrototypeIdAndTypeNameStatement.readCallback(callback, + exportedTypeName, + typeId); +} + void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) { using NanotraceHR::keyValue; @@ -3002,6 +3154,7 @@ void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations & keyValue("deleted type ids", deletedTypeIds)}; std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); + // todo remove duplicates Utils::set_greedy_difference( aliasPropertyDeclarations.cbegin(), @@ -3011,17 +3164,22 @@ void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations & [&](const AliasPropertyDeclaration &alias) { auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; + if (typeId) { + auto [propertyImportedTypeNameId, propertyTypeId, aliasId, propertyTraits] + = fetchPropertyDeclarationByTypeIdAndNameUngarded(typeId, alias.aliasPropertyName); - auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( - typeId, alias.aliasPropertyName); - - s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, - propertyTypeId, - propertyTraits, - alias.aliasImportedTypeNameId, - aliasId); + s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, + propertyTypeId, + propertyTraits, + propertyImportedTypeNameId, + aliasId); + } else { + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName( + alias.aliasImportedTypeNameId), + fetchTypeSourceId(alias.typeId)); + s->resetAliasPropertyDeclarationStatement.write(alias.propertyDeclarationId, + Storage::PropertyDeclarationTraits{}); + } }, TypeCompare{}); } @@ -3037,6 +3195,9 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable keyValue("deleted type ids", deletedTypeIds)}; std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); + relinkablePropertyDeclaration.erase(std::unique(relinkablePropertyDeclaration.begin(), + relinkablePropertyDeclaration.end()), + relinkablePropertyDeclaration.end()); Utils::set_greedy_difference( relinkablePropertyDeclaration.cbegin(), @@ -3046,8 +3207,12 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable [&](const PropertyDeclaration &property) { TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; + if (!propertyTypeId) { + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName( + property.importedTypeNameId), + fetchTypeSourceId(property.typeId)); + propertyTypeId = TypeId{}; + } s->updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, propertyTypeId); @@ -3160,7 +3325,8 @@ PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId, aliasPropertyNameTail); } -void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) +void ProjectStorage::linkAliasPropertyDeclarationAliasIds( + const AliasPropertyDeclarations &aliasDeclarations, RaiseError raiseError) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, @@ -3170,17 +3336,22 @@ void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDec for (const auto &aliasDeclaration : aliasDeclarations) { auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); - if (!aliasTypeId) { - throw TypeNameDoesNotExists{ - fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; + if (aliasTypeId) { + auto aliasId = fetchAliasId(aliasTypeId, + aliasDeclaration.aliasPropertyName, + aliasDeclaration.aliasPropertyNameTail); + + s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement + .write(aliasDeclaration.propertyDeclarationId, + aliasId, + aliasDeclaration.aliasImportedTypeNameId); + } else if (raiseError == RaiseError::Yes) { + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName( + aliasDeclaration.aliasImportedTypeNameId), + aliasDeclaration.sourceId); + s->resetAliasPropertyDeclarationStatement.write(aliasDeclaration.propertyDeclarationId, + Storage::PropertyDeclarationTraits{}); } - - auto aliasId = fetchAliasId(aliasTypeId, - aliasDeclaration.aliasPropertyName, - aliasDeclaration.aliasPropertyNameTail); - - s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write( - aliasDeclaration.propertyDeclarationId, aliasId, aliasDeclaration.aliasImportedTypeNameId); } } @@ -3192,7 +3363,7 @@ void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDec keyValue("alias property declarations", aliasDeclarations)}; for (const auto &aliasDeclaration : aliasDeclarations) { - s->updatetPropertiesDeclarationValuesOfAliasStatement.write( + s->updatePropertiesDeclarationValuesOfAliasStatement.write( aliasDeclaration.propertyDeclarationId); s->updatePropertyAliasDeclarationRecursivelyStatement.write( aliasDeclaration.propertyDeclarationId); @@ -3209,20 +3380,29 @@ void ProjectStorage::checkAliasPropertyDeclarationCycles(const AliasPropertyDecl checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); } -void ProjectStorage::linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) +void ProjectStorage::linkAliases(const AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, + RaiseError raiseError) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; - linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); - linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); + linkAliasPropertyDeclarationAliasIds(aliasPropertyDeclarationsToLink, raiseError); - checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations); - checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations); + checkAliasPropertyDeclarationCycles(aliasPropertyDeclarationsToLink); - updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations); - updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); + updateAliasPropertyDeclarationValues(aliasPropertyDeclarationsToLink); +} + +void ProjectStorage::repairBrokenAliasPropertyDeclarations() +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"repair broken alias property declarations"_t, + projectStorageCategory()}; + + auto brokenAliasPropertyDeclarations = s->selectBrokenAliasPropertyDeclarationsStatement + .values(); + + linkAliases(brokenAliasPropertyDeclarations, RaiseError::No); } void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, @@ -3303,6 +3483,12 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; } + handlePropertyDeclarationsWithExportedTypeNameAndTypeId(type.name, + TypeId{}, + relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithExportedTypeNameAndTypeId(type.name, + TypeId{}, + relinkableAliasPropertyDeclarations); handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes); handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions); }; @@ -3348,7 +3534,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, } void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) @@ -3358,17 +3544,24 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( projectStorageCategory(), keyValue("property declaration", value)}; + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); + auto callback = [&](PropertyDeclarationId propertyDeclarationId) { - insertedAliasPropertyDeclarations.emplace_back(typeId, - propertyDeclarationId, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail); + aliasPropertyDeclarationsToLink.emplace_back(typeId, + propertyDeclarationId, + propertyImportedTypeNameId, + value.aliasPropertyName, + value.aliasPropertyNameTail, + sourceId); return Sqlite::CallbackControl::Abort; }; - s->insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); + s->insertAliasPropertyDeclarationStatement.readCallback(callback, + typeId, + value.name, + propertyImportedTypeNameId, + value.aliasPropertyName, + value.aliasPropertyNameTail); } QVarLengthArray ProjectStorage::fetchPropertyDeclarationIds( @@ -3452,8 +3645,12 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + if (!propertyTypeId) { + auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; }, + value.typeName); + errorNotifier->typeNameCannotBeResolved(typeName, sourceId); + propertyTypeId = TypeId{}; + } auto propertyDeclarationId = s->insertPropertyDeclarationStatement.value( typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); @@ -3468,7 +3665,7 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( } void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias( - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId) @@ -3479,12 +3676,13 @@ void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias( keyValue("property declaration", value), keyValue("property declaration view", view)}; - updatedAliasPropertyDeclarations.emplace_back(view.typeId, - view.id, - fetchImportedTypeNameId(value.typeName, sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail, - view.aliasId); + aliasPropertyDeclarationsToLink.emplace_back(view.propertyTypeId, + view.id, + fetchImportedTypeNameId(value.typeName, sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail, + sourceId, + view.aliasId); } Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProperty( @@ -3503,10 +3701,15 @@ Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProper auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + if (!propertyTypeId) { + auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; }, + value.typeName); + errorNotifier->typeNameCannotBeResolved(typeName, sourceId); + propertyTypeId = TypeId{}; + propertyDeclarationIds.push_back(view.id); + } - if (view.traits == value.traits && propertyTypeId == view.typeId + if (view.traits == value.traits && compareId(propertyTypeId, view.propertyTypeId) && propertyImportedTypeNameId == view.typeNameId) return Sqlite::UpdateChange::No; @@ -3528,8 +3731,7 @@ void ProjectStorage::synchronizePropertyDeclarations( TypeId typeId, Storage::Synchronization::PropertyDeclarations &propertyDeclarations, SourceId sourceId, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarationIds &propertyDeclarationIds) { NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; @@ -3548,7 +3750,7 @@ void ProjectStorage::synchronizePropertyDeclarations( auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) { if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations, + synchronizePropertyDeclarationsInsertAlias(aliasPropertyDeclarationsToLink, value, sourceId, typeId); @@ -3560,7 +3762,7 @@ void ProjectStorage::synchronizePropertyDeclarations( auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value) { if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations, + synchronizePropertyDeclarationsUpdateAlias(aliasPropertyDeclarationsToLink, view, value, sourceId); @@ -4254,8 +4456,7 @@ TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type) } void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarationIds &propertyDeclarationIds) { NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; @@ -4266,8 +4467,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, synchronizePropertyDeclarations(type.typeId, type.propertyDeclarations, type.sourceId, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, + aliasPropertyDeclarationsToLink, propertyDeclarationIds); synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); @@ -4275,8 +4475,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, } void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarations &relinkablePropertyDeclarations) { NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; @@ -4285,10 +4484,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, propertyDeclarationIds.reserve(types.size() * 10); for (auto &&type : types) - syncDeclarations(type, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - propertyDeclarationIds); + syncDeclarations(type, aliasPropertyDeclarationsToLink, propertyDeclarationIds); removeRelinkableEntries(relinkablePropertyDeclarations, propertyDeclarationIds, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 54d91015968..c4ea3acef95 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -40,6 +40,7 @@ class ProjectStorage final : public ProjectStorageInterface friend Storage::Info::CommonTypeCache; enum class Relink { No, Yes }; + enum class RaiseError { No, Yes }; public: ProjectStorage(Database &database, @@ -352,6 +353,7 @@ private: ImportedTypeNameId aliasImportedTypeNameId, Utils::SmallString aliasPropertyName, Utils::SmallString aliasPropertyNameTail, + SourceId sourceId, PropertyDeclarationId aliasPropertyDeclarationId = PropertyDeclarationId{}) : typeId{typeId} , propertyDeclarationId{propertyDeclarationId} @@ -359,6 +361,21 @@ private: , aliasPropertyName{std::move(aliasPropertyName)} , aliasPropertyNameTail{std::move(aliasPropertyNameTail)} , aliasPropertyDeclarationId{aliasPropertyDeclarationId} + , sourceId{sourceId} + {} + + AliasPropertyDeclaration(TypeId typeId, + PropertyDeclarationId propertyDeclarationId, + ImportedTypeNameId aliasImportedTypeNameId, + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail, + SourceId sourceId) + : typeId{typeId} + , propertyDeclarationId{propertyDeclarationId} + , aliasImportedTypeNameId{aliasImportedTypeNameId} + , aliasPropertyName{aliasPropertyName} + , aliasPropertyNameTail{aliasPropertyNameTail} + , sourceId{sourceId} {} friend bool operator<(const AliasPropertyDeclaration &first, @@ -368,6 +385,13 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + friend bool operator==(const AliasPropertyDeclaration &first, + const AliasPropertyDeclaration &second) + { + return std::tie(first.typeId, first.propertyDeclarationId) + == std::tie(second.typeId, second.propertyDeclarationId); + } + template friend void convertToString(String &string, const AliasPropertyDeclaration &aliasPropertyDeclaration) @@ -394,6 +418,7 @@ private: Utils::SmallString aliasPropertyName; Utils::SmallString aliasPropertyNameTail; PropertyDeclarationId aliasPropertyDeclarationId; + SourceId sourceId; }; using AliasPropertyDeclarations = std::vector; @@ -409,6 +434,12 @@ private: , importedTypeNameId{std::move(importedTypeNameId)} {} + friend bool operator==(const PropertyDeclaration &first, const PropertyDeclaration &second) + { + return std::tie(first.typeId, first.propertyDeclarationId) + == std::tie(second.typeId, second.propertyDeclarationId); + } + friend bool operator<(const PropertyDeclaration &first, const PropertyDeclaration &second) { return std::tie(first.typeId, first.propertyDeclarationId) @@ -569,8 +600,7 @@ private: void synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, @@ -605,7 +635,14 @@ private: void handlePropertyDeclarationWithPropertyType(TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations); - + void handlePropertyDeclarationsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, + TypeId typeId, + PropertyDeclarations &relinkablePropertyDeclarations); + void handleAliasPropertyDeclarationsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, + TypeId typeId, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, TypeId typeId, @@ -651,14 +688,17 @@ private: Utils::SmallStringView aliasPropertyName, Utils::SmallStringView aliasPropertyNameTail); - void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations); + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations, + RaiseError raiseError); void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations); void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations); - void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - const AliasPropertyDeclarations &updatedAliasPropertyDeclarations); + void linkAliases(const AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, + RaiseError raiseError); + + void repairBrokenAliasPropertyDeclarations(); void synchronizeExportedTypes(const TypeIds &updatedTypeIds, Storage::Synchronization::ExportedTypes &exportedTypes, @@ -689,7 +729,7 @@ private: const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId); void synchronizePropertyDeclarationsUpdateAlias( - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId); @@ -700,13 +740,11 @@ private: SourceId sourceId, PropertyDeclarationIds &propertyDeclarationIds); - void synchronizePropertyDeclarations( - TypeId typeId, - Storage::Synchronization::PropertyDeclarations &propertyDeclarations, - SourceId sourceId, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarationIds &propertyDeclarationIds); + void synchronizePropertyDeclarations(TypeId typeId, + Storage::Synchronization::PropertyDeclarations &propertyDeclarations, + SourceId sourceId, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, + PropertyDeclarationIds &propertyDeclarationIds); class AliasPropertyDeclarationView { @@ -832,8 +870,7 @@ private: TypeId declareType(Storage::Synchronization::Type &type); void syncDeclarations(Storage::Synchronization::Type &type, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarationIds &propertyDeclarationIds); template @@ -859,8 +896,7 @@ private: } void syncDeclarations(Storage::Synchronization::Types &types, - AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarations &relinkablePropertyDeclarations); class TypeWithDefaultPropertyView @@ -924,10 +960,12 @@ private: class FetchPropertyDeclarationResult { public: - FetchPropertyDeclarationResult(TypeId propertyTypeId, + FetchPropertyDeclarationResult(ImportedTypeNameId propertyImportedTypeNameId, + TypeId propertyTypeId, PropertyDeclarationId propertyDeclarationId, Storage::PropertyDeclarationTraits propertyTraits) - : propertyTypeId{propertyTypeId} + : propertyImportedTypeNameId{propertyImportedTypeNameId} + , propertyTypeId{propertyTypeId} , propertyDeclarationId{propertyDeclarationId} , propertyTraits{propertyTraits} {} @@ -937,7 +975,9 @@ private: { using NanotraceHR::dictonary; using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("property type id", result.propertyTypeId), + auto dict = dictonary(keyValue("property imported type name id", + result.propertyImportedTypeNameId), + keyValue("property type id", result.propertyTypeId), keyValue("property declaration id", result.propertyDeclarationId), keyValue("property traits", result.propertyTraits)); @@ -945,6 +985,7 @@ private: } public: + ImportedTypeNameId propertyImportedTypeNameId; TypeId propertyTypeId; PropertyDeclarationId propertyDeclarationId; Storage::PropertyDeclarationTraits propertyTraits; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 1592628af53..8608f3ad805 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -870,6 +870,21 @@ public: , kind{PropertyKind::Property} {} + explicit PropertyDeclaration(PropertyDeclarationId propertyDeclarationId, + ::Utils::SmallStringView name, + TypeId propertyTypeId, + PropertyDeclarationTraits traits, + ::Utils::SmallStringView aliasPropertyName, + TypeId typeId) + : name{name} + , aliasPropertyName{aliasPropertyName} + , traits{traits} + , propertyTypeId{propertyTypeId} + , typeId{typeId} + , propertyDeclarationId{propertyDeclarationId} + , kind{PropertyKind::Property} + {} + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName aliasTypeName, ::Utils::SmallStringView aliasPropertyName, @@ -916,6 +931,7 @@ public: PropertyDeclarationTraits traits = {}; TypeId propertyTypeId; TypeId typeId; + PropertyDeclarationId propertyDeclarationId; PropertyKind kind = PropertyKind::Property; }; @@ -926,13 +942,13 @@ class PropertyDeclarationView public: explicit PropertyDeclarationView(::Utils::SmallStringView name, PropertyDeclarationTraits traits, - TypeId typeId, + TypeId propertyTypeId, ImportedTypeNameId typeNameId, PropertyDeclarationId id, PropertyDeclarationId aliasId) : name{name} , traits{traits} - , typeId{typeId} + , propertyTypeId{propertyTypeId} , typeNameId{typeNameId} , id{id} , aliasId{aliasId} @@ -945,7 +961,7 @@ public: using NanotraceHR::keyValue; auto dict = dictonary(keyValue("name", propertyDeclaration.name), keyValue("traits", propertyDeclaration.traits), - keyValue("type id", propertyDeclaration.typeId), + keyValue("type id", propertyDeclaration.propertyTypeId), keyValue("type name id", propertyDeclaration.typeNameId), keyValue("id", propertyDeclaration.id), keyValue("alias id", propertyDeclaration.aliasId)); @@ -956,7 +972,7 @@ public: public: ::Utils::SmallStringView name; PropertyDeclarationTraits traits = {}; - TypeId typeId; + TypeId propertyTypeId; ImportedTypeNameId typeNameId; PropertyDeclarationId id; PropertyDeclarationId aliasId; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index d1ad492e00d..0a1f8708032 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -705,8 +705,7 @@ std::ostream &operator<<(std::ostream &out, const PropertyDeclaration &propertyD { using Utils::operator<<; return out << "(\"" << propertyDeclaration.typeId << "\", " << propertyDeclaration.name << ", " - << propertyDeclaration.typeId << ", " << propertyDeclaration.traits << ", " - << propertyDeclaration.propertyTypeId << ")"; + << propertyDeclaration.traits << ", " << propertyDeclaration.propertyTypeId << ")"; } std::ostream &operator<<(std::ostream &out, const Type &type) diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index fc806d73a95..a6ea1a6880c 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -160,49 +160,50 @@ MATCHER_P4(IsExportedType, && type.version == Storage::Version{majorVersion, minorVersion}; } -MATCHER_P3( - IsPropertyDeclaration, - name, - propertyTypeId, - traits, - std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Synchronization::PropertyDeclaration{name, propertyTypeId, traits})) +template +auto IsPropertyDeclaration(Utils::SmallStringView name, + const PropertyTypeIdMatcher &propertyTypeIdMatcher) { - const Storage::Synchronization::PropertyDeclaration &propertyDeclaration = arg; - - return propertyDeclaration.name == name && propertyTypeId == propertyDeclaration.propertyTypeId - && propertyDeclaration.traits == traits; + return AllOf(Field(&Storage::Synchronization::PropertyDeclaration::name, name), + Field(&Storage::Synchronization::PropertyDeclaration::propertyTypeId, + propertyTypeIdMatcher)); } -MATCHER_P4(IsPropertyDeclaration, - name, - propertyTypeId, - traits, - aliasPropertyName, - std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Synchronization::PropertyDeclaration{ - name, propertyTypeId, traits, aliasPropertyName})) +template +auto IsPropertyDeclaration(Utils::SmallStringView name, + const PropertyTypeIdMatcher &propertyTypeIdMatcher, + TraitsMatcher traitsMatcher) { - const Storage::Synchronization::PropertyDeclaration &propertyDeclaration = arg; - - return propertyDeclaration.name == name && propertyTypeId == propertyDeclaration.propertyTypeId - && propertyDeclaration.aliasPropertyName == aliasPropertyName - && propertyDeclaration.traits == traits; + return AllOf(Field(&Storage::Synchronization::PropertyDeclaration::name, name), + Field(&Storage::Synchronization::PropertyDeclaration::propertyTypeId, + propertyTypeIdMatcher), + Field(&Storage::Synchronization::PropertyDeclaration::traits, traitsMatcher)); } -MATCHER_P4(IsInfoPropertyDeclaration, - typeId, - name, - traits, - propertyTypeId, - std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Info::PropertyDeclaration{typeId, name, traits, propertyTypeId})) +template +auto IsPropertyDeclaration(Utils::SmallStringView name, + const PropertyTypeIdMatcher &propertyTypeIdMatcher, + TraitsMatcher traitsMatcher, + Utils::SmallStringView aliasPropertyName) { - const Storage::Info::PropertyDeclaration &propertyDeclaration = arg; + return AllOf(Field(&Storage::Synchronization::PropertyDeclaration::name, name), + Field(&Storage::Synchronization::PropertyDeclaration::propertyTypeId, + propertyTypeIdMatcher), + Field(&Storage::Synchronization::PropertyDeclaration::traits, traitsMatcher), + Field(&Storage::Synchronization::PropertyDeclaration::aliasPropertyName, + aliasPropertyName)); +} - return propertyDeclaration.typeId == typeId && propertyDeclaration.name == name - && propertyDeclaration.propertyTypeId == propertyTypeId - && propertyDeclaration.traits == traits; +template +auto IsInfoPropertyDeclaration(TypeId typeId, + Utils::SmallStringView name, + TraitsMatcher traitsMatcher, + const PropertyTypeIdMatcher &propertyTypeIdMatcher) +{ + return AllOf(Field(&Storage::Info::PropertyDeclaration::typeId, typeId), + Field(&Storage::Info::PropertyDeclaration::name, name), + Field(&Storage::Info::PropertyDeclaration::propertyTypeId, propertyTypeIdMatcher), + Field(&Storage::Info::PropertyDeclaration::traits, traitsMatcher)); } auto IsUnresolvedTypeId() @@ -210,18 +211,35 @@ auto IsUnresolvedTypeId() return Property(&QmlDesigner::TypeId::internalId, -1); } +auto IsNullTypeId() +{ + return Property(&QmlDesigner::TypeId::isNull, true); +} + template -auto IsPrototypeId(const Matcher &matcher) +auto HasPrototypeId(const Matcher &matcher) { return Field(&Storage::Synchronization::Type::prototypeId, matcher); } template -auto IsExtensionId(const Matcher &matcher) +auto HasExtensionId(const Matcher &matcher) { return Field(&Storage::Synchronization::Type::extensionId, matcher); } +template +auto PropertyDeclarations(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::Type::propertyDeclarations, matcher); +} + +template +auto HasPropertyTypeId(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::PropertyDeclaration::propertyTypeId, matcher); +} + class HasNameMatcher { public: @@ -335,6 +353,9 @@ protected: { SynchronizationPackage package; + importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); + package.types.push_back(Storage::Synchronization::Type{ "QQuickItem", Storage::Synchronization::ImportedType{}, @@ -1550,7 +1571,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_ty storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(IsUnresolvedTypeId())); + HasPrototypeId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added) @@ -1563,7 +1584,7 @@ TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_expo storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); + HasPrototypeId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, @@ -1578,7 +1599,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(IsUnresolvedTypeId())); + HasPrototypeId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, @@ -1594,7 +1615,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); + HasPrototypeId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, @@ -1611,7 +1632,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(IsUnresolvedTypeId())); + HasPrototypeId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, @@ -1640,7 +1661,7 @@ TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_expo storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsExtensionId(fetchTypeId(sourceId2, "QObject"))); + HasExtensionId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, @@ -1655,7 +1676,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsExtensionId(IsUnresolvedTypeId())); + HasExtensionId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, @@ -1671,7 +1692,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsExtensionId(fetchTypeId(sourceId2, "QObject"))); + HasExtensionId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, @@ -1688,7 +1709,7 @@ TEST_F(ProjectStorage, storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsExtensionId(IsUnresolvedTypeId())); + HasExtensionId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, @@ -2119,27 +2140,84 @@ TEST_F(ProjectStorage, update_exported_types_if_type_name_changes) "QQuickItem")))))); } -TEST_F(ProjectStorage, breaking_prototype_chain_by_deleting_base_component_throws) +TEST_F(ProjectStorage, + breaking_prototype_chain_by_deleting_base_component_notifies_unresolved_type_name) { - auto package{createSimpleSynchronizationPackage()}; + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; storage.synchronize(package); - ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId1, - {package.types[0]}, - {sourceId1, sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); } -TEST_F(ProjectStorage, breaking_extension_chain_by_deleting_base_component_throws) +TEST_F(ProjectStorage, breaking_prototype_chain_by_deleting_base_component_has_unresolved_type_name) { - auto package{createSimpleSynchronizationPackage()}; - std::swap(package.types.front().extension, package.types.front().prototype); + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; storage.synchronize(package); - ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId1, - {package.types[0]}, - {sourceId1, sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + HasPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, repairing_prototype_chain_by_fixing_base_component) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + HasPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + breaking_extension_chain_by_deleting_base_component_notifies_unresolved_type_name) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); +} + +TEST_F(ProjectStorage, breaking_extension_chain_by_deleting_base_component_has_unresolved_type_name) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + HasExtensionId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, repairing_extension_chain_by_fixing_base_component) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + storage.synchronize( + SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1, sourceId2}}); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + HasExtensionId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, synchronize_types_add_property_declarations) @@ -2166,12 +2244,39 @@ TEST_F(ProjectStorage, synchronize_types_add_property_declarations) | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } -TEST_F(ProjectStorage, synchronize_types_add_property_declarations_with_missing_imports) +TEST_F(ProjectStorage, + synchronize_adds_property_declarations_with_missing_imports_notifies_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.imports.clear(); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Item"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, synchronize_adds_property_declarations_with_missing_imports_has_null_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.imports.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, synchronize_fixes_unresolved_property_declarations_with_missing_imports) +{ + auto package{createSimpleSynchronizationPackage()}; + package.imports.clear(); + storage.synchronize(package); + + storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("data", fetchTypeId(sourceId2, "QObject"))))); } TEST_F(ProjectStorage, synchronize_types_add_property_declaration_qualified_type) @@ -2359,40 +2464,141 @@ TEST_F(ProjectStorage, synchronize_types_rename_a_property_declaration) | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } -TEST_F(ProjectStorage, using_non_existing_property_type_throws) +TEST_F(ProjectStorage, using_non_existing_property_type_notifies_unresolved_type_name) { - auto package{createSimpleSynchronizationPackage()}; - package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ - "QObject2"}; - package.types.pop_back(); - package.imports = importsSourceId1; + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations.emplace_back("data", + Storage::Synchronization::ImportedType{ + "QObject2"}, + Storage::PropertyDeclarationTraits::IsList); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject2"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, using_non_existing_qualified_exported_property_type_with_wrong_name_throws) +TEST_F(ProjectStorage, using_non_existing_property_type_has_null_type_id) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations.emplace_back("data", + Storage::Synchronization::ImportedType{ + "QObject2"}, + Storage::PropertyDeclarationTraits::IsList); + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, resolve_type_id_after_fixing_non_existing_property_type) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations.emplace_back("data", + Storage::Synchronization::ImportedType{ + "QObject2"}, + Storage::PropertyDeclarationTraits::IsList); + storage.synchronize(package); + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object"}; + + storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(fetchTypeId(sourceId2, "QObject"))))); +} + +TEST_F(ProjectStorage, + using_non_existing_qualified_exported_property_type_with_wrong_name_notifies_unresovled_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "QObject2", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; - package.types.pop_back(); - package.imports = importsSourceId1; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject2"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, using_non_existing_qualified_exported_property_type_with_wrong_module_throws) +TEST_F(ProjectStorage, + using_non_existing_qualified_exported_property_type_with_wrong_name_has_null_type_id) { auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ - "QObject", Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}}; + "QObject2", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, resolve_type_id_after_fixing_non_existing_qualified_property_type) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "QObject2", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; + storage.synchronize(package); + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "QObject", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; + + storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("data", fetchTypeId(sourceId2, "QObject"))))); +} + +TEST_F(ProjectStorage, + using_non_existing_qualified_exported_property_type_with_wrong_module_notifies_unresovled_type_name) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = {}; + package.types[0].propertyDeclarations.pop_back(); + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; package.types.pop_back(); package.imports = importsSourceId1; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, breaking_property_declaration_type_dependency_by_deleting_type_throws) +TEST_F(ProjectStorage, + using_non_existing_qualified_exported_property_type_with_wrong_module_has_null_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; + package.types.pop_back(); + package.imports = importsSourceId1; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, resolve_qualified_exported_property_type_with_fixed_module) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlNativeModuleId, Storage::Version{}, sourceId1}}; + package.types.pop_back(); + package.imports = importsSourceId1; + storage.synchronize(package); + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}}; + + storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, + breaking_property_declaration_type_dependency_by_deleting_type_nofifies_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -2400,7 +2606,40 @@ TEST_F(ProjectStorage, breaking_property_declaration_type_dependency_by_deleting package.types.pop_back(); package.imports = importsSourceId1; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, breaking_property_declaration_type_dependency_by_deleting_type_has_null_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + package.types[0].prototype = Storage::Synchronization::ImportedType{}; + package.types.pop_back(); + package.imports = importsSourceId1; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("data", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, fixing_broken_property_declaration_type_dependenc_has_valid_id) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + package.types[0].prototype = Storage::Synchronization::ImportedType{}; + auto objectType = package.types.back(); + package.types.pop_back(); + package.imports = importsSourceId1; + storage.synchronize(package); + + storage.synchronize({{importsSourceId2}, {objectType}, {sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("data", fetchTypeId(sourceId2, "QObject"))))); } TEST_F(ProjectStorage, synchronize_types_add_function_declarations) @@ -3224,18 +3463,45 @@ TEST_F(ProjectStorage, synchronize_types_remove_alias_declarations) Storage::PropertyDeclarationTraits::IsList)))))); } -TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_throws_for_wrong_type_name) +TEST_F(ProjectStorage, + synchronize_types_add_alias_declarations_notifies_unresolved_type_name_for_wrong_type) { auto package{createSynchronizationPackageWithAliases()}; package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ "QQuickItemWrong"}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId4, - {package.types[2]}, - {sourceId4}, - moduleDependenciesSourceId4, - {sourceId4}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QQuickItemWrong"), sourceId3)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_set_null_type_id_for_wrong_type) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "QQuickItemWrong"}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("items", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, synchronize_types_fixes_null_type_id_after_add_alias_declarations_for_wrong_type) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "QQuickItemWrong"}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "QQuickItem"}; + + storage.synchronize( + {moduleDependenciesSourceId3 + importsSourceId3, {package.types[2]}, {sourceId3}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("items", fetchTypeId(sourceId1, "QQuickItem"))))); } TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_throws_for_wrong_property_name) @@ -3478,7 +3744,8 @@ TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_and_alias) Storage::PropertyDeclarationTraits::IsList)))))); } -TEST_F(ProjectStorage, synchronize_types_remove_type_with_alias_target_property_declaration_throws) +TEST_F(ProjectStorage, + synchronize_remove_type_with_alias_target_property_declaration_nofifies_unresolved_type_name) { auto package{createSynchronizationPackageWithAliases()}; package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ @@ -3486,8 +3753,42 @@ TEST_F(ProjectStorage, synchronize_types_remove_type_with_alias_target_property_ package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); storage.synchronize(package); - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId4}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object2"), sourceId3)); + + storage.synchronize(SynchronizationPackage{{sourceId4}}); +} + +TEST_F(ProjectStorage, + synchronize_remove_type_with_alias_target_property_declaration_set_unresolved_type_id) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); + storage.synchronize(package); + + storage.synchronize(SynchronizationPackage{{sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, synchronize_fixes_type_with_alias_target_property_declaration) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); + storage.synchronize(package); + storage.synchronize(SynchronizationPackage{{sourceId4}}); + + storage.synchronize(SynchronizationPackage{importsSourceId4 + moduleDependenciesSourceId4, + {package.types[3]}, + {sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId2, "QObject"))))); } TEST_F(ProjectStorage, synchronize_types_remove_type_and_alias_property_declaration) @@ -3659,9 +3960,51 @@ TEST_F(ProjectStorage, do_not_relink_alias_property_for_qualified_imported_type_ package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId4, {package.types[3]}, {sourceId4}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object2"), sourceId2)); + + storage.synchronize(SynchronizationPackage{importsSourceId4 + moduleDependenciesSourceId4, + {package.types[3]}, + {sourceId4}}); +} + +TEST_F(ProjectStorage, not_relinked_alias_property_for_qualified_imported_type_name_has_null_type_id) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object2", Storage::Import{pathToModuleId, Storage::Version{}, sourceId2}}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); + + storage.synchronize(SynchronizationPackage{importsSourceId4 + moduleDependenciesSourceId4, + {package.types[3]}, + {sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, + relinked_alias_property_for_qualified_imported_type_name_after_alias_chain_was_fixed) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object2", Storage::Import{pathToModuleId, Storage::Version{}, sourceId2}}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); + storage.synchronize(SynchronizationPackage{importsSourceId4, {package.types[3]}, {sourceId4}}); + package.types[3].exportedTypes[0].moduleId = pathToModuleId; + + storage.synchronize(SynchronizationPackage{importsSourceId4 + moduleDependenciesSourceId4, + {package.types[3]}, + {sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId4, "QObject2"))))); } TEST_F(ProjectStorage, @@ -3799,7 +4142,7 @@ TEST_F(ProjectStorage, do_not_relink_alias_property_for_deleted_type_and_propert TypeTraitsKind::Reference)))); } -TEST_F(ProjectStorage, do_not_relink_property_type_does_not_exists) +TEST_F(ProjectStorage, do_not_relink_property_type_does_not_exists_notifies_unresolved_type_name) { auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ @@ -3808,11 +4151,63 @@ TEST_F(ProjectStorage, do_not_relink_property_type_does_not_exists) package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId4}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object2"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{sourceId4}}); } -TEST_F(ProjectStorage, do_not_relink_alias_property_type_does_not_exists) +TEST_F(ProjectStorage, not_relinked_property_type_has_null_type_id) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + + storage.synchronize({{sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId2, "QObject")), + PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, not_relinked_property_type_fixed_after_adding_property_type_again) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + storage.synchronize({{sourceId4}}); + + storage.synchronize( + {importsSourceId4 + moduleDependenciesSourceId4, {package.types[3]}, {sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId2, "QObject")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId4, "QObject2"))))); +} + +TEST_F(ProjectStorage, not_relinked_property_type_fixed_after_type_is_added_again) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + storage.synchronize({{sourceId4}}); + + storage.synchronize( + {importsSourceId4 + moduleDependenciesSourceId4, {package.types[3]}, {sourceId4}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId2, "QObject")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId4, "QObject2"))))); +} + +TEST_F(ProjectStorage, do_not_relink_alias_property_type_does_not_exists_notifies_unresolved_type_name) { auto package{createSynchronizationPackageWithAliases2()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ @@ -3820,8 +4215,40 @@ TEST_F(ProjectStorage, do_not_relink_alias_property_type_does_not_exists) package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId1}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Item"), sourceId3)); + + storage.synchronize(SynchronizationPackage{{sourceId1}}); +} + +TEST_F(ProjectStorage, not_relinked_alias_property_type_has_null_type_id) +{ + auto package{createSynchronizationPackageWithAliases2()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + + storage.synchronize(SynchronizationPackage{{sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, not_relinked_alias_property_type_fixed_after_referenced_type_is_added_again) +{ + auto package{createSynchronizationPackageWithAliases2()}; + package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ + "Object2"}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + storage.synchronize(package); + storage.synchronize({{sourceId1}}); + + storage.synchronize( + {importsSourceId1 + moduleDependenciesSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations( + Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId4, "QObject2"))))); } TEST_F(ProjectStorage, change_prototype_type_name) @@ -4620,13 +5047,45 @@ TEST_F(ProjectStorage, qualified_property_declaration_type_name) Storage::PropertyDeclarationTraits::IsList))))); } -TEST_F(ProjectStorage, qualified_property_declaration_type_name_down_the_module_chain_throws) +TEST_F(ProjectStorage, + qualified_property_declaration_type_name_down_the_module_chain_nofifies_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ - "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; + "Obj", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, + qualified_property_declaration_type_name_down_the_module_chain_has_null_type_name) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Obj", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, borken_qualified_property_declaration_type_name_down_the_module_chain_fixed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Obj", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; + storage.synchronize(package); + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Obj", Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}}; + + storage.synchronize( + {importsSourceId1 + moduleDependenciesSourceId1, {package.types[0]}, {sourceId1}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Not(Contains(HasPropertyTypeId(IsNullTypeId()))))); } TEST_F(ProjectStorage, qualified_property_declaration_type_name_in_the_module_chain) @@ -4672,7 +5131,7 @@ TEST_F(ProjectStorage, qualified_property_declaration_type_name_with_version) Storage::PropertyDeclarationTraits::IsList))))); } -TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type_throws) +TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type_notifies_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ @@ -4680,9 +5139,48 @@ TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type_throws storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2 + moduleDependenciesSourceId2, + {package.types[1]}, + {sourceId2}}); +} + +TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type_sets_type_id_to_null) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}}; + storage.synchronize(package); + package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; + + storage.synchronize(SynchronizationPackage{importsSourceId2 + moduleDependenciesSourceId2, + {package.types[1]}, + {sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Contains(HasPropertyTypeId(IsNullTypeId())))); +} + +TEST_F(ProjectStorage, + fixed_broken_change_property_type_module_id_with_qualified_type_sets_fixes_type_idl) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ + "Object", Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}}; + storage.synchronize(package); + package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; + storage.synchronize(SynchronizationPackage{importsSourceId2 + moduleDependenciesSourceId2, + {package.types[1]}, + {sourceId2}}); + package.types[1].exportedTypes[0].moduleId = qmlModuleId; + + storage.synchronize(SynchronizationPackage{importsSourceId2 + moduleDependenciesSourceId2, + {package.types[1]}, + {sourceId2}}); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + PropertyDeclarations(Not(Contains(HasPropertyTypeId(IsNullTypeId()))))); } TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type) @@ -5988,19 +6486,54 @@ TEST_F(ProjectStorage, synchronize_types_remove_indirect_alias_declaration) Storage::PropertyDeclarationTraits::IsList)))))); } -TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_throws_for_wrong_type_name) +TEST_F(ProjectStorage, + synchronize_types_add_indirect_alias_declarations_notifies_unresolved_type_name_for_wrong_type_name) { auto package{createSynchronizationPackageWithIndirectAliases()}; storage.synchronize(package); package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ "QQuickItemWrong"}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, - {package.types[2]}, - {sourceId3}, - moduleDependenciesSourceId3, - {sourceId3}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QQuickItemWrong"), sourceId3)); + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); +} + +TEST_F(ProjectStorage, + synchronize_types_add_indirect_alias_declarations_for_wrong_type_name_sets_null_type_id) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "QQuickItemWrong"}; + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + auto type = storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")); + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))); +} + +TEST_F(ProjectStorage, + fixed_synchronize_types_add_indirect_alias_declarations_for_wrong_type_name_sets_type_id) +{ + auto package{createSynchronizationPackageWithIndirectAliases()}; + storage.synchronize(package); + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "QQuickItemWrong"}; + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ + "Item"}; + + storage.synchronize(SynchronizationPackage{ + importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); + + auto type = storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")); + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId3, "QAliasItem")), + PropertyDeclarations(Not(Contains(IsPropertyDeclaration("objects", IsNullTypeId()))))); } TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_throws_for_wrong_property_name) @@ -7517,7 +8050,7 @@ TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_p storage.synchronizeDocumentImports({}, sourceId1); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(IsUnresolvedTypeId())); + HasPrototypeId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved) @@ -7531,7 +8064,7 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prot storage.synchronizeDocumentImports(imports, sourceId1); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); + HasPrototypeId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, get_exported_type_names) @@ -8331,7 +8864,7 @@ TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved) storage.synchronize(package); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId())); + HasPrototypeId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved) @@ -8348,7 +8881,7 @@ TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved) storage.synchronize(package); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId())); + HasExtensionId(IsUnresolvedTypeId())); } TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype) @@ -8364,7 +8897,7 @@ TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype) storage.synchronize(package); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); + HasPrototypeId(fetchTypeId(sourceId2, "QObject"))); } TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension) @@ -8380,7 +8913,7 @@ TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension) storage.synchronize(package); ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), - IsExtensionId(fetchTypeId(sourceId2, "QObject"))); + HasExtensionId(fetchTypeId(sourceId2, "QObject"))); } } // namespace