QmlDesigner: Use error notifier for prototype and extension type name resolving

If the prototype or extension has an unresolved id, it needs to be
checked, if an exported name belonging to the prototype or extension was
updated. In that case the id has to be again resolved.

Task-number: QDS-12761
Change-Id: I7a733662cf37e13e8c2db53dec5a4f3e0a9b6ecf
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2024-05-16 18:17:21 +02:00
parent bcf0594419
commit b8322aece2
8 changed files with 846 additions and 140 deletions

View File

@@ -36,13 +36,6 @@ class DatabaseBackend;
enum class Type : char { Invalid, Integer, Float, Text, Blob, Null };
template<typename Enumeration>
constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
class SQLITE_EXPORT BaseStatement
{
public:
@@ -87,7 +80,7 @@ public:
template<typename Type, typename = std::enable_if_t<Type::IsBasicId::value>>
void bind(int index, Type id)
{
if (id)
if (!id.isNull())
bind(index, id.internalId());
else
bindNull(index);

View File

@@ -11,6 +11,13 @@
namespace Sqlite {
template<typename Enumeration>
static constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
template<auto Type, typename InternalIntegerType = long long>
class BasicId
{
@@ -27,7 +34,15 @@ public:
return id;
}
constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second)
template<typename Enumeration>
static constexpr BasicId createSpecialState(Enumeration specialState)
{
BasicId id;
id.id = ::Sqlite::to_underlying(specialState);
return id;
}
friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second)
{
return first.id == second.id;
}
@@ -57,6 +72,14 @@ public:
constexpr bool isValid() const { return id > 0; }
constexpr bool isNull() const { return id == 0; }
template<typename Enumeration>
constexpr bool hasSpecialState(Enumeration specialState) const
{
return id == ::Sqlite::to_underlying(specialState);
}
explicit operator bool() const { return isValid(); }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
@@ -68,13 +91,13 @@ public:
template<typename String>
friend void convertToString(String &string, BasicId id)
{
if (id.isValid())
NanotraceHR::convertToString(string, id.internalId());
if (id.isNull())
NanotraceHR::convertToString(string, "invalid null");
else
NanotraceHR::convertToString(string, "invalid");
NanotraceHR::convertToString(string, id.internalId());
}
private:
protected:
InternalIntegerType id = 0;
};

View File

@@ -7,6 +7,25 @@
namespace QmlDesigner {
enum class SpecialIdState { Unresolved = -1 };
constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved);
class UnresolvedTypeId : public TypeId
{
public:
constexpr UnresolvedTypeId()
: TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)}
{}
static constexpr UnresolvedTypeId create(DatabaseType idNumber)
{
UnresolvedTypeId id;
id.id = idNumber;
return id;
}
};
struct ProjectStorage::Statements
{
Statements(Sqlite::Database &database)
@@ -17,9 +36,13 @@ struct ProjectStorage::Statements
Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{
"INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database};
Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{
"UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
"WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId "
"IS NOT ?4 OR extensionNameId IS NOT ?5)",
"UPDATE types "
"SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
"WHERE typeId=?1 AND ( "
" prototypeId IS NOT ?2 "
" OR extensionId IS NOT ?3 "
" OR prototypeId IS NOT ?4 "
" OR extensionNameId IS NOT ?5)",
database};
mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{
"SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
@@ -357,13 +380,51 @@ struct ProjectStorage::Statements
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{
"UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
"typeId, prototypeNameId",
Sqlite::ReadWriteStatement<2, 2> updatePrototypeIdToTypeIdStatement{
"UPDATE types "
"SET prototypeId=?2 "
"WHERE prototypeId=?1 "
"RETURNING typeId, prototypeNameId",
database};
Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{
"UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING "
"typeId, extensionNameId",
Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{
"UPDATE types "
"SET extensionId=?2 "
"WHERE extensionId=?1 "
"RETURNING typeId, extensionNameId",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{
"SELECT typeId, prototypeNameId "
"FROM types "
"WHERE prototypeNameId IN ( "
" SELECT importedTypeNameId "
" FROM "
" importedTypeNames WHERE name=?1) "
" AND prototypeId=?2",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{
"SELECT typeId , prototypeNameId "
"FROM types "
"WHERE prototypeId=?1 AND sourceId=?2",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{
"SELECT typeId, extensionNameId "
"FROM types "
"WHERE extensionId=?1 AND sourceId=?2",
database};
Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{
"UPDATE types "
"SET prototypeId=?2, extensionId=?3 "
"WHERE sourceId=?1 "
"RETURNING typeId, prototypeNameId, extensionNameId",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{
"SELECT typeId , prototypeNameId "
"FROM types "
"WHERE extensionNameId IN ( "
" SELECT importedTypeNameId "
" FROM importedTypeNames "
" WHERE name=?1) "
" AND extensionId=?2",
database};
Sqlite::WriteStatement<2> updateTypePrototypeStatement{
"UPDATE types SET prototypeId=?2 WHERE typeId=?1", database};
@@ -619,6 +680,8 @@ struct ProjectStorage::Statements
"UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{
"SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database};
mutable Sqlite::ReadStatement<1, 1> selectSourceIdByTypeIdStatement{
"SELECT sourceId FROM types WHERE typeId=?", database};
mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{
"SELECT annotationTraits "
"FROM types "
@@ -801,23 +864,23 @@ public:
auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text);
typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer);
auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId",
typesTable,
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Restrict);
typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer);
auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId",
typesTable,
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Restrict);
typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer);
auto &prototypeIdColumn = typesTable.addColumn("prototypeId",
Sqlite::StrictColumnType::Integer);
auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId",
Sqlite::StrictColumnType::Integer);
auto &extensionIdColumn = typesTable.addColumn("extensionId",
Sqlite::StrictColumnType::Integer);
auto &extensionNameIdColumn = typesTable.addColumn("extensionNameId",
Sqlite::StrictColumnType::Integer);
auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId",
Sqlite::StrictColumnType::Integer);
typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer);
typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
typesTable.addIndex({defaultPropertyIdColumn});
typesTable.addIndex({prototypeIdColumn});
typesTable.addIndex({extensionIdColumn});
typesTable.addIndex({prototypeIdColumn, sourceIdColumn});
typesTable.addIndex({extensionIdColumn, sourceIdColumn});
typesTable.addIndex({prototypeNameIdColumn});
typesTable.addIndex({extensionNameIdColumn});
typesTable.initialize(database);
@@ -1131,7 +1194,7 @@ ProjectStorage::ProjectStorage(Database &database,
ProjectStorageErrorNotifierInterface &errorNotifier,
bool isInitialized)
: database{database}
, errorNotifier{errorNotifier}
, errorNotifier{&errorNotifier}
, exclusiveTransaction{database}
, initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)}
, moduleCache{ModuleStorageAdapter{*this}}
@@ -1175,7 +1238,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag
package.moduleDependencies,
package.updatedModuleDependencySourceIds,
package.moduleExportedImports,
package.updatedModuleIds);
package.updatedModuleIds,
relinkablePrototypes,
relinkableExtensions);
synchronizeTypes(package.types,
updatedTypeIds,
insertedAliasPropertyDeclarations,
@@ -1223,7 +1288,24 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source
keyValue("source id", sourceId)};
Sqlite::withImmediateTransaction(database, [&] {
synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import);
AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
synchronizeDocumentImports(imports,
{sourceId},
Storage::Synchronization::ImportKind::Import,
Relink::Yes,
relinkablePrototypes,
relinkableExtensions);
relink(relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
});
}
@@ -2642,19 +2724,29 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports,
Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds)
const ModuleIds &updatedModuleIds,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()};
synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import);
synchronizeDocumentImports(imports,
updatedSourceIds,
Storage::Synchronization::ImportKind::Import,
Relink::No,
relinkablePrototypes,
relinkableExtensions);
importTracer.end();
NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t,
projectStorageCategory()};
synchronizeDocumentImports(moduleDependencies,
updatedModuleDependencySourceIds,
Storage::Synchronization::ImportKind::ModuleDependency);
Storage::Synchronization::ImportKind::ModuleDependency,
Relink::Yes,
relinkablePrototypes,
relinkableExtensions);
moduleDependenciesTracer.end();
}
@@ -2822,10 +2914,29 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable
keyValue("relinkable prototypes", relinkablePrototypes)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId);
s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId);
}
void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId(
Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle invalid prototypes"_t,
projectStorageCategory(),
keyValue("type id", exportedTypeName),
keyValue("relinkable prototypes", relinkablePrototypes)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback,
exportedTypeName,
typeId);
}
void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
@@ -2837,10 +2948,27 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable
keyValue("relinkable extensions", relinkableExtensions)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->updateExtensionIdToNullStatement.readCallback(callback, extensionId);
s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId);
}
void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId(
Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle invalid extensions"_t,
projectStorageCategory(),
keyValue("type id", exportedTypeName),
keyValue("relinkable extensions", relinkableExtensions)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId);
}
void ProjectStorage::deleteType(TypeId typeId,
@@ -2927,6 +3055,39 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable
TypeCompare<PropertyDeclaration>{});
}
template<typename Callable>
void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds,
Callable updateStatement)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"relink prototypes"_t,
projectStorageCategory(),
keyValue("relinkable prototypes", relinkablePrototypes),
keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()),
relinkablePrototypes.end());
Utils::set_greedy_difference(
relinkablePrototypes.cbegin(),
relinkablePrototypes.cend(),
deletedTypeIds.begin(),
deletedTypeIds.end(),
[&](const Prototype &prototype) {
TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
if (!prototypeId)
errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId),
fetchTypeSourceId(prototype.typeId));
updateStatement(prototype.typeId, prototypeId);
checkForPrototypeChainCycle(prototype.typeId);
},
TypeCompare<Prototype>{});
}
void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds,
const TypeIds &typeIdsToBeDeleted,
@@ -3141,6 +3302,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
} catch (const Sqlite::ConstraintPreventsModification &) {
throw QmlDesigner::ExportedTypeCannotBeInserted{type.name};
}
handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes);
handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions);
};
auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
@@ -3176,6 +3340,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
relinkableAliasPropertyDeclarations);
handlePrototypes(view.typeId, relinkablePrototypes);
handleExtensions(view.typeId, relinkableExtensions);
s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId);
};
@@ -3491,11 +3656,90 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull(
PropertyCompare<AliasPropertyDeclaration>{});
}
void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
TypeId prototypeId,
Prototypes &relinkablePrototypes)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("type id", prototypeId)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback,
prototypeId,
sourceId);
}
void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
TypeId prototypeId,
TypeId extensionId,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("prototype id", prototypeId),
keyValue("extension id", extensionId)};
auto callback =
[&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback,
sourceId,
prototypeId,
extensionId);
}
void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
TypeId extensionId,
Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("type id", extensionId)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback,
extensionId,
sourceId);
}
ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId,
ImportId parentImportId)
ImportId parentImportId,
Relink relink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
if (relink == Relink::Yes) {
handlePrototypesWithSourceIdAndPrototypeId(import.sourceId,
unresolvedTypeId,
relinkablePrototypes);
handleExtensionsWithSourceIdAndExtensionId(import.sourceId,
unresolvedTypeId,
relinkableExtensions);
}
if (import.version.minor) {
return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId,
import.moduleId,
@@ -3522,7 +3766,10 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
Storage::Synchronization::ImportKind importKind)
Storage::Synchronization::ImportKind importKind,
Relink relink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) {
return std::tie(first.sourceId, first.moduleId, first.version)
@@ -3559,7 +3806,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
keyValue("source id", import.sourceId),
keyValue("module id", import.moduleId)};
auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{});
auto importId = insertDocumentImport(import,
importKind,
import.moduleId,
ImportId{},
relink,
relinkablePrototypes,
relinkableExtensions);
auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
Storage::Import additionImport{exportedModuleId,
Storage::Version{majorVersion, minorVersion},
@@ -3579,7 +3832,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
auto indirectImportId = insertDocumentImport(additionImport,
exportedImportKind,
import.moduleId,
importId);
importId,
relink,
relinkablePrototypes,
relinkableExtensions);
tracer.end(keyValue("import id", indirectImportId));
};
@@ -3606,6 +3862,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
s->deleteDocumentImportStatement.write(view.importId);
s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
if (relink == Relink::Yes) {
handlePrototypesAndExtensionsWithSourceId(view.sourceId,
unresolvedTypeId,
unresolvedTypeId,
relinkablePrototypes,
relinkableExtensions);
}
};
Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
@@ -4156,25 +4419,29 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla
}
std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId(
const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId)
const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t,
projectStorageCategory(),
keyValue("imported type name", typeName),
keyValue("imported type name", importedTypeName),
keyValue("source id", sourceId)};
TypeId typeId;
ImportedTypeNameId typeNameId;
if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) {
typeNameId = fetchImportedTypeNameId(typeName, sourceId);
auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; },
importedTypeName);
if (!typeName.empty()) {
typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId);
typeId = fetchTypeId(typeNameId);
tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId));
if (!typeId)
throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId};
if (!typeId) {
errorNotifier->typeNameCannotBeResolved(typeName, sourceId);
return {unresolvedTypeId, typeNameId};
}
}
return {typeId, typeNameId};
@@ -4323,6 +4590,11 @@ Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId type
return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId);
}
SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const
{
return s->selectSourceIdByTypeIdStatement.value<SourceId>(typeId);
}
TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const
{
@@ -4334,9 +4606,10 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
TypeId typeId;
if (kind == Storage::Synchronization::TypeNameKind::Exported) {
typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<UnresolvedTypeId>(typeNameId);
} else {
typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<UnresolvedTypeId>(
typeNameId);
}
tracer.end(keyValue("type id", typeId));

View File

@@ -39,6 +39,8 @@ class ProjectStorage final : public ProjectStorageInterface
using Database = Sqlite::Database;
friend Storage::Info::CommonTypeCache<ProjectStorageType>;
enum class Relink { No, Yes };
public:
ProjectStorage(Database &database,
ProjectStorageErrorNotifierInterface &errorNotifier,
@@ -49,6 +51,11 @@ public:
void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override;
void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier)
{
this->errorNotifier = &errorNotifier;
}
void addObserver(ProjectStorageObserver *observer) override;
void removeObserver(ProjectStorageObserver *observer) override;
@@ -443,6 +450,11 @@ private:
return first.typeId < second.typeId;
}
friend bool operator==(Prototype first, Prototype second)
{
return first.typeId == second.typeId;
}
template<typename String>
friend void convertToString(String &string, const Prototype &prototype)
{
@@ -575,7 +587,9 @@ private:
Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds);
const ModuleIds &updatedModuleIds,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void synchromizeModuleExportedImports(
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
@@ -593,9 +607,14 @@ private:
PropertyDeclarations &relinkablePropertyDeclarations);
void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes);
void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
TypeId typeId,
Prototypes &relinkablePrototypes);
void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions);
void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
TypeId typeId,
Prototypes &relinkableExtensions);
void deleteType(TypeId typeId,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations,
@@ -611,32 +630,7 @@ private:
template<typename Callable>
void relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds,
Callable updateStatement)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"relink prototypes"_t,
projectStorageCategory(),
keyValue("relinkable prototypes", relinkablePrototypes),
keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
Utils::set_greedy_difference(
relinkablePrototypes.cbegin(),
relinkablePrototypes.cend(),
deletedTypeIds.begin(),
deletedTypeIds.end(),
[&](const Prototype &prototype) {
TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
if (!prototypeId)
throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)};
updateStatement(prototype.typeId, prototypeId);
checkForPrototypeChainCycle(prototype.typeId);
},
TypeCompare<Prototype>{});
}
Callable updateStatement);
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds,
@@ -751,14 +745,32 @@ private:
Storage::Synchronization::Types &types,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
TypeId prototypeId,
Prototypes &relinkablePrototypes);
void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
TypeId prototypeId,
TypeId extensionId,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
TypeId extensionId,
Prototypes &relinkableExtensions);
ImportId insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId,
ImportId parentImportId);
ImportId parentImportId,
Relink forceRelink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
Storage::Synchronization::ImportKind importKind);
Storage::Synchronization::ImportKind importKind,
Relink forceRelink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters);
@@ -904,6 +916,7 @@ private:
TypeId fetchTypeId(ImportedTypeNameId typeNameId) const;
Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const;
SourceId fetchTypeSourceId(TypeId typeId) const;
TypeId fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const;
@@ -970,7 +983,7 @@ private:
public:
Database &database;
ProjectStorageErrorNotifierInterface &errorNotifier;
ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null
Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction;
std::unique_ptr<Initializer> initializer;
mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}};

View File

@@ -9,6 +9,7 @@
class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface
{
public:
MOCK_METHOD(void,
typeNameCannotBeResolved,
(Utils::SmallStringView typeName, QmlDesigner::SourceId souceId),

View File

@@ -833,8 +833,9 @@ std::ostream &operator<<(std::ostream &out, const Type &type)
{
using std::operator<<;
using Utils::operator<<;
return out << "( typename: \"" << type.typeName << "\", prototype: " << type.prototype << ", "
<< type.prototypeId << ", " << type.traits << ", source: " << type.sourceId
return out << "( typename: \"" << type.typeName << "\", prototype: {\"" << type.prototype
<< "\", " << type.prototypeId << "}, " << "\", extension: {\"" << type.extension
<< "\", " << type.extensionId << "}, " << type.traits << ", source: " << type.sourceId
<< ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations
<< ", functions: " << type.functionDeclarations
<< ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel

View File

@@ -205,6 +205,23 @@ MATCHER_P4(IsInfoPropertyDeclaration,
&& propertyDeclaration.traits == traits;
}
auto IsUnresolvedTypeId()
{
return Property(&QmlDesigner::TypeId::internalId, -1);
}
template<typename Matcher>
auto IsPrototypeId(const Matcher &matcher)
{
return Field(&Storage::Synchronization::Type::prototypeId, matcher);
}
template<typename Matcher>
auto IsExtensionId(const Matcher &matcher)
{
return Field(&Storage::Synchronization::Type::extensionId, matcher);
}
class HasNameMatcher
{
public:
@@ -286,6 +303,8 @@ protected:
static void TearDownTestSuite() { staticData.reset(); }
ProjectStorage() { storage.setErrorNotifier(errorNotifierMock); }
~ProjectStorage() { storage.resetForTestsOnly(); }
template<typename Range>
@@ -312,6 +331,32 @@ protected:
storage.fetchSourceId(sourceContextId3, "bar");
}
auto createVerySimpleSynchronizationPackage()
{
SynchronizationPackage package;
package.types.push_back(Storage::Synchronization::Type{
"QQuickItem",
Storage::Synchronization::ImportedType{},
Storage::Synchronization::ImportedType{},
TypeTraitsKind::Reference,
sourceId1,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}});
package.types.push_back(Storage::Synchronization::Type{
"QObject",
Storage::Synchronization::ImportedType{},
Storage::Synchronization::ImportedType{},
TypeTraitsKind::Reference,
sourceId2,
{Storage::Synchronization::ExportedType{qmlModuleId, "Object"},
Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}});
package.updatedSourceIds = {sourceId1, sourceId2};
return package;
}
auto createSimpleSynchronizationPackage()
{
SynchronizationPackage package;
@@ -1142,7 +1187,7 @@ protected:
inline static std::unique_ptr<StaticData> staticData;
Sqlite::Database &database = staticData->database;
QmlDesigner::ProjectStorage &storage = staticData->storage;
ProjectStorageErrorNotifierMock &errorNotifierMock = staticData->errorNotifierMock;
NiceMock<ProjectStorageErrorNotifierMock> errorNotifierMock;
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{storage};
QmlDesigner::SourcePathView path1{"/path1/to"};
QmlDesigner::SourcePathView path2{"/path2/to"};
@@ -1486,22 +1531,193 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_
"QQuickItem"))))));
}
TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_prototype_name)
TEST_F(ProjectStorage,
synchronize_types_adds_unknown_prototype_which_notifies_about_unresolved_type_name)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_extension_name)
TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_type_id)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_unresolved_prototype_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_extension_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_unresolved_extension_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_invalid_extension_indirectly_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_extension_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_adds_extension_which_notifies_about_unresolved_type_name)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module)
{
auto package{createSimpleSynchronizationPackage()};
@@ -1774,7 +1990,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension)
IsExportedType(qtQuickNativeModuleId, "QQuickItem"))))));
}
TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype)
TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_prototype)
{
auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{
@@ -1786,10 +2002,12 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension)
TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_extension)
{
auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{
@@ -1801,7 +2019,9 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module)
@@ -3670,7 +3890,8 @@ TEST_F(ProjectStorage, change_extension_type_module_id)
TypeTraitsKind::Reference)));
}
TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws)
TEST_F(ProjectStorage,
change_qualified_prototype_type_module_id_notifies_that_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -3678,12 +3899,12 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_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, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws)
TEST_F(ProjectStorage, change_qualified_extension_type_module_id_notifies_cannot_resolve)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
@@ -3692,9 +3913,9 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id_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, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, change_qualified_prototype_type_module_id)
@@ -3798,9 +4019,9 @@ TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_protot
package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3";
ASSERT_THROW(storage.synchronize(
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name)
@@ -3813,9 +4034,9 @@ TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extens
package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3";
ASSERT_THROW(storage.synchronize(
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, throw_for_prototype_chain_cycles)
@@ -4117,23 +4338,29 @@ TEST_F(ProjectStorage, qualified_extension)
TypeTraitsKind::Reference)));
}
TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws)
TEST_F(ProjectStorage,
qualified_prototype_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_extension_upper_down_the_module_chain_throws)
TEST_F(ProjectStorage,
qualified_extension_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain)
@@ -4189,7 +4416,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain)
TypeTraitsKind::Reference)));
}
TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws)
TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -4205,10 +4432,12 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3);
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws)
TEST_F(ProjectStorage, qualified_extension_with_wrong_version_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
@@ -4225,7 +4454,9 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3);
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_prototype_with_version)
@@ -4338,23 +4569,29 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain)
TypeTraitsKind::Reference)));
}
TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws)
TEST_F(ProjectStorage,
qualified_prototype_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_extension_with_version_down_the_proto_type_chain_throws)
TEST_F(ProjectStorage,
qualified_extension_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_property_declaration_type_name)
@@ -4659,7 +4896,7 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im
}
TEST_F(ProjectStorage,
fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_throws)
fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4673,12 +4910,13 @@ TEST_F(ProjectStorage,
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage,
fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_throws)
fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4691,11 +4929,12 @@ TEST_F(ProjectStorage,
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws)
TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4709,11 +4948,13 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws)
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throws)
TEST_F(ProjectStorage,
fetch_low_minor_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4726,8 +4967,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type)
@@ -4775,7 +5017,8 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type)
TypeTraitsKind::Reference)));
}
TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws)
TEST_F(ProjectStorage,
fetch_different_major_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4789,11 +5032,13 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws)
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type_throws)
TEST_F(ProjectStorage,
fetch_different_major_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4806,8 +5051,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type)
@@ -7248,6 +7494,46 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import)
ASSERT_TRUE(storage.importId(imports.back()));
}
TEST_F(ProjectStorage,
synchronize_document_imports_removes_import_notifies_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronizeDocumentImports({}, sourceId1);
}
TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_prototype_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
storage.synchronizeDocumentImports({}, sourceId1);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
Storage::Imports imports;
imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
storage.synchronizeDocumentImports(imports, sourceId1);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage, get_exported_type_names)
{
auto package{createSimpleSynchronizationPackage()};
@@ -7996,4 +8282,105 @@ TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id)
ASSERT_THAT(heirIds, IsEmpty());
}
TEST_F(ProjectStorage,
removed_document_import_notifies_for_prototypes_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage,
removed_document_import_notifies_for_extensions_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
} // namespace

View File

@@ -269,6 +269,21 @@ TEST_F(SqliteStatement, bind_int_id)
ASSERT_THAT(readStatement.fetchIntValue(0), 42);
}
TEST_F(SqliteStatement, bind_special_state_id)
{
enum class SpecialIdState { Unresolved = -1 };
constexpr TestIntId unresolvedTypeId = TestIntId::createSpecialState(SpecialIdState::Unresolved);
SqliteTestStatement<0, 1> statement("INSERT INTO test VALUES ('id', 323, ?)", database);
statement.bind(1, unresolvedTypeId);
statement.next();
SqliteTestStatement<1, 1> readStatement("SELECT value FROM test WHERE name='id'", database);
readStatement.next();
ASSERT_THAT(readStatement.fetchType(0), Sqlite::Type::Integer);
ASSERT_THAT(readStatement.fetchIntValue(0), -1);
}
TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null)
{
TestLongLongId id;