forked from qt-creator/qt-creator
QmlDesigner: Relink prototypes
Task-number: QDS-4552 Change-Id: Ibb0d0048c114e100c215493bc9d633d4dbe3c4e0 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -472,6 +472,21 @@ private:
|
|||||||
SourceId sourceId;
|
SourceId sourceId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Prototype
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Prototype(TypeId typeId, Storage::TypeName prototypeName, SourceId sourceId)
|
||||||
|
: typeId{typeId}
|
||||||
|
, typeName{std::move(prototypeName)}
|
||||||
|
, sourceId{sourceId}
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TypeId typeId;
|
||||||
|
Storage::TypeName typeName;
|
||||||
|
SourceId sourceId;
|
||||||
|
};
|
||||||
|
|
||||||
Storage::TypeName fetchTypeName(TypeNameId typeNameId)
|
Storage::TypeName fetchTypeName(TypeNameId typeNameId)
|
||||||
{
|
{
|
||||||
Storage::TypeName typeName;
|
Storage::TypeName typeName;
|
||||||
@@ -530,12 +545,27 @@ private:
|
|||||||
updatesPropertyDeclarationPropertyTypeToNullStatement.readCallback(callback, &typeId);
|
updatesPropertyDeclarationPropertyTypeToNullStatement.readCallback(callback, &typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handlePrototypes(TypeId prototypeId, std::vector<Prototype> &relinkablePrototypes)
|
||||||
|
{
|
||||||
|
auto callback = [&](long long typeId, long long prototypeNameId, int sourceId) {
|
||||||
|
relinkablePrototypes.emplace_back(TypeId{typeId},
|
||||||
|
fetchTypeName(TypeNameId{prototypeNameId}),
|
||||||
|
SourceId{sourceId});
|
||||||
|
|
||||||
|
return Sqlite::CallbackControl::Continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePrototypeToNullStatement.readCallback(callback, &prototypeId);
|
||||||
|
}
|
||||||
|
|
||||||
void deleteType(TypeId typeId,
|
void deleteType(TypeId typeId,
|
||||||
std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations,
|
std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations,
|
||||||
std::vector<PropertyDeclaration> &relinkablePropertyDeclarations)
|
std::vector<PropertyDeclaration> &relinkablePropertyDeclarations,
|
||||||
|
std::vector<Prototype> &relinkablePrototypes)
|
||||||
{
|
{
|
||||||
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
|
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
|
||||||
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
|
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
|
||||||
|
handlePrototypes(typeId, relinkablePrototypes);
|
||||||
deleteTypeNamesByTypeIdStatement.write(&typeId);
|
deleteTypeNamesByTypeIdStatement.write(&typeId);
|
||||||
deleteEnumerationDeclarationByTypeIdStatement.write(&typeId);
|
deleteEnumerationDeclarationByTypeIdStatement.write(&typeId);
|
||||||
deletePropertyDeclarationByTypeIdStatement.write(&typeId);
|
deletePropertyDeclarationByTypeIdStatement.write(&typeId);
|
||||||
@@ -544,7 +574,8 @@ private:
|
|||||||
deleteTypeStatement.write(&typeId);
|
deleteTypeStatement.write(&typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void relinkAliasPropertyDeclaration(const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations)
|
void relinkAliasPropertyDeclarations(
|
||||||
|
const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations)
|
||||||
{
|
{
|
||||||
for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) {
|
for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) {
|
||||||
auto [typeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(alias.aliasTypeName,
|
auto [typeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(alias.aliasTypeName,
|
||||||
@@ -571,7 +602,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void relinkPropertyDeclaration(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration)
|
void relinkPropertyDeclarations(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration)
|
||||||
{
|
{
|
||||||
for (const PropertyDeclaration &property : relinkablePropertyDeclaration) {
|
for (const PropertyDeclaration &property : relinkablePropertyDeclaration) {
|
||||||
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(property.typeName,
|
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(property.typeName,
|
||||||
@@ -593,10 +624,30 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void relinkPrototypes(std::vector<Prototype> relinkablePrototypes)
|
||||||
|
{
|
||||||
|
for (const Prototype &prototype : relinkablePrototypes) {
|
||||||
|
auto [prototypeId, prototypeNameId] = fetchTypeIdByNameUngarded(prototype.typeName,
|
||||||
|
prototype.sourceId);
|
||||||
|
|
||||||
|
if (!prototypeId) {
|
||||||
|
auto hasTypeId = selectPropertyDeclarationIdStatement.template optionalValue<TypeId>(
|
||||||
|
&prototype.typeId);
|
||||||
|
if (hasTypeId)
|
||||||
|
throw TypeNameDoesNotExists{};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId, &prototypeNameId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds)
|
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds)
|
||||||
{
|
{
|
||||||
std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations;
|
std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations;
|
||||||
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
|
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
|
||||||
|
std::vector<Prototype> relinkablePrototypes;
|
||||||
|
|
||||||
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
|
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
|
||||||
return &typeId;
|
return &typeId;
|
||||||
@@ -609,7 +660,8 @@ private:
|
|||||||
auto callback = [&](long long typeId) {
|
auto callback = [&](long long typeId) {
|
||||||
deleteType(TypeId{typeId},
|
deleteType(TypeId{typeId},
|
||||||
relinkableAliasPropertyDeclarations,
|
relinkableAliasPropertyDeclarations,
|
||||||
relinkablePropertyDeclarations);
|
relinkablePropertyDeclarations,
|
||||||
|
relinkablePrototypes);
|
||||||
return Sqlite::CallbackControl::Continue;
|
return Sqlite::CallbackControl::Continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -617,17 +669,22 @@ private:
|
|||||||
Utils::span(sourceIdValues),
|
Utils::span(sourceIdValues),
|
||||||
Utils::span(updatedTypeIdValues));
|
Utils::span(updatedTypeIdValues));
|
||||||
|
|
||||||
relinkPropertyDeclaration(relinkablePropertyDeclarations);
|
relinkPropertyDeclarations(relinkablePropertyDeclarations);
|
||||||
relinkAliasPropertyDeclaration(relinkableAliasPropertyDeclarations);
|
relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations);
|
||||||
|
relinkPrototypes(relinkablePrototypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteTypesForImportId(ImportId importId)
|
void deleteTypesForImportId(ImportId importId)
|
||||||
{
|
{
|
||||||
std::vector<AliasPropertyDeclaration> aliasPropertyDeclarations;
|
std::vector<AliasPropertyDeclaration> aliasPropertyDeclarations;
|
||||||
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
|
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
|
||||||
|
std::vector<Prototype> relinkablePrototypes;
|
||||||
|
|
||||||
auto callback = [&](long long typeId) {
|
auto callback = [&](long long typeId) {
|
||||||
deleteType(TypeId{typeId}, aliasPropertyDeclarations, relinkablePropertyDeclarations);
|
deleteType(TypeId{typeId},
|
||||||
|
aliasPropertyDeclarations,
|
||||||
|
relinkablePropertyDeclarations,
|
||||||
|
relinkablePrototypes);
|
||||||
return Sqlite::CallbackControl::Continue;
|
return Sqlite::CallbackControl::Continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1037,7 +1094,7 @@ private:
|
|||||||
{
|
{
|
||||||
if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); },
|
if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); },
|
||||||
type.prototype)) {
|
type.prototype)) {
|
||||||
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{});
|
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{}, Sqlite::NullValue{});
|
||||||
} else {
|
} else {
|
||||||
auto [prototypeId, prototypeTypeNameId] = fetchTypeIdByNameUngarded(type.prototype,
|
auto [prototypeId, prototypeTypeNameId] = fetchTypeIdByNameUngarded(type.prototype,
|
||||||
type.sourceId);
|
type.sourceId);
|
||||||
@@ -1045,7 +1102,7 @@ private:
|
|||||||
if (!prototypeId)
|
if (!prototypeId)
|
||||||
throw TypeNameDoesNotExists{};
|
throw TypeNameDoesNotExists{};
|
||||||
|
|
||||||
updatePrototypeStatement.write(&type.typeId, &prototypeId);
|
updatePrototypeStatement.write(&type.typeId, &prototypeId, &prototypeTypeNameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1307,6 +1364,7 @@ private:
|
|||||||
typesTable,
|
typesTable,
|
||||||
Sqlite::ForeignKeyAction::NoAction,
|
Sqlite::ForeignKeyAction::NoAction,
|
||||||
Sqlite::ForeignKeyAction::Restrict);
|
Sqlite::ForeignKeyAction::Restrict);
|
||||||
|
typesTable.addColumn("prototypeNameId");
|
||||||
|
|
||||||
typesTable.addUniqueIndex({importIdColumn, typesNameColumn});
|
typesTable.addUniqueIndex({importIdColumn, typesNameColumn});
|
||||||
|
|
||||||
@@ -1460,8 +1518,8 @@ public:
|
|||||||
"CONFLICT DO UPDATE SET prototypeId=excluded.prototypeId, "
|
"CONFLICT DO UPDATE SET prototypeId=excluded.prototypeId, "
|
||||||
"accessSemantics=excluded.accessSemantics, sourceId=excluded.sourceId RETURNING typeId",
|
"accessSemantics=excluded.accessSemantics, sourceId=excluded.sourceId RETURNING typeId",
|
||||||
database};
|
database};
|
||||||
WriteStatement updatePrototypeStatement{"UPDATE types SET prototypeId=?2 WHERE typeId=?1",
|
WriteStatement updatePrototypeStatement{
|
||||||
database};
|
"UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1", database};
|
||||||
mutable ReadStatement<1> selectTypeIdByExportedNameStatement{
|
mutable ReadStatement<1> selectTypeIdByExportedNameStatement{
|
||||||
"SELECT typeId FROM typeNames WHERE name=?1 AND kind=1", database};
|
"SELECT typeId FROM typeNames WHERE name=?1 AND kind=1", database};
|
||||||
mutable ReadStatement<1> selectPrototypeIdStatement{
|
mutable ReadStatement<1> selectPrototypeIdStatement{
|
||||||
@@ -1753,6 +1811,13 @@ public:
|
|||||||
ReadStatement<1> selectPropertyDeclarationIdStatement{
|
ReadStatement<1> selectPropertyDeclarationIdStatement{
|
||||||
"SELECT propertyDeclarationId FROM propertyDeclarations WHERE propertyDeclarationId=?",
|
"SELECT propertyDeclarationId FROM propertyDeclarations WHERE propertyDeclarationId=?",
|
||||||
database};
|
database};
|
||||||
|
ReadWriteStatement<3> updatePrototypeToNullStatement{
|
||||||
|
"UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
|
||||||
|
"typeId, prototypeNameId, sourceId",
|
||||||
|
database};
|
||||||
|
ReadStatement<1> selectTypeIdStatement{"SELECT typeId FROM types WHERE typeId=?", database};
|
||||||
|
WriteStatement updateTypePrototypeStatement{
|
||||||
|
"UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1", database};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -1034,7 +1034,7 @@ std::ostream &operator<<(std::ostream &out, const Type &type)
|
|||||||
{
|
{
|
||||||
using Utils::operator<<;
|
using Utils::operator<<;
|
||||||
return out << "(import: " << type.importId << ", typename: \"" << type.typeName
|
return out << "(import: " << type.importId << ", typename: \"" << type.typeName
|
||||||
<< "\", prototype: \"" << type.prototype << "\", " << type.accessSemantics
|
<< "\", prototype: " << type.prototype << ", " << type.accessSemantics
|
||||||
<< ", source: " << type.sourceId << ", exports: " << type.exportedTypes
|
<< ", source: " << type.sourceId << ", exports: " << type.exportedTypes
|
||||||
<< ", properties: " << type.propertyDeclarations
|
<< ", properties: " << type.propertyDeclarations
|
||||||
<< ", functions: " << type.functionDeclarations
|
<< ", functions: " << type.functionDeclarations
|
||||||
|
@@ -1191,7 +1191,7 @@ TEST_F(ProjectStorageSlowTest, BreakingPrototypeChainByDeletingBaseComponentThro
|
|||||||
types.pop_back();
|
types.pop_back();
|
||||||
|
|
||||||
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
|
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
|
||||||
Sqlite::ConstraintPreventsModification);
|
QmlDesigner::TypeNameDoesNotExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddPropertyDeclarations)
|
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddPropertyDeclarations)
|
||||||
@@ -3005,4 +3005,69 @@ TEST_F(ProjectStorageSlowTest, DoNotRelinkAliasPropertyTypeDoesNotExists)
|
|||||||
|
|
||||||
ASSERT_THROW(storage.synchronizeTypes({}, {sourceId1}), QmlDesigner::TypeNameDoesNotExists);
|
ASSERT_THROW(storage.synchronizeTypes({}, {sourceId1}), QmlDesigner::TypeNameDoesNotExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeName)
|
||||||
|
{
|
||||||
|
Storage::Types types{createTypes()};
|
||||||
|
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
|
||||||
|
types[0].prototype = Storage::ExportedType{"Object"};
|
||||||
|
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||||
|
types[1].typeName = "QObject3";
|
||||||
|
|
||||||
|
storage.synchronizeTypes({types[1]}, {sourceId2});
|
||||||
|
|
||||||
|
ASSERT_THAT(storage.fetchTypes(),
|
||||||
|
Contains(IsStorageType(importId2,
|
||||||
|
"QQuickItem",
|
||||||
|
Storage::NativeType{"QObject3"},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeImportId)
|
||||||
|
{
|
||||||
|
Storage::Types types{createTypes()};
|
||||||
|
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||||
|
types[1].importId = importId2;
|
||||||
|
|
||||||
|
storage.synchronizeTypes({types[1]}, {sourceId2});
|
||||||
|
|
||||||
|
ASSERT_THAT(storage.fetchTypes(),
|
||||||
|
Contains(IsStorageType(importId2,
|
||||||
|
"QQuickItem",
|
||||||
|
Storage::NativeType{"QObject"},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeNameAndImportId)
|
||||||
|
{
|
||||||
|
Storage::Types types{createTypes()};
|
||||||
|
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
|
||||||
|
types[0].prototype = Storage::ExportedType{"Object"};
|
||||||
|
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||||
|
types[1].importId = importId2;
|
||||||
|
types[1].typeName = "QObject3";
|
||||||
|
|
||||||
|
storage.synchronizeTypes({types[1]}, {sourceId2});
|
||||||
|
|
||||||
|
ASSERT_THAT(storage.fetchTypes(),
|
||||||
|
Contains(IsStorageType(importId2,
|
||||||
|
"QQuickItem",
|
||||||
|
Storage::NativeType{"QObject3"},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeNameThrowsForWrongNativePrototupeTypeName)
|
||||||
|
{
|
||||||
|
Storage::Types types{createTypes()};
|
||||||
|
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
|
||||||
|
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||||
|
types[1].typeName = "QObject3";
|
||||||
|
|
||||||
|
ASSERT_THROW(storage.synchronizeTypes({types[1]}, {sourceId2}),
|
||||||
|
QmlDesigner::TypeNameDoesNotExists);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user