forked from qt-creator/qt-creator
QmlDesigner: Notify error for alias cycle
When a cyclic dependency is introduced through QML aliases, the code model's synchronization mechanism is disrupted. This interruption prevents the model from updating type information consistently across components, resulting in stale or incorrect type data. Such inconsistencies can lead to unpredictable behavior or runtime errors in the application. This commit documents the issue and highlights the need for user intervention to manually resolve the cycle. Change-Id: I3c8283b0bbeba0634463e783ee18d5c99d8c919a Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -786,6 +786,12 @@ struct ProjectStorage::Statements
|
||||
"FROM propertyDeclarations "
|
||||
"WHERE propertyDeclarationId=?1 LIMIT 1",
|
||||
database};
|
||||
mutable Sqlite::ReadStatement<2, 1> selectPropertyDeclarationNameAndTypeIdForPropertyDeclarationIdStatement{
|
||||
"SELECT name, typeId "
|
||||
"FROM propertyDeclarations "
|
||||
"WHERE propertyDeclarationId=?1 "
|
||||
"LIMIT 1",
|
||||
database};
|
||||
mutable Sqlite::ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{
|
||||
"WITH RECURSIVE "
|
||||
" prototypes(typeId) AS ( "
|
||||
@@ -4447,9 +4453,17 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla
|
||||
NanotraceHR::Tracer tracer{"check for alias chain cycle",
|
||||
category(),
|
||||
keyValue("property declaration id", propertyDeclarationId)};
|
||||
auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) {
|
||||
if (propertyDeclarationId == currentPropertyDeclarationId)
|
||||
auto callback = [&](PropertyDeclarationId currentPropertyDeclarationId) {
|
||||
if (propertyDeclarationId == currentPropertyDeclarationId) {
|
||||
auto [propertyName, typeId] = s->selectPropertyDeclarationNameAndTypeIdForPropertyDeclarationIdStatement
|
||||
.value<std::tuple<Utils::SmallString, TypeId>>(
|
||||
propertyDeclarationId);
|
||||
auto [typeName, sourceId] = s->selectTypeNameAndSourceIdByTypeIdStatement
|
||||
.value<std::tuple<Utils::SmallString, SourceId>>(typeId);
|
||||
errorNotifier->aliasCycle(typeName, propertyName, sourceId);
|
||||
|
||||
throw AliasChainCycle{};
|
||||
}
|
||||
};
|
||||
|
||||
s->selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback,
|
||||
|
@@ -33,6 +33,10 @@ public:
|
||||
|
||||
virtual void qmltypesFileMissing(QStringView qmltypesPath) = 0;
|
||||
virtual void prototypeCycle(Utils::SmallStringView typeName, SourceId typeSourceId) = 0;
|
||||
virtual void aliasCycle(Utils::SmallStringView typeName,
|
||||
Utils::SmallStringView propertyName,
|
||||
SourceId typeSourceId)
|
||||
= 0;
|
||||
|
||||
protected:
|
||||
~ProjectStorageErrorNotifierInterface() = default;
|
||||
|
@@ -98,4 +98,18 @@ void ProjectStorageErrorNotifier::prototypeCycle(Utils::SmallStringView typeName
|
||||
m_pathCache.sourcePath(typeSourceId));
|
||||
}
|
||||
|
||||
void ProjectStorageErrorNotifier::aliasCycle(Utils::SmallStringView typeName,
|
||||
Utils::SmallStringView propertyName,
|
||||
SourceId typeSourceId)
|
||||
{
|
||||
const QString typeNameString{typeName};
|
||||
const QString propertyNameString{propertyName};
|
||||
|
||||
logIssue(ProjectExplorer::Task::Error,
|
||||
Tr::tr("Alias cycle detected for type %1 and property %2 in %3.")
|
||||
.arg(typeNameString)
|
||||
.arg(propertyNameString),
|
||||
m_pathCache.sourcePath(typeSourceId));
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -29,6 +29,9 @@ public:
|
||||
SourceId qmldirSourceId) override;
|
||||
void qmltypesFileMissing(QStringView qmltypesPath) override;
|
||||
void prototypeCycle(Utils::SmallStringView typeName, SourceId typeSourceId) override;
|
||||
void aliasCycle(Utils::SmallStringView typeName,
|
||||
Utils::SmallStringView propertyName,
|
||||
SourceId typeSourceId) override;
|
||||
|
||||
private:
|
||||
PathCacheType &m_pathCache;
|
||||
|
@@ -37,4 +37,10 @@ public:
|
||||
prototypeCycle,
|
||||
(Utils::SmallStringView typeName, QmlDesigner::SourceId typeSourceId),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
aliasCycle,
|
||||
(Utils::SmallStringView typeName,
|
||||
Utils::SmallStringView propertyName,
|
||||
QmlDesigner::SourceId typeSourceId),
|
||||
(override));
|
||||
};
|
||||
|
@@ -4677,7 +4677,7 @@ TEST_F(ProjectStorage, update_aliases_after_change_property_to_alias)
|
||||
"objects"))))));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, check_for_proto_type_cycle_throws)
|
||||
TEST_F(ProjectStorage, check_for_alias_type_cycle_throws)
|
||||
{
|
||||
auto package{createSynchronizationPackageWithRecursiveAliases()};
|
||||
package.types[1].propertyDeclarations.clear();
|
||||
@@ -4688,7 +4688,7 @@ TEST_F(ProjectStorage, check_for_proto_type_cycle_throws)
|
||||
ASSERT_THROW(storage.synchronize(package), QmlDesigner::AliasChainCycle);
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, check_for_proto_type_cycle_after_update_throws)
|
||||
TEST_F(ProjectStorage, check_for_alias_type_cycle_after_update_throws)
|
||||
{
|
||||
auto package{createSynchronizationPackageWithRecursiveAliases()};
|
||||
storage.synchronize(package);
|
||||
@@ -4702,6 +4702,21 @@ TEST_F(ProjectStorage, check_for_proto_type_cycle_after_update_throws)
|
||||
QmlDesigner::AliasChainCycle);
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, check_for_alias_type_cycle_after_update_notifies_about_error)
|
||||
{
|
||||
auto package{createSynchronizationPackageWithRecursiveAliases()};
|
||||
storage.synchronize(package);
|
||||
package.types[1].propertyDeclarations.clear();
|
||||
package.types[1].propertyDeclarations.push_back(Storage::Synchronization::PropertyDeclaration{
|
||||
"objects", Storage::Synchronization::ImportedType{"AliasItem2"}, "objects"});
|
||||
importsSourceId2.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2);
|
||||
|
||||
EXPECT_CALL(errorNotifierMock, aliasCycle(Eq("QObject"), Eq("objects"), sourceId2));
|
||||
|
||||
EXPECT_ANY_THROW(storage.synchronize(
|
||||
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, qualified_prototype)
|
||||
{
|
||||
auto package{createSimpleSynchronizationPackage()};
|
||||
|
Reference in New Issue
Block a user