aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2026-09-25 09:36:36 +0200
committerSami Shalayel <sami.shalayel@qt.io>2026-09-30 16:03:57 +0000
commitcb0dfff455eea02c0303a56eb4551fc8ea5f8b23 (patch)
tree3d05443cf1e69474d21963e179a7697c8c532c6b
parentdfcc4a0885fbd046010d33a243c7f6155eefbf28 (diff)
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 <ulf.hermann@qt.io>
-rw-r--r--src/qmlls/qqmllsutils.cpp140
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.cpp10
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp39
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<Location> locationFromDomItem(const DomItem &item, FileLoca
*/
std::optional<Location> 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<Binding>();
- 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<QmlFile>();
+ 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()