From a9f9fac76d8e79f9074d8f1897dcb534474b555f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 18 May 2022 16:33:43 +0200 Subject: [PATCH] QmlDesigner: Alias support in the QmlDocumentParser We have to adjust the storage to support indirect alias like: Item { property alias textSize: foo.text.size Text{ id: foo } } Task-number: QDS-7117 Change-Id: I0cf915ed710800dfa46ceff5d823e48ef45ec906 Change-Id: I11df6077ebbda208114b5e6f9c4eece611740096 Reviewed-by: Thomas Hartmann --- .../projectstorage/qmldocumentparser.cpp | 38 ++++++-- .../unit/unittest/qmldocumentparser-test.cpp | 87 +++++++++++++++++++ 2 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 5070387865e..4bbeebddf81 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -27,6 +27,7 @@ #include "qmldocumentparser.h" #include "projectstorage.h" +#include "projectstorageexceptions.h" #include "sourcepathcache.h" #include @@ -161,14 +162,33 @@ Storage::ImportedTypeName createImportedTypeName(const QStringView rawtypeName, } void addPropertyDeclarations(Storage::Type &type, - const QmlDom::QmlObject &rootObject, - const QualifiedImports &qualifiedImports) + QmlDom::QmlObject &rootObject, + const QualifiedImports &qualifiedImports, + QmlDom::DomItem &fileItem) { for (const QmlDom::PropertyDefinition &propertyDeclaration : rootObject.propertyDefs()) { - type.propertyDeclarations.emplace_back(Utils::SmallString{propertyDeclaration.name}, - createImportedTypeName(propertyDeclaration.typeName, - qualifiedImports), - Storage::PropertyDeclarationTraits::None); + if (propertyDeclaration.isAlias()) { + auto rootObjectItem = fileItem.copy(&rootObject); + auto property = rootObjectItem.bindings() + .key(propertyDeclaration.name) + .index(0) + .field(QmlDom::Fields::value); + auto resolvedAlias = rootObject.resolveAlias(rootObjectItem, + property.ownerAs()); + if (resolvedAlias.valid()) { + type.propertyDeclarations.emplace_back(Utils::SmallString{propertyDeclaration.name}, + createImportedTypeName(resolvedAlias.typeName, + qualifiedImports), + Storage::PropertyDeclarationTraits::None, + Utils::SmallString{ + resolvedAlias.accessedPath.join('.')}); + } + } else { + type.propertyDeclarations.emplace_back(Utils::SmallString{propertyDeclaration.name}, + createImportedTypeName(propertyDeclaration.typeName, + qualifiedImports), + Storage::PropertyDeclarationTraits::None); + } } } @@ -256,12 +276,12 @@ Storage::Type QmlDocumentParser::parse(const QString &sourceContent, return type; const auto &component = components.first(); - const auto &objects = component.objects(); + auto objects = component.objects(); if (objects.empty()) return type; - const QmlDom::QmlObject &qmlObject = objects.front(); + QmlDom::QmlObject &qmlObject = objects.front(); const auto qmlImports = qmlFile->imports(); @@ -274,7 +294,7 @@ Storage::Type QmlDocumentParser::parse(const QString &sourceContent, addImports(imports, qmlFile->imports(), sourceId, directoryPath, m_storage); - addPropertyDeclarations(type, qmlObject, qualifiedImports); + addPropertyDeclarations(type, qmlObject, qualifiedImports, file); addFunctionAndSignalDeclarations(type, qmlObject); addEnumeraton(type, component); diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp index 9835745cc33..dce80cc05e1 100644 --- a/tests/unit/unittest/qmldocumentparser-test.cpp +++ b/tests/unit/unittest/qmldocumentparser-test.cpp @@ -59,6 +59,22 @@ MATCHER_P3(IsPropertyDeclaration, && propertyDeclaration.traits == traits; } +MATCHER_P4(IsAliasPropertyDeclaration, + name, + typeName, + traits, + aliasPropertyName, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::PropertyDeclaration{name, typeName, traits, aliasPropertyName})) +{ + const Storage::PropertyDeclaration &propertyDeclaration = arg; + + return propertyDeclaration.name == name + && Storage::ImportedTypeName{typeName} == propertyDeclaration.typeName + && propertyDeclaration.traits == traits + && propertyDeclaration.aliasPropertyName == aliasPropertyName; +} + MATCHER_P2(IsFunctionDeclaration, name, returnTypeName, @@ -367,4 +383,75 @@ TEST_F(QmlDocumentParser, DISABLED_DuplicateImportsAreRemoved) Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); } +TEST_F(QmlDocumentParser, AliasItemProperties) +{ + auto type = parser.parse(R"(Example{ + property alias delegate: foo + Item { + id: foo + } + })", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(type.propertyDeclarations, + UnorderedElementsAre(IsPropertyDeclaration("delegate", + Storage::ImportedType{"Item"}, + Storage::PropertyDeclarationTraits::None))); +} + +TEST_F(QmlDocumentParser, AliasProperties) +{ + auto type = parser.parse(R"(Example{ + property alias text: foo.text2 + Item { + id: foo + } + })", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(type.propertyDeclarations, + UnorderedElementsAre(IsAliasPropertyDeclaration("text", + Storage::ImportedType{"Item"}, + Storage::PropertyDeclarationTraits::None, + "text2"))); +} + +TEST_F(QmlDocumentParser, IndirectAliasProperties) +{ + auto type = parser.parse(R"(Example{ + property alias textSize: foo.text.size + Item { + id: foo + } + })", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(type.propertyDeclarations, + UnorderedElementsAre(IsAliasPropertyDeclaration("textSize", + Storage::ImportedType{"Item"}, + Storage::PropertyDeclarationTraits::None, + "text.size"))); +} + +TEST_F(QmlDocumentParser, InvalidAliasPropertiesAreSkipped) +{ + auto type = parser.parse(R"(Example{ + property alias textSize: foo2.text.size + Item { + id: foo + } + })", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(type.propertyDeclarations, IsEmpty()); +} + } // namespace