QmlDesigner: Workaround missing qualifications in parameter types

Task-number: QDS-6767
Change-Id: Ic1a34c206a7c4f6ec2be5bf5fbd25ead591fd008
Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
This commit is contained in:
Marco Bubke
2022-04-19 13:06:53 +02:00
parent 7c5dc97102
commit 697fd3ac5c
2 changed files with 160 additions and 28 deletions

View File

@@ -44,6 +44,29 @@ namespace QmlDom = QQmlJS::Dom;
namespace { namespace {
using ComponentWithoutNamespaces = QMap<QString, QString>;
ComponentWithoutNamespaces createComponentNameWithoutNamespaces(
const QHash<QString, QQmlJSScope::Ptr> &objects)
{
ComponentWithoutNamespaces componentWithoutNamespaces;
for (auto current = objects.keyBegin(), end = objects.keyEnd(); current != end; ++current) {
const QString &key = *current;
QString searchTerm{"::"};
auto found = std::search(key.cbegin(), key.cend(), searchTerm.cbegin(), searchTerm.cend());
if (found == key.cend())
continue;
componentWithoutNamespaces.insert(QStringView{std::next(found, 2), key.cend()}.toString(),
key);
}
return componentWithoutNamespaces;
}
void appendImports(Storage::Imports &imports, void appendImports(Storage::Imports &imports,
const QString &dependency, const QString &dependency,
SourceId sourceId, SourceId sourceId,
@@ -162,14 +185,28 @@ struct EnumerationType
using EnumerationTypes = std::vector<EnumerationType>; using EnumerationTypes = std::vector<EnumerationType>;
Storage::PropertyDeclarations createProperties(const QHash<QString, QQmlJSMetaProperty> &qmlProperties, Utils::SmallString fullyQualifiedTypeName(const QString &typeName,
const EnumerationTypes &enumerationTypes) const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{
if (auto found = componentNameWithoutNamespace.find(typeName);
found != componentNameWithoutNamespace.end())
return found.value();
return typeName;
}
Storage::PropertyDeclarations createProperties(
const QHash<QString, QQmlJSMetaProperty> &qmlProperties,
const EnumerationTypes &enumerationTypes,
const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{ {
Storage::PropertyDeclarations propertyDeclarations; Storage::PropertyDeclarations propertyDeclarations;
propertyDeclarations.reserve(Utils::usize(qmlProperties)); propertyDeclarations.reserve(Utils::usize(qmlProperties));
for (const QQmlJSMetaProperty &qmlProperty : qmlProperties) { for (const QQmlJSMetaProperty &qmlProperty : qmlProperties) {
Utils::SmallString propertyTypeName{qmlProperty.typeName()}; Utils::SmallString propertyTypeName{
fullyQualifiedTypeName(qmlProperty.typeName(), componentNameWithoutNamespace)};
auto found = find_if(enumerationTypes.begin(), enumerationTypes.end(), [&](auto &entry) { auto found = find_if(enumerationTypes.begin(), enumerationTypes.end(), [&](auto &entry) {
return entry.name == propertyTypeName; return entry.name == propertyTypeName;
}); });
@@ -185,7 +222,8 @@ Storage::PropertyDeclarations createProperties(const QHash<QString, QQmlJSMetaPr
return propertyDeclarations; return propertyDeclarations;
} }
Storage::ParameterDeclarations createParameters(const QQmlJSMetaMethod &qmlMethod) Storage::ParameterDeclarations createParameters(
const QQmlJSMetaMethod &qmlMethod, const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{ {
Storage::ParameterDeclarations parameterDeclarations; Storage::ParameterDeclarations parameterDeclarations;
@@ -198,14 +236,16 @@ Storage::ParameterDeclarations createParameters(const QQmlJSMetaMethod &qmlMetho
for (; currentName != nameEnd && currentType != typeEnd; ++currentName, ++currentType) { for (; currentName != nameEnd && currentType != typeEnd; ++currentName, ++currentType) {
parameterDeclarations.emplace_back(Utils::SmallString{*currentName}, parameterDeclarations.emplace_back(Utils::SmallString{*currentName},
Utils::SmallString{*currentType}); fullyQualifiedTypeName(*currentType,
componentNameWithoutNamespace));
} }
return parameterDeclarations; return parameterDeclarations;
} }
std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> createFunctionAndSignals( std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> createFunctionAndSignals(
const QMultiHash<QString, QQmlJSMetaMethod> &qmlMethods) const QMultiHash<QString, QQmlJSMetaMethod> &qmlMethods,
const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{ {
std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> functionAndSignalDeclarations; std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> functionAndSignalDeclarations;
Storage::FunctionDeclarations &functionsDeclarations{std::get<0>(functionAndSignalDeclarations)}; Storage::FunctionDeclarations &functionsDeclarations{std::get<0>(functionAndSignalDeclarations)};
@@ -216,11 +256,13 @@ std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> createFun
for (const QQmlJSMetaMethod &qmlMethod : qmlMethods) { for (const QQmlJSMetaMethod &qmlMethod : qmlMethods) {
if (qmlMethod.methodType() != QQmlJSMetaMethod::Type::Signal) { if (qmlMethod.methodType() != QQmlJSMetaMethod::Type::Signal) {
functionsDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()}, functionsDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()},
Utils::SmallString{qmlMethod.returnTypeName()}, fullyQualifiedTypeName(qmlMethod.returnTypeName(),
createParameters(qmlMethod)); componentNameWithoutNamespace),
createParameters(qmlMethod,
componentNameWithoutNamespace));
} else { } else {
signalDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()}, signalDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()},
createParameters(qmlMethod)); createParameters(qmlMethod, componentNameWithoutNamespace));
} }
} }
@@ -279,9 +321,7 @@ EnumerationTypes addEnumerationTypes(Storage::Types &types,
Utils::SmallStringView typeName, Utils::SmallStringView typeName,
SourceId sourceId, SourceId sourceId,
ModuleId cppModuleId, ModuleId cppModuleId,
QmlTypesParser::ProjectStorage &storage, const QHash<QString, QQmlJSMetaEnum> &qmlEnumerations)
const QHash<QString, QQmlJSMetaEnum> &qmlEnumerations,
const QList<QQmlJSScope::Export> &qmlExports)
{ {
EnumerationTypes enumerationTypes; EnumerationTypes enumerationTypes;
enumerationTypes.reserve(Utils::usize(qmlEnumerations)); enumerationTypes.reserve(Utils::usize(qmlEnumerations));
@@ -289,14 +329,11 @@ EnumerationTypes addEnumerationTypes(Storage::Types &types,
for (const QQmlJSMetaEnum &qmlEnumeration : qmlEnumerations) { for (const QQmlJSMetaEnum &qmlEnumeration : qmlEnumerations) {
Utils::SmallString enumerationName{qmlEnumeration.name()}; Utils::SmallString enumerationName{qmlEnumeration.name()};
auto fullTypeName = Utils::SmallString::join({typeName, "::", enumerationName}); auto fullTypeName = Utils::SmallString::join({typeName, "::", enumerationName});
auto &type = types.emplace_back(fullTypeName, types.emplace_back(fullTypeName,
Storage::ImportedType{Utils::SmallString{}}, Storage::ImportedType{Utils::SmallString{}},
Storage::TypeAccessSemantics::Value Storage::TypeAccessSemantics::Value | Storage::TypeAccessSemantics::IsEnum,
| Storage::TypeAccessSemantics::IsEnum, sourceId,
sourceId, createCppEnumerationExports(typeName, cppModuleId, enumerationName));
createCppEnumerationExports(typeName,
cppModuleId,
enumerationName));
enumerationTypes.emplace_back(enumerationName, std::move(fullTypeName)); enumerationTypes.emplace_back(enumerationName, std::move(fullTypeName));
} }
@@ -307,21 +344,24 @@ void addType(Storage::Types &types,
SourceId sourceId, SourceId sourceId,
ModuleId cppModuleId, ModuleId cppModuleId,
const QQmlJSScope &component, const QQmlJSScope &component,
QmlTypesParser::ProjectStorage &storage) QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{ {
auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(component.ownMethods()); auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(
component.ownMethods(), componentNameWithoutNamespace);
Utils::SmallString typeName{component.internalName()}; Utils::SmallString typeName{component.internalName()};
auto enumerations = component.ownEnumerations(); auto enumerations = component.ownEnumerations();
auto exports = component.exports(); auto exports = component.exports();
auto enumerationTypes = addEnumerationTypes( auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations);
types, typeName, sourceId, cppModuleId, storage, enumerations, exports);
types.emplace_back(Utils::SmallStringView{typeName}, types.emplace_back(Utils::SmallStringView{typeName},
Storage::ImportedType{Utils::SmallString{component.baseTypeName()}}, Storage::ImportedType{Utils::SmallString{component.baseTypeName()}},
createTypeAccessSemantics(component.accessSemantics()), createTypeAccessSemantics(component.accessSemantics()),
sourceId, sourceId,
createExports(exports, typeName, storage, cppModuleId), createExports(exports, typeName, storage, cppModuleId),
createProperties(component.ownProperties(), enumerationTypes), createProperties(component.ownProperties(),
enumerationTypes,
componentNameWithoutNamespace),
std::move(functionsDeclarations), std::move(functionsDeclarations),
std::move(signalDeclarations), std::move(signalDeclarations),
createEnumeration(enumerations)); createEnumeration(enumerations));
@@ -330,12 +370,18 @@ void addType(Storage::Types &types,
void addTypes(Storage::Types &types, void addTypes(Storage::Types &types,
const Storage::ProjectData &projectData, const Storage::ProjectData &projectData,
const QHash<QString, QQmlJSScope::Ptr> &objects, const QHash<QString, QQmlJSScope::Ptr> &objects,
QmlTypesParser::ProjectStorage &storage) QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespaces)
{ {
types.reserve(Utils::usize(objects) + types.size()); types.reserve(Utils::usize(objects) + types.size());
for (const auto &object : objects) for (const auto &object : objects)
addType(types, projectData.sourceId, projectData.moduleId, *object.get(), storage); addType(types,
projectData.sourceId,
projectData.moduleId,
*object.get(),
storage,
componentNameWithoutNamespaces);
} }
} // namespace } // namespace
@@ -352,8 +398,10 @@ void QmlTypesParser::parse(const QString &sourceContent,
if (!isValid) if (!isValid)
throw CannotParseQmlTypesFile{}; throw CannotParseQmlTypesFile{};
auto componentNameWithoutNamespaces = createComponentNameWithoutNamespaces(components);
addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId); addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId);
addTypes(types, projectData, components, m_storage); addTypes(types, projectData, components, m_storage, componentNameWithoutNamespaces);
} }
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -276,6 +276,34 @@ TEST_F(QmlTypesParser, Properties)
| Storage::PropertyDeclarationTraits::IsPointer))))); | Storage::PropertyDeclarationTraits::IsPointer)))));
} }
TEST_F(QmlTypesParser, PropertiesWithQualifiedTypes)
{
QString source{R"(import QtQuick.tooling 1.2
Module{
Component { name: "Qt::Vector" }
Component { name: "Qt::List" }
Component { name: "QObject"
Property { name: "values"; type: "Vector" }
Property { name: "items"; type: "List" }
Property { name: "values2"; type: "Qt::Vector" }
}})"};
parser.parse(source, imports, types, projectData);
ASSERT_THAT(types,
Contains(Field(&Storage::Type::propertyDeclarations,
UnorderedElementsAre(
IsPropertyDeclaration("values",
Storage::ImportedType{"Qt::Vector"},
Storage::PropertyDeclarationTraits::None),
IsPropertyDeclaration("items",
Storage::ImportedType{"Qt::List"},
Storage::PropertyDeclarationTraits::None),
IsPropertyDeclaration("values2",
Storage::ImportedType{"Qt::Vector"},
Storage::PropertyDeclarationTraits::None)))));
}
TEST_F(QmlTypesParser, Functions) TEST_F(QmlTypesParser, Functions)
{ {
QString source{R"(import QtQuick.tooling 1.2 QString source{R"(import QtQuick.tooling 1.2
@@ -318,6 +346,34 @@ TEST_F(QmlTypesParser, Functions)
Field(&Storage::FunctionDeclaration::parameters, IsEmpty())))))); Field(&Storage::FunctionDeclaration::parameters, IsEmpty()))))));
} }
TEST_F(QmlTypesParser, FunctionsWithQualifiedTypes)
{
QString source{R"(import QtQuick.tooling 1.2
Module{
Component { name: "Qt::Vector" }
Component { name: "Qt::List" }
Component { name: "QObject"
Method {
name: "values"
Parameter { name: "values"; type: "Vector" }
Parameter { name: "items"; type: "List" }
Parameter { name: "values2"; type: "Qt::Vector" }
}
}})"};
parser.parse(source, imports, types, projectData);
ASSERT_THAT(types,
Contains(
Field(&Storage::Type::functionDeclarations,
UnorderedElementsAre(AllOf(
IsFunctionDeclaration("values", ""),
Field(&Storage::FunctionDeclaration::parameters,
UnorderedElementsAre(IsParameter("values", "Qt::Vector"),
IsParameter("items", "Qt::List"),
IsParameter("values2", "Qt::Vector"))))))));
}
TEST_F(QmlTypesParser, Signals) TEST_F(QmlTypesParser, Signals)
{ {
QString source{R"(import QtQuick.tooling 1.2 QString source{R"(import QtQuick.tooling 1.2
@@ -357,6 +413,34 @@ TEST_F(QmlTypesParser, Signals)
IsParameter("args", "QQmlV4Function")))))))); IsParameter("args", "QQmlV4Function"))))))));
} }
TEST_F(QmlTypesParser, SignalsWithQualifiedTypes)
{
QString source{R"(import QtQuick.tooling 1.2
Module{
Component { name: "Qt::Vector" }
Component { name: "Qt::List" }
Component { name: "QObject"
Signal {
name: "values"
Parameter { name: "values"; type: "Vector" }
Parameter { name: "items"; type: "List" }
Parameter { name: "values2"; type: "Qt::Vector" }
}
}})"};
parser.parse(source, imports, types, projectData);
ASSERT_THAT(types,
Contains(
Field(&Storage::Type::signalDeclarations,
UnorderedElementsAre(AllOf(
IsSignalDeclaration("values"),
Field(&Storage::SignalDeclaration::parameters,
UnorderedElementsAre(IsParameter("values", "Qt::Vector"),
IsParameter("items", "Qt::List"),
IsParameter("values2", "Qt::Vector"))))))));
}
TEST_F(QmlTypesParser, Enumerations) TEST_F(QmlTypesParser, Enumerations)
{ {
QString source{R"(import QtQuick.tooling 1.2 QString source{R"(import QtQuick.tooling 1.2