forked from qt-creator/qt-creator
QmlDesigner: Notify error for bases cycle
When a cyclic dependency occurs within the code model, the synchronization process halts, leading to inconsistencies in type data across components. This desynchronization can cause unexpected behavior or errors during runtime. Currently, the cycle must be manually resolved by the user to restore proper synchronization. Future improvements may include automated cycle detection and resolution mechanisms. Change-Id: I7365122fab1912b20e1ae34e7ee4fd7137fb637e Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -34,7 +34,7 @@ public:
|
||||
using IsBasicId = std::true_type;
|
||||
using DatabaseType = long long;
|
||||
|
||||
constexpr explicit SourceId() = default;
|
||||
constexpr SourceId() = default;
|
||||
|
||||
static constexpr SourceId create(DirectoryPathId directoryPathId, FileNameId fileNameId)
|
||||
{
|
||||
|
@@ -132,6 +132,8 @@ struct ProjectStorage::Statements
|
||||
"defaultPropertyId=propertyDeclarationId "
|
||||
"WHERE t.typeId=?",
|
||||
database};
|
||||
mutable Sqlite::ReadStatement<2, 1> selectTypeNameAndSourceIdByTypeIdStatement{
|
||||
"SELECT name, sourceId FROM types WHERE typeId=?", database};
|
||||
mutable Sqlite::ReadStatement<5, 1> selectExportedTypesByTypeIdStatement{
|
||||
"SELECT moduleId, typeId, name, majorVersion, minorVersion "
|
||||
"FROM exportedTypeNames "
|
||||
@@ -4428,9 +4430,13 @@ void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const
|
||||
category(),
|
||||
keyValue("type id", typeId)};
|
||||
|
||||
auto callback = [=](TypeId currentTypeId) {
|
||||
if (typeId == currentTypeId)
|
||||
auto callback = [&](TypeId currentTypeId) {
|
||||
if (typeId == currentTypeId) {
|
||||
auto [name, sourceId] = s->selectTypeNameAndSourceIdByTypeIdStatement
|
||||
.value<std::tuple<Utils::SmallString, SourceId>>(typeId);
|
||||
errorNotifier->prototypeCycle(name, sourceId);
|
||||
throw PrototypeChainCycle{};
|
||||
}
|
||||
};
|
||||
|
||||
s->selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId);
|
||||
|
@@ -32,6 +32,7 @@ public:
|
||||
= 0;
|
||||
|
||||
virtual void qmltypesFileMissing(QStringView qmltypesPath) = 0;
|
||||
virtual void prototypeCycle(Utils::SmallStringView typeName, SourceId typeSourceId) = 0;
|
||||
|
||||
protected:
|
||||
~ProjectStorageErrorNotifierInterface() = default;
|
||||
|
@@ -89,4 +89,13 @@ void ProjectStorageErrorNotifier::qmltypesFileMissing(QStringView qmltypesPath)
|
||||
qmltypesPath);
|
||||
}
|
||||
|
||||
void ProjectStorageErrorNotifier::prototypeCycle(Utils::SmallStringView typeName, SourceId typeSourceId)
|
||||
{
|
||||
const QString typeNameString{typeName};
|
||||
|
||||
logIssue(ProjectExplorer::Task::Error,
|
||||
Tr::tr("Prototype cycle detected for type %1 in %2.").arg(typeNameString),
|
||||
m_pathCache.sourcePath(typeSourceId));
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -28,6 +28,7 @@ public:
|
||||
SourceId qmlDocumentSourceId,
|
||||
SourceId qmldirSourceId) override;
|
||||
void qmltypesFileMissing(QStringView qmltypesPath) override;
|
||||
void prototypeCycle(Utils::SmallStringView typeName, SourceId typeSourceId) override;
|
||||
|
||||
private:
|
||||
PathCacheType &m_pathCache;
|
||||
|
@@ -33,4 +33,8 @@ public:
|
||||
QmlDesigner::SourceId qmldirSourceId),
|
||||
(override));
|
||||
MOCK_METHOD(void, qmltypesFileMissing, (QStringView qmltypesPath), (override));
|
||||
MOCK_METHOD(void,
|
||||
prototypeCycle,
|
||||
(Utils::SmallStringView typeName, QmlDesigner::SourceId typeSourceId),
|
||||
(override));
|
||||
};
|
||||
|
@@ -4448,6 +4448,32 @@ TEST_F(ProjectStorage, throw_for_prototype_chain_cycles)
|
||||
QmlDesigner::PrototypeChainCycle);
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, notifies_error_for_prototype_chain_cycles)
|
||||
{
|
||||
auto package{createSimpleSynchronizationPackage()};
|
||||
package.types[1].prototype = Storage::Synchronization::ImportedType{"Object2"};
|
||||
package.types.push_back(Storage::Synchronization::Type{
|
||||
"QObject2",
|
||||
Storage::Synchronization::ImportedType{"Item"},
|
||||
Storage::Synchronization::ImportedType{},
|
||||
TypeTraitsKind::Reference,
|
||||
sourceId3,
|
||||
{Storage::Synchronization::ExportedType{pathToModuleId, "Object2"},
|
||||
Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}});
|
||||
package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2);
|
||||
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
|
||||
package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3);
|
||||
|
||||
EXPECT_CALL(errorNotifierMock, prototypeCycle(Eq("QObject2"), sourceId3));
|
||||
|
||||
EXPECT_ANY_THROW(
|
||||
storage.synchronize(SynchronizationPackage{package.imports,
|
||||
package.types,
|
||||
{sourceId1, sourceId2, sourceId3},
|
||||
package.moduleDependencies,
|
||||
package.updatedModuleDependencySourceIds}));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, throw_for_extension_chain_cycles)
|
||||
{
|
||||
auto package{createSimpleSynchronizationPackage()};
|
||||
|
Reference in New Issue
Block a user