From cb0dfff455eea02c0303a56eb4551fc8ea5f8b23 Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Fri, 25 Sep 2026 09:36:36 +0200 Subject: QQmlLSUtils: rewrite findTypeDefinitionOf logic with QQmlJSScope We can use the information from the QQmlJSScope instead of the DOM importing code for findTypeDefinitionOf, so rewrite it with QQmlJSScope. This might allow a future commit to get rid of the DOM importing and type resolution code which only seems to be used in QQmlLSUtils. Adapt the test, as the QQmlJSScope information is a bit different, for example instead of returning the location for 'C', we return the location of 'Item' when jumping to an inline component like: component C: Item { ... } I believe the small difference of location won't affect the user too much. Remove the functionality and the test for the "go-to-type-definition" on a method name that goes to the type definition of the return type, which is not really intuitive. Adapt the tst_qmlls_modules tests to expect the new location end ranges, for example we return the location of the `Item` on line 3 instead of returning the entire location from line 3 to 10 of the Item in Zzz.qml Task-number: QTBUG-150842 Change-Id: I3659921f04518b5e02338e57676b1010b996c8b0 Reviewed-by: Ulf Hermann --- src/qmlls/qqmllsutils.cpp | 140 ++++++++----------------- tests/auto/qmlls/modules/tst_qmlls_modules.cpp | 10 +- tests/auto/qmlls/utils/tst_qmlls_utils.cpp | 39 +++---- 3 files changed, 66 insertions(+), 123 deletions(-) diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp index 86d2ea22ca..07cd44db22 100644 --- a/src/qmlls/qqmllsutils.cpp +++ b/src/qmlls/qqmllsutils.cpp @@ -446,104 +446,56 @@ static std::optional locationFromDomItem(const DomItem &item, FileLoca */ std::optional findTypeDefinitionOf(const DomItem &object) { - DomItem typeDefinition; + auto expressionType = resolveExpressionType(object, ResolveOptions::ResolveOwnerType); - switch (object.internalKind()) { - case QQmlJS::Dom::DomType::QmlComponent: - typeDefinition = object.field(Fields::objects).index(0); - break; - case QQmlJS::Dom::DomType::QmlObject: - typeDefinition = baseObject(object); - break; - case QQmlJS::Dom::DomType::Binding: { - auto binding = object.as(); - Q_ASSERT(binding); - - // try to grab the type from the bound object - if (binding->valueKind() == BindingValueKind::Object) { - typeDefinition = baseObject(object.field(Fields::value)); - break; - } else { - // use the type of the property it is bound on for scriptexpression etc. - DomItem propertyDefinition; - const QString bindingName = binding->name(); - object.containingObject().visitLookup( - bindingName, - [&propertyDefinition](const DomItem &item) { - if (item.internalKind() == QQmlJS::Dom::DomType::PropertyDefinition) { - propertyDefinition = item; - return false; - } - return true; - }, - LookupType::PropertyDef); - typeDefinition = propertyDefinition.field(Fields::type).proceedToScope(); - break; - } - Q_UNREACHABLE(); - } - case QQmlJS::Dom::DomType::Id: - typeDefinition = object.field(Fields::referredObject).proceedToScope(); - break; - case QQmlJS::Dom::DomType::PropertyDefinition: - case QQmlJS::Dom::DomType::MethodParameter: - case QQmlJS::Dom::DomType::MethodInfo: - typeDefinition = object.field(Fields::type).proceedToScope(); - break; - case QQmlJS::Dom::DomType::ScriptIdentifierExpression: { - if (DomItem type = object.filterUp( - [](DomType k, const DomItem &) { return k == DomType::ScriptType; }, - FilterUpOptions::ReturnOuter)) { - - const QString name = fieldMemberExpressionBits(type.field(Fields::typeName)).join(u'.'); - switch (type.directParent().internalKind()) { - case DomType::QmlObject: - // is the type name of a QmlObject, like Item in `Item {...}` - typeDefinition = baseObject(type.directParent()); - break; - case DomType::QmlComponent: - typeDefinition = type.directParent(); - return locationFromDomItem(typeDefinition, FileLocationRegion::IdentifierRegion); - break; - default: - // is a type annotation, like Item in `function f(x: Item) { ... }` - typeDefinition = object.path(Paths::lookupTypePath(name)); - if (typeDefinition.internalKind() == DomType::Export) { - typeDefinition = typeDefinition.field(Fields::type).get(); - } - } - break; - } - if (DomItem id = object.filterUp( - [](DomType k, const DomItem &) { return k == DomType::Id; }, - FilterUpOptions::ReturnOuter)) { - - typeDefinition = id.field(Fields::referredObject).proceedToScope(); - break; - } - - auto scope = resolveExpressionType( - object, ResolveOptions::ResolveActualTypeForFieldMemberExpression); - if (!scope || !scope->semanticScope) - return {}; - - if (scope->type == QmlObjectIdIdentifier) { - return Location::tryFrom(scope->semanticScope->filePath(), - scope->semanticScope->sourceLocation(), object); - } + if (!expressionType) + return { }; - typeDefinition = sourceLocationToDomItem(object.containingFile(), - scope->semanticScope->sourceLocation()); - return locationFromDomItem(typeDefinition.component(), - FileLocationRegion::IdentifierRegion); + QQmlJSScope::ConstPtr scope = expressionType->semanticScope; + if (!scope) + return { }; + switch (expressionType->type) { + case PropertyIdentifier: { + if (!expressionType->name) + return { }; + QQmlJSScope::ConstPtr propertyType = scope->property(*expressionType->name).type(); + if (!propertyType) + return { }; + return Location::tryFrom(propertyType->filePath(), propertyType->sourceLocation(), object); } - default: - qDebug() << "QQmlLSUtils::findTypeDefinitionOf: Found unimplemented Type" - << object.internalKindStr(); - return {}; + case JavaScriptIdentifier: { + if (!expressionType->name) + return { }; + auto jsIdentifier = scope->jsIdentifier(*expressionType->name); + if (!jsIdentifier || !jsIdentifier->typeName) + return { }; + auto qmlFile = object.fileObject().as(); + if (!qmlFile) + return { }; + auto definition = qmlFile->typeResolver()->typeForName(*jsIdentifier->typeName); + if (!definition) + return { }; + return Location::tryFrom(definition->filePath(), definition->sourceLocation(), object); } - - return locationFromDomItem(typeDefinition, FileLocationRegion::MainRegion); + case PropertyChangedSignalIdentifier: + case PropertyChangedHandlerIdentifier: + case AttachedTypeIdentifier: + case AttachedTypeIdentifierInBindingTarget: + case GroupedPropertyIdentifier: + case QmlObjectIdIdentifier: + case NotAnIdentifier: + case SignalIdentifier: + case SignalHandlerIdentifier: + case MethodIdentifier: + case LambdaMethodIdentifier: + case SingletonIdentifier: + case QualifiedModuleIdentifier: + case EnumeratorIdentifier: + case EnumeratorValueIdentifier: + case QmlComponentIdentifier: + return Location::tryFrom(scope->filePath(), scope->sourceLocation(), object); + } + Q_UNREACHABLE_RETURN({ }); } static bool findDefinitionFromItem(const DomItem &item, const QString &name) diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp index 22260d1572..4f08ba6c3e 100644 --- a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp +++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp @@ -605,13 +605,13 @@ void tst_qmlls_modules::goToTypeDefinition_data() const QString zzzPath = u"completions/Zzz.qml"_s; const QString someBasePath = u"completions/SomeBase.qml"_s; - QTest::newRow("BaseOfYyy") << yyyPath << 3 << 1 << zzzPath << 2 << 0 << 9 << 1; - QTest::newRow("BaseOfIC") << yyyPath << 29 << 19 << zzzPath << 2 << 0 << 9 << 1; + QTest::newRow("BaseOfYyy") << yyyPath << 3 << 1 << zzzPath << 2 << 0 << 2 << 4; + QTest::newRow("BaseOfIC") << yyyPath << 29 << 19 << zzzPath << 2 << 0 << 2 << 4; - QTest::newRow("PropertyType") << yyyPath << 30 << 14 << someBasePath << 2 << 0 << 4 << 1; + QTest::newRow("PropertyType") << yyyPath << 30 << 14 << someBasePath << 2 << 0 << 2 << 4; - QTest::newRow("TypeInIC") << yyyPath << 29 << 36 << someBasePath << 2 << 0 << 4 << 1; - QTest::newRow("ICTypeDefinition") << yyyPath << 29 << 15 << yyyPath << 29 << 14 << 29 << 16; + QTest::newRow("TypeInIC") << yyyPath << 29 << 36 << someBasePath << 2 << 0 << 2 << 4; + QTest::newRow("ICTypeDefinition") << yyyPath << 29 << 15 << yyyPath << 29 << 18 << 29 << 21; } void tst_qmlls_modules::goToTypeDefinition() diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp index 979625718d..dc2bb58d3f 100644 --- a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp +++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp @@ -345,19 +345,19 @@ void tst_qmlls_utils::findTypeDefinitionFromLocation_data() // pass this as file when no result is expected, e.g. for type definition of "var". QTest::addRow("onCProperty") << file1Qml << 11 << 16 << firstResult << outOfOne << file1Qml << 7 - << positionAfterOneIndent; + << 18; QTest::addRow("onCProperty2") << file1Qml << 28 << 37 << firstResult << outOfOne << file1Qml - << 7 << positionAfterOneIndent; + << 7 << 18; QTest::addRow("onCProperty3") << file1Qml << 28 << 35 << firstResult << outOfOne << file1Qml - << 7 << positionAfterOneIndent; + << 7 << 18; QTest::addRow("onCBinding") << file1Qml << 46 << 8 << firstResult << outOfOne << file1Qml << 7 - << positionAfterOneIndent; + << 18; QTest::addRow("onDefaultBinding") << file1Qml << 16 << positionAfterOneIndent << firstResult - << outOfOne << file1Qml << 7 << positionAfterOneIndent; + << outOfOne << file1Qml << 7 << 18; QTest::addRow("onDefaultBindingId") << file1Qml << 16 << 28 << firstResult << outOfOne << file1Qml << 16 << 20; @@ -378,42 +378,33 @@ void tst_qmlls_utils::findTypeDefinitionFromLocation_data() QTest::addRow("onWhitespaceBetweenCAndD") << file1Qml << 17 << 24 << firstResult << outOfOne << noResultExpected << -1 << -1; - QTest::addRow("ic") << file1Qml << 15 << 15 << firstResult << outOfOne << file1Qml << 15 << 15; + QTest::addRow("ic") << file1Qml << 15 << 15 << firstResult << outOfOne << file1Qml << 15 << 18; QTest::addRow("icBase") << file1Qml << 15 << 20 << firstResult << outOfOne << u"TODO: file location for C++ defined types?"_s << -1 << -1; QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne << file1Qml << -1 << 18; - // TODO: type definition of function = type definition of return type? - // if not, this might need fixing: - // currently, asking the type definition of the "function" keyword returns - // the type definitin of the return type (when available). - QTest::addRow("function-keyword") << file1Qml << 33 << 5 << firstResult << outOfOne << file1Qml - << 7 << positionAfterOneIndent; QTest::addRow("function-parameter-builtin") << file1Qml << 33 << 20 << firstResult << outOfOne << file1Qml << -1 << -1; - QTest::addRow("function-parameter-item") << file1Qml << 33 << 36 << firstResult << outOfOne - << file1Qml << 7 << positionAfterOneIndent; + QTest::addRow("function-parameter-item") + << file1Qml << 33 << 36 << firstResult << outOfOne << file1Qml << 7 << 18; - QTest::addRow("function-return") << file1Qml << 33 << 41 << firstResult << outOfOne << file1Qml - << 7 << positionAfterOneIndent; - - QTest::addRow("void-function") - << file1Qml << 36 << 17 << firstResult << outOfOne << noResultExpected << -1 << -1; + QTest::addRow("function-return") + << file1Qml << 33 << 41 << firstResult << outOfOne << file1Qml << 7 << 18; QTest::addRow("rectangle-property") << file1Qml << 44 << 31 << firstResult << outOfOne << "TODO: c++ type location" << -1 << -1; QTest::addRow("functionParameterICUsage") - << file1Qml << 34 << 16 << firstResult << outOfOne << file1Qml << 7 << 15; + << file1Qml << 34 << 16 << firstResult << outOfOne << file1Qml << 7 << 18; QTest::addRow("ICBindingUsage") - << file1Qml << 47 << 21 << firstResult << outOfOne << file1Qml << 7 << 15; + << file1Qml << 47 << 21 << firstResult << outOfOne << file1Qml << 7 << 18; QTest::addRow("ICBindingUsage2") - << file1Qml << 49 << 11 << firstResult << outOfOne << file1Qml << 7 << 15; + << file1Qml << 49 << 11 << firstResult << outOfOne << file1Qml << 7 << 18; QTest::addRow("ICBindingUsage3") - << file1Qml << 52 << 17 << firstResult << outOfOne << file1Qml << 7 << 15; + << file1Qml << 52 << 17 << firstResult << outOfOne << file1Qml << 7 << 18; QTest::addRow("ICBindingUsageFromLambda") - << file1Qml << 58 << 23 << firstResult << outOfOne << file1Qml << 7 << 15; + << file1Qml << 58 << 23 << firstResult << outOfOne << file1Qml << 7 << 18; } void tst_qmlls_utils::findTypeDefinitionFromLocation() -- cgit v1.2.3