forked from qt-creator/qt-creator
QmlDesigner: Detect alias cycle
An exception is thrown if an alias cycle is detected. This prevents that the designer would get in an endless loop. Task-number: QDS-4682 Change-Id: Iffbfb42d7334aa2a5490fe2830b5cfab8c8e627a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -712,7 +712,7 @@ private:
|
||||
|
||||
void linkAliasPropertyDeclarationAliasIds(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
|
||||
{
|
||||
for (auto &aliasDeclaration : aliasDeclarations) {
|
||||
for (const auto &aliasDeclaration : aliasDeclarations) {
|
||||
auto [aliasTypeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(aliasDeclaration.aliasTypeName,
|
||||
aliasDeclaration.sourceId);
|
||||
|
||||
@@ -729,7 +729,7 @@ private:
|
||||
|
||||
void updateAliasPropertyDeclarationValues(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
|
||||
{
|
||||
for (auto &aliasDeclaration : aliasDeclarations) {
|
||||
for (const auto &aliasDeclaration : aliasDeclarations) {
|
||||
updatetPropertiesDeclarationValuesOfAliasStatement.write(
|
||||
&aliasDeclaration.propertyDeclarationId);
|
||||
updatePropertyAliasDeclarationRecursivelyStatement.write(
|
||||
@@ -737,12 +737,21 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void checkAliasPropertyDeclarationCycles(const std::vector<AliasPropertyDeclaration> &aliasDeclarations)
|
||||
{
|
||||
for (const auto &aliasDeclaration : aliasDeclarations)
|
||||
checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId);
|
||||
}
|
||||
|
||||
void linkAliases(const std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
|
||||
const std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
|
||||
{
|
||||
linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations);
|
||||
linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations);
|
||||
|
||||
checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations);
|
||||
checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations);
|
||||
|
||||
updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations);
|
||||
updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations);
|
||||
}
|
||||
@@ -1183,7 +1192,7 @@ private:
|
||||
synchronizeEnumerationDeclarations(typeId, type.enumerationDeclarations);
|
||||
}
|
||||
|
||||
void checkForPrototypeChainCycle(TypeId typeId)
|
||||
void checkForPrototypeChainCycle(TypeId typeId) const
|
||||
{
|
||||
auto callback = [=](long long currentTypeId) {
|
||||
if (typeId == TypeId{currentTypeId})
|
||||
@@ -1195,6 +1204,19 @@ private:
|
||||
selectTypeIdsForPrototypeChainIdStatement.readCallback(callback, &typeId);
|
||||
}
|
||||
|
||||
void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const
|
||||
{
|
||||
auto callback = [=](long long currentPropertyDeclarationId) {
|
||||
if (propertyDeclarationId == PropertyDeclarationId{currentPropertyDeclarationId})
|
||||
throw AliasChainCycle{};
|
||||
|
||||
return Sqlite::CallbackControl::Continue;
|
||||
};
|
||||
|
||||
selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback,
|
||||
&propertyDeclarationId);
|
||||
}
|
||||
|
||||
void syncPrototypes(Storage::Type &type)
|
||||
{
|
||||
if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); },
|
||||
@@ -2015,6 +2037,16 @@ public:
|
||||
"UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE "
|
||||
"propertyDeclarationId=?1",
|
||||
database};
|
||||
mutable ReadStatement<1> selectPropertyDeclarationIdsForAliasChainStatement{
|
||||
"WITH RECURSIVE "
|
||||
" properties(propertyDeclarationId) AS ( "
|
||||
" SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE "
|
||||
" propertyDeclarationId=?1 "
|
||||
" UNION ALL "
|
||||
" SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties "
|
||||
" USING(propertyDeclarationId)) "
|
||||
"SELECT propertyDeclarationId FROM properties",
|
||||
database};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -95,4 +95,10 @@ public:
|
||||
const char *what() const noexcept override { return "There is a prototype chain cycle!"; }
|
||||
};
|
||||
|
||||
class AliasChainCycle : std::exception
|
||||
{
|
||||
public:
|
||||
const char *what() const noexcept override { return "There is a prototype chain cycle!"; }
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -3271,4 +3271,27 @@ TEST_F(ProjectStorageSlowTest, UpdateAliasesAfterChangePropertyToAlias)
|
||||
"objects"))))));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, CheckForProtoTypeCycle)
|
||||
{
|
||||
Storage::Types types{createTypesWithRecursiveAliases()};
|
||||
types[1].propertyDeclarations.clear();
|
||||
types[1].propertyDeclarations.push_back(
|
||||
Storage::PropertyDeclaration{"objects", Storage::ExportedType{"AliasItem2"}, "objects"});
|
||||
|
||||
ASSERT_THROW(storage.synchronizeTypes(types,
|
||||
{sourceId1, sourceId2, sourceId3, sourceId4, sourceId5}),
|
||||
QmlDesigner::AliasChainCycle);
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, CheckForProtoTypeCycleAfterUpdate)
|
||||
{
|
||||
Storage::Types types{createTypesWithRecursiveAliases()};
|
||||
storage.synchronizeTypes(types, {sourceId1, sourceId2, sourceId3, sourceId4, sourceId5});
|
||||
types[1].propertyDeclarations.clear();
|
||||
types[1].propertyDeclarations.push_back(
|
||||
Storage::PropertyDeclaration{"objects", Storage::ExportedType{"AliasItem2"}, "objects"});
|
||||
|
||||
ASSERT_THROW(storage.synchronizeTypes({types[1]}, {sourceId2}), QmlDesigner::AliasChainCycle);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user