7#include <QtCore/qassert.h>
8#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
9#include <QtCore/qthreadpool.h>
10#include <QtCore/private/qduplicatetracker_p.h>
11#include <QtCore/QRegularExpression>
12#include <QtQmlDom/private/qqmldomexternalitems_p.h>
13#include <QtQmlDom/private/qqmldomtop_p.h>
14#include <QtQmlDom/private/qqmldomscriptelements_p.h>
15#include <QtQmlDom/private/qqmldom_utils_p.h>
16#include <QtQml/private/qqmlsignalnames_p.h>
17#include <QtQml/private/qqmljslexer_p.h>
18#include <QtQmlCompiler/private/qqmljsutils_p.h>
30using namespace QQmlJS::Dom;
31using namespace Qt::StringLiterals;
36 const bool isAccess = QQmlLSUtils::isFieldMemberAccess(el);
37 if (!isAccess && !QQmlLSUtils::isFieldMemberExpression(el))
40 const DomItem fieldMemberExpressionBeginning = el.filterUp(
41 [](DomType,
const DomItem &item) {
return !QQmlLSUtils::isFieldMemberAccess(item); },
42 FilterUpOptions::ReturnOuter);
43 QStringList qualifiers =
44 QQmlLSUtils::fieldMemberExpressionBits(fieldMemberExpressionBeginning, el);
47 for (
const QString &qualifier : qualifiers)
48 result.append(qualifier).append(QChar(u'.'));
53
54
55
58 return item.internalKind() == DomType::ScriptBinaryExpression
59 && item.field(Fields::operation).value().toInteger()
60 == ScriptElements::BinaryExpression::FieldMemberAccess;
64
65
66
67
70 auto parent = item.directParent();
71 if (!isFieldMemberExpression(parent))
74 DomItem rightHandSide = parent.field(Fields::right);
75 return item == rightHandSide;
79
80
81
82
85 auto parent = item.directParent();
86 if (!isFieldMemberExpression(parent))
91 const DomItem leftHandSide = parent.field(Fields::left);
92 if (item == leftHandSide)
99 const DomItem grandParent = parent.directParent();
100 return isFieldMemberExpression(grandParent) && grandParent.field(Fields::left) == parent;
104
105
106
107
108
109
110
111
114 const bool isAccess = isFieldMemberAccess(item);
115 const bool isExpression = isFieldMemberExpression(item);
118 if (!isAccess && !isExpression)
119 return { item.value().toString() };
121 const DomItem stopMarker =
122 isFieldMemberExpression(stopAtChild) ? stopAtChild : stopAtChild.directParent();
126 isAccess ? item.directParent() : (isFieldMemberExpression(item) ? item : DomItem{});
128 for (; isFieldMemberExpression(current); current = current.field(Fields::right)) {
129 result << current.field(Fields::left).value().toString();
131 if (current == stopMarker)
134 result << current.value().toString();
140
141
142
143
144
145
146
147
148
149
150
151
152
164
165
166
167
168
171 QLspSpecification::Range range;
173 range.start.line = qmlLocation.sourceLocation().startLine - 1;
174 range.start.character = qmlLocation.sourceLocation().startColumn - 1;
182
183
184
185
186
187
188
189
192 return QQmlJS::SourceLocation::offsetFrom(text, row + 1, column + 1);
196
197
198
199
200
201
202
203
204
207 auto [row, column] = QQmlJS::SourceLocation::rowAndColumnFrom(text, offset);
210 if (offset >= text.size())
219 auto smallest =
std::min_element(
221 return a.fileLocation->info().fullRegion.length
222 < b.fileLocation->info().fullRegion.length;
225 if (smallest->domItem.internalKind() == DomType::Binding) {
237 auto smallestPropertyDefinition =
std::min_element(
241 const bool aIsPropertyDefinition =
242 a.domItem.internalKind() == DomType::PropertyDefinition;
243 const bool bIsPropertyDefinition =
244 b.domItem.internalKind() == DomType::PropertyDefinition;
245 return aIsPropertyDefinition > bIsPropertyDefinition
246 && a.fileLocation->info().fullRegion.length
247 < b.fileLocation->info().fullRegion.length;
250 if (smallestPropertyDefinition->domItem.internalKind() != DomType::PropertyDefinition)
253 const auto propertyDefinitionColon =
254 smallestPropertyDefinition->fileLocation->info().regions[ColonTokenRegion];
255 const auto smallestColon = smallest->fileLocation->info().regions[ColonTokenRegion];
258 if (propertyDefinitionColon.isValid() && propertyDefinitionColon == smallestColon
259 && offsetInFile < smallestColon.begin()) {
260 return smallestPropertyDefinition;
267 qsizetype offsetInFile)
269 if (items.size() < 2)
278 QList<ItemLocation> filteredItems;
280 auto smallest = handlePropertyDefinitionAndBindingOverlap(items, offsetInFile);
282 filteredItems.append(*smallest);
284 const QQmlJS::SourceLocation smallestLoc = smallest->fileLocation->info().fullRegion;
285 const qsizetype smallestBegin = smallestLoc.begin();
286 const qsizetype smallestEnd = smallestLoc.end();
288 for (
auto it = items.begin(); it != items.end(); it++) {
292 const QQmlJS::SourceLocation itLoc = it->fileLocation->info().fullRegion;
293 const qsizetype itBegin = itLoc.begin();
294 const qsizetype itEnd = itLoc.end();
295 if (itBegin == smallestEnd || smallestBegin == itEnd) {
296 filteredItems.append(*it);
299 return filteredItems;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
320 QList<ItemLocation> itemsFound;
321 std::shared_ptr<QmlFile> filePtr = file.ownerAs<QmlFile>();
324 FileLocations::Tree t = filePtr->fileLocationsTree();
326 QString code = filePtr->code();
327 QList<ItemLocation> toDo;
328 qsizetype targetPos = textOffsetFrom(code, line, character);
329 Q_ASSERT(targetPos >= 0);
331 enum ComparisonOption { Normal, ExcludePositionAfterLast };
332 auto containsTarget = [targetPos](QQmlJS::SourceLocation l, ComparisonOption c) {
333 return l.begin() <= targetPos && targetPos < l.end() + (c == Normal ? 1 : 0);
335 if (containsTarget(t->info().fullRegion, Normal)) {
338 loc.fileLocation = t;
341 while (!toDo.isEmpty()) {
345 bool inParentButOutsideChildren =
true;
352 const ComparisonOption comparisonOption =
353 iLoc.domItem.internalKind() == DomType::ScriptBinaryExpression
354 || iLoc.domItem.directParent().internalKind()
355 == DomType::ScriptTemplateLiteral
356 ? ExcludePositionAfterLast
359 auto subEls = iLoc.fileLocation->subItems();
360 for (
auto it = subEls.begin(); it != subEls.end(); ++it) {
361 auto subLoc = it.value();
364 if (containsTarget(subLoc->info().fullRegion, comparisonOption)) {
366 subItem.domItem = iLoc.domItem.path(it.key());
367 if (!subItem.domItem) {
368 qCDebug(QQmlLSUtilsLog)
369 <<
"A DomItem child is missing or the FileLocationsTree structure does "
370 "not follow the DomItem Structure.";
375 if (subItem.domItem.internalKind() == DomType::ScriptExpression
376 && subLoc->info().fullRegion.length == 0) {
379 subItem.fileLocation = subLoc;
380 toDo.append(subItem);
381 inParentButOutsideChildren =
false;
384 if (inParentButOutsideChildren) {
385 itemsFound.append(iLoc);
390 auto filtered = filterItemsFromTextLocation(itemsFound, targetPos);
397 DomItem qmlObject = object.qmlObject();
399 if (object.internalKind() == DomType::QmlComponent || !qmlObject) {
400 prototypes = object.component()
401 .field(Fields::objects)
403 .field(QQmlJS::Dom::Fields::prototypes);
406 prototypes = qmlObject.field(QQmlJS::Dom::Fields::prototypes);
408 switch (prototypes.indexes()) {
414 qDebug() <<
"Multiple prototypes found for " << object.name() <<
", taking the first one.";
417 QQmlJS::Dom::DomItem base = prototypes.index(0).proceedToScope();
423 auto tree = FileLocations::treeOf(item);
428 QQmlJS::SourceLocation sourceLocation = FileLocations::region(tree, region);
429 if (!sourceLocation.isValid() && region != QQmlJS::Dom::MainRegion)
430 sourceLocation = FileLocations::region(tree, QQmlJS::Dom::MainRegion);
432 return Location::tryFrom(item.canonicalFilePath(), sourceLocation, item);
436
437
438
439
440
441
442
443
444
445
446
449 DomItem typeDefinition;
451 switch (object.internalKind()) {
452 case QQmlJS::Dom::DomType::QmlComponent:
453 typeDefinition = object.field(Fields::objects).index(0);
455 case QQmlJS::Dom::DomType::QmlObject:
456 typeDefinition = baseObject(object);
458 case QQmlJS::Dom::DomType::Binding: {
459 auto binding = object.as<Binding>();
463 if (binding->valueKind() == BindingValueKind::Object) {
464 typeDefinition = baseObject(object.field(Fields::value));
468 DomItem propertyDefinition;
469 const QString bindingName = binding->name();
470 object.containingObject().visitLookup(
472 [&propertyDefinition](
const DomItem &item) {
473 if (item.internalKind() == QQmlJS::Dom::DomType::PropertyDefinition) {
474 propertyDefinition = item;
479 LookupType::PropertyDef);
480 typeDefinition = propertyDefinition.field(Fields::type).proceedToScope();
485 case QQmlJS::Dom::DomType::Id:
486 typeDefinition = object.field(Fields::referredObject).proceedToScope();
488 case QQmlJS::Dom::DomType::PropertyDefinition:
489 case QQmlJS::Dom::DomType::MethodParameter:
490 case QQmlJS::Dom::DomType::MethodInfo:
491 typeDefinition = object.field(Fields::type).proceedToScope();
493 case QQmlJS::Dom::DomType::ScriptIdentifierExpression: {
494 if (DomItem type = object.filterUp(
495 [](DomType k,
const DomItem &) {
return k == DomType::ScriptType; },
496 FilterUpOptions::ReturnOuter)) {
498 const QString name = fieldMemberExpressionBits(type.field(Fields::typeName)).join(u'.');
499 switch (type.directParent().internalKind()) {
500 case DomType::QmlObject:
502 typeDefinition = baseObject(type.directParent());
504 case DomType::QmlComponent:
505 typeDefinition = type.directParent();
506 return locationFromDomItem(typeDefinition, FileLocationRegion::IdentifierRegion);
510 typeDefinition = object.path(Paths::lookupTypePath(name));
511 if (typeDefinition.internalKind() == DomType::Export) {
512 typeDefinition = typeDefinition.field(Fields::type).get();
517 if (DomItem id = object.filterUp(
518 [](DomType k,
const DomItem &) {
return k == DomType::Id; },
519 FilterUpOptions::ReturnOuter)) {
521 typeDefinition = id.field(Fields::referredObject).proceedToScope();
525 auto scope = resolveExpressionType(
526 object, ResolveOptions::ResolveActualTypeForFieldMemberExpression);
527 if (!scope || !scope->semanticScope)
531 return Location::tryFrom(scope->semanticScope->filePath(),
532 scope->semanticScope->sourceLocation(), object);
535 typeDefinition = sourceLocationToDomItem(object.containingFile(),
536 scope->semanticScope->sourceLocation());
537 return locationFromDomItem(typeDefinition.component(),
538 FileLocationRegion::IdentifierRegion);
541 qDebug() <<
"QQmlLSUtils::findTypeDefinitionOf: Found unimplemented Type"
542 << object.internalKindStr();
546 return locationFromDomItem(typeDefinition, FileLocationRegion::MainRegion);
551 if (
const QQmlJSScope::ConstPtr &scope = item.semanticScope()) {
552 qCDebug(QQmlLSUtilsLog) <<
"Searching for definition in" << item.internalKindStr();
553 if (
auto jsIdentifier = scope->ownJSIdentifier(name)) {
554 qCDebug(QQmlLSUtilsLog) <<
"Found scope" << scope->baseTypeName();
563 DomItem definitionOfItem;
564 item.visitUp([&name, &definitionOfItem](
const DomItem &i) {
565 if (findDefinitionFromItem(i, name)) {
566 definitionOfItem = i;
570 if (i.internalKind() == DomType::ScriptExpression)
575 if (definitionOfItem)
576 return definitionOfItem;
580 if (DomItem res = item.filterUp([](DomType k,
const DomItem &) {
return k == DomType::MethodInfo; },
581 FilterUpOptions::ReturnOuter)) {
582 DomItem candidate = res.field(Fields::body).field(Fields::scriptElement);
583 if (findDefinitionFromItem(candidate, name)) {
589 if (DomItem res = item.filterUp(
590 [](DomType k,
const DomItem &) {
return k == DomType::ScriptFunctionExpression; },
591 FilterUpOptions::ReturnOuter)) {
592 if (findDefinitionFromItem(res, name)) {
597 return definitionOfItem;
601
602
603
604
616
617
618
619
620
621
622
623
624
625
626
627
628
630 const QQmlJSScope::ConstPtr &owner)
632 if (owner->hasProperty(name)) {
636 if (
const auto propertyName = QQmlSignalNames::changedHandlerNameToPropertyName(name)) {
637 if (owner->hasProperty(*propertyName)) {
638 const QString signalName = *QQmlSignalNames::changedHandlerNameToSignalName(name);
639 const QQmlJSMetaMethod signal = owner->methods(signalName).front();
642 if (signal.parameterNames().size() == 0)
649 if (
const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
650 if (
auto methods = owner->methods(*signalName); !methods.isEmpty()) {
651 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
657 if (
const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
658 if (owner->hasProperty(*propertyName)) {
663 if (
auto methods = owner->methods(name); !methods.isEmpty()) {
664 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
673
674
675
676
679 const QQmlJSScope::ConstPtr &targetType)
681 QStringList namesToCheck = { name };
682 if (item.internalKind() == DomType::EnumItem || item.internalKind() == DomType::EnumDecl)
685 auto namings = resolveNameInQmlScope(name, targetType);
688 switch (namings->type) {
692 const QString propertyChangedHandler =
693 QQmlSignalNames::propertyNameToChangedHandlerName(namings->name);
694 namesToCheck.append(propertyChangedHandler);
696 const QString propertyChangedSignal =
697 QQmlSignalNames::propertyNameToChangedSignalName(namings->name);
698 namesToCheck.append(propertyChangedSignal);
704 namesToCheck.append(namings->name);
705 namesToCheck.append(QQmlSignalNames::propertyNameToChangedSignalName(namings->name));
711 namesToCheck.append(namings->name);
712 namesToCheck.append(QQmlSignalNames::propertyNameToChangedHandlerName(namings->name));
717 namesToCheck.append(QQmlSignalNames::signalNameToHandlerName(namings->name));
722 namesToCheck.append(namings->name);
732template<
typename Predicate>
734 const QQmlJSScope::ConstPtr &referrerScope, Predicate &&check)
736 QQmlJSScope::ConstPtr result;
737 QQmlJSUtils::searchBaseAndExtensionTypes(
738 referrerScope, [&](
const QQmlJSScope::ConstPtr &scope) {
750
751
752
753
754
755
757 const QString &nameToCheck)
759 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
760 return scope->hasOwnProperty(nameToCheck);
765
766
767
768
769
771 const QString &nameToCheck)
773 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
774 return scope->hasOwnProperty(nameToCheck) || scope->hasOwnMethod(nameToCheck);
779
780
781
783 const QString &nameToCheck)
785 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
786 return scope->hasOwnMethod(nameToCheck);
791
792
793
795 const QString &nameToCheck)
797 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
798 return scope->hasOwnEnumeration(nameToCheck);
803
804
805
807 const QString &nameToCheck)
809 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
810 return scope->hasOwnEnumerationKey(nameToCheck);
815
816
817
818
819
820
821
822
823
824
827 FieldFilter filter{ {},
829 { QString(), Fields::propertyInfos.toString() },
830 { QString(), Fields::defaultPropertyName.toString() },
831 { QString(), Fields::get.toString() },
838 const auto expressionType = resolveExpressionType(item, ResolveOwnerType);
844 && !expressionType->semanticScope->isInlineComponent()) {
845 result.appendFilenameUsage(expressionType->semanticScope->filePath());
848 const QStringList namesToCheck = namesOfPossibleUsages(name, item, expressionType->semanticScope);
850 const auto addLocationIfTypeMatchesTarget =
851 [&result, &expressionType, &item](
const DomItem &toBeResolved, FileLocationRegion subRegion) {
852 const auto currentType =
853 resolveExpressionType(toBeResolved, ResolveOptions::ResolveOwnerType);
857 const QQmlJSScope::ConstPtr target = expressionType->semanticScope;
858 const QQmlJSScope::ConstPtr current = currentType->semanticScope;
859 if (target == current) {
860 auto tree = FileLocations::treeOf(toBeResolved);
861 QQmlJS::SourceLocation sourceLocation;
863 sourceLocation = FileLocations::region(tree, subRegion);
864 if (!sourceLocation.isValid())
867 if (
auto location = Location::tryFrom(toBeResolved.canonicalFilePath(),
868 sourceLocation, item)) {
874 auto findUsages = [&addLocationIfTypeMatchesTarget, &name,
875 &namesToCheck](Path,
const DomItem ¤t,
bool) ->
bool {
876 bool continueForChildren =
true;
877 if (
auto scope = current.semanticScope()) {
879 if (scope->ownJSIdentifier(name)) {
883 switch (current.internalKind()) {
884 case DomType::QmlObject:
885 case DomType::Binding:
886 case DomType::MethodInfo:
887 case DomType::PropertyDefinition: {
888 const QString propertyName = current.field(Fields::name).value().toString();
889 if (namesToCheck.contains(propertyName))
890 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
891 return continueForChildren;
893 case DomType::ScriptIdentifierExpression: {
894 const QString identifierName = current.field(Fields::identifier).value().toString();
895 if (namesToCheck.contains(identifierName))
896 addLocationIfTypeMatchesTarget(current, MainRegion);
897 return continueForChildren;
899 case DomType::ScriptLiteral: {
900 const QString literal = current.field(Fields::value).value().toString();
901 if (namesToCheck.contains(literal))
902 addLocationIfTypeMatchesTarget(current, MainRegion);
903 return continueForChildren;
905 case DomType::EnumItem: {
908 const auto parentPath = current.containingObject().pathFromOwner();
909 const auto index = parentPath.last().headIndex();
911 return continueForChildren;
912 const QString enumValue = current.field(Fields::name).value().toString();
913 if (namesToCheck.contains(enumValue))
914 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
915 return continueForChildren;
917 case DomType::EnumDecl: {
920 const auto parentPath = current.pathFromOwner();
921 const auto index = parentPath.last().headIndex();
923 return continueForChildren;
924 const QString enumValue = current.field(Fields::name).value().toString();
925 if (namesToCheck.contains(enumValue))
926 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
927 return continueForChildren;
930 return continueForChildren;
933 Q_UNREACHABLE_RETURN(continueForChildren);
936 const DomItem qmlFiles = item.top().field(Fields::qmlFileWithPath);
937 const auto filter = filterForFindUsages();
938 for (
const QString &file : qmlFiles.keys()) {
939 const DomItem currentFileComponents =
940 qmlFiles.key(file).field(Fields::currentItem).field(Fields::components);
941 currentFileComponents.visitTree(Path(), emptyChildrenVisitor,
942 VisitOption::Recurse | VisitOption::VisitSelf, findUsages,
943 emptyChildrenVisitor, filter);
950 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
951 && definitionOfItem.semanticScope()->ownJSIdentifier(name).has_value(),
952 "QQmlLSUtils::locationFromJSIdentifierDefinition",
953 "JS definition does not actually define the JS identifier. "
954 "Did you obtain definitionOfItem from findJSIdentifierDefinition() ?");
955 const QQmlJS::SourceLocation location =
956 definitionOfItem.semanticScope()->ownJSIdentifier(name).value().location;
958 return Location::tryFrom(definitionOfItem.canonicalFilePath(), location, definitionOfItem);
963 qCDebug(QQmlLSUtilsLog) <<
"Looking for JS identifier with name" << name;
964 DomItem definitionOfItem = findJSIdentifierDefinition(item, name);
967 if (!definitionOfItem) {
968 qCDebug(QQmlLSUtilsLog) <<
"No defining JS-Scope found!";
969 findUsagesOfNonJSIdentifiers(item, name, result);
973 definitionOfItem.visitTree(
974 Path(), emptyChildrenVisitor, VisitOption::VisitAdopted | VisitOption::Recurse,
975 [&name, &result](Path,
const DomItem &item,
bool) ->
bool {
976 qCDebug(QQmlLSUtilsLog) <<
"Visiting a " << item.internalKindStr();
977 if (item.internalKind() == DomType::ScriptIdentifierExpression
978 && item.field(Fields::identifier).value().toString() == name) {
980 auto fileLocation = FileLocations::treeOf(item);
982 qCWarning(QQmlLSUtilsLog) <<
"Failed finding filelocation of found usage";
985 const QQmlJS::SourceLocation sourceLocation = fileLocation->info().fullRegion;
986 const QString fileName = item.canonicalFilePath();
987 if (
auto location = Location::tryFrom(fileName, sourceLocation, item))
991 QQmlJSScope::ConstPtr scope = item.semanticScope();
993 if (scope && scope != item.directParent().semanticScope()
994 && scope->ownJSIdentifier(name)) {
1003 emptyChildrenVisitor, filterForFindUsages());
1005 if (
const auto definition = locationFromJSIdentifierDefinition(definitionOfItem, name))
1013 switch (item.internalKind()) {
1014 case DomType::ScriptIdentifierExpression: {
1015 const QString name = item.field(Fields::identifier).value().toString();
1016 findUsagesHelper(item, name, result);
1019 case DomType::ScriptVariableDeclarationEntry: {
1020 const QString name = item.field(Fields::identifier).value().toString();
1021 findUsagesHelper(item, name, result);
1024 case DomType::EnumDecl:
1025 case DomType::EnumItem:
1026 case DomType::QmlObject:
1027 case DomType::PropertyDefinition:
1028 case DomType::Binding:
1029 case DomType::MethodInfo: {
1030 const QString name = item.field(Fields::name).value().toString();
1031 findUsagesHelper(item, name, result);
1034 case DomType::QmlComponent: {
1035 QString name = item.field(Fields::name).value().toString();
1038 if (
const auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1039 name = name.sliced(dotIndex + 1);
1040 findUsagesHelper(item, name, result);
1044 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
1045 <<
"was not implemented for QQmlLSUtils::findUsagesOf";
1051 if (QQmlLSUtilsLog().isDebugEnabled()) {
1052 qCDebug(QQmlLSUtilsLog) <<
"Found following usages in files:";
1053 for (
auto r : result.usagesInFile()) {
1054 qCDebug(QQmlLSUtilsLog) << r.filename() <<
" @ " << r.sourceLocation().startLine <<
":"
1055 << r.sourceLocation().startColumn <<
" with length "
1056 << r.sourceLocation().length;
1058 qCDebug(QQmlLSUtilsLog) <<
"And following usages in file names:"
1059 << result.usagesInFilename();
1066 const QString &name)
1068 auto methods = scope->methods(name);
1069 if (methods.isEmpty())
1072 const bool isSignal = methods.front().methodType() == QQmlJSMetaMethodType::Signal;
1079
1080
1081
1082
1083
1084
1085
1086
1087
1092 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1093 if (
auto type = hasMethodOrSignal(current, name)) {
1095 case ResolveOwnerType:
1096 return ExpressionType{ name, findDefiningScopeForMethod(current, name), *type };
1107 if (
const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
1108 if (
auto type = hasMethodOrSignal(current, *signalName)) {
1110 case ResolveOwnerType:
1111 return ExpressionType{ name, findDefiningScopeForMethod(current, *signalName),
1112 SignalHandlerIdentifier };
1125
1126
1127
1132 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1133 const auto resolved = resolveNameInQmlScope(propertyName, current);
1137 if (
auto property = current->property(resolved->name); property.isValid()) {
1139 case ResolveOwnerType:
1140 return ExpressionType{ propertyName,
1141 findDefiningScopeForProperty(current, resolved->name),
1144 return ExpressionType{ propertyName, property.type(), resolved->type };
1152
1153
1154
1155
1156
1157
1162 const auto bindings = referrerScope->propertyBindings(name);
1163 if (bindings.isEmpty())
1166 const auto binding = bindings.begin();
1167 const auto bindingType = binding->bindingType();
1168 const bool bindingIsAttached = bindingType == QQmlSA::BindingType::AttachedProperty;
1169 if (!bindingIsAttached && bindingType != QQmlSA::BindingType::GroupProperty)
1175 if (!bindingIsAttached && !referrerScope->hasProperty(name)
1176 && referrerScope->isNameDeferred(name)) {
1177 if (!resolverForIds)
1182 resolverForIds->typeForId(referrerScope, name,
1183 QQmlJSScopesByIdOption::AssumeComponentsAreBound),
1188 const auto typeIdentifier =
1191 const auto getScope = [bindingIsAttached, binding]() -> QQmlJSScope::ConstPtr {
1192 if (bindingIsAttached)
1193 return binding->attachedType();
1195 return binding->groupType();
1200 return ExpressionType{ name,
1206 : findDefiningScopeForProperty(referrerScope, name),
1212 Q_UNREACHABLE_RETURN({});
1216
1217
1218
1220 const QQmlJSScope::ConstPtr &scope,
const DomItem &item)
1225 const QSet<QString> specialItems = {u"QQmlConnections"_s,
1226 u"QQuickPropertyChanges"_s,
1228 u"QQuickAnchorChanges"_s};
1230 const auto special = QQmlJSUtils::searchBaseAndExtensionTypes(
1231 scope, [&specialItems](
const QQmlJSScope::ConstPtr &visitedScope) {
1232 const auto typeName = visitedScope->internalName();
1233 if (specialItems.contains(typeName))
1243 if (scope->hasOwnPropertyBindings(u"target"_s)) {
1247 DomItem current = item.qmlObject();
1248 auto target = current.bindings().key(u"target"_s).index(0);
1250 targetName = target.field(Fields::value)
1251 .field(Fields::scriptElement)
1252 .field(Fields::identifier)
1258 if (!targetName.isEmpty()) {
1260 auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
1265 return resolver->scopedType(scope, targetName);
1268 if (item.internalKind() == DomType::Binding &&
1269 item.field(Fields::bindingType).value().toInteger() ==
int(BindingType::OnBinding)) {
1274 return scope->parentScope();
1278
1279
1280
1285 const auto scope = resolver->typeForName(name);
1289 if (scope->isSingleton())
1294 if (!isFieldMemberBase(item))
1298 const DomItem rightHandSide = [&item]() {
1299 const DomItem candidate = item.directParent().field(Fields::right);
1301 if (candidate != item)
1304 return item.directParent().directParent().field(Fields::right);
1307 if (rightHandSide.internalKind() != DomType::ScriptIdentifierExpression)
1310 const QString fieldMemberAccessName = rightHandSide.value().toString();
1311 if (fieldMemberAccessName.isEmpty() || !fieldMemberAccessName.front().isLower())
1314 if (scope->attachedType()) {
1324 const QString name = item.field(Fields::identifier).value().toString();
1325 DomItem parent = item.directParent();
1326 auto owner = resolveExpressionType(parent.field(Fields::left),
1327 ResolveOptions::ResolveActualTypeForFieldMemberExpression);
1331 if (!owner->semanticScope) {
1338 const QmlFile *qmlFile = item.fileObject().as<QmlFile>();
1341 if (
auto scope = resolveTypeName(
1342 qmlFile->typeResolver(), u"%1.%2"_s.arg(*owner->name, name), item,
1352 if (
auto scope = methodFromReferrerScope(owner->semanticScope, name, options))
1355 if (
auto scope = propertyBindingFromReferrerScope(owner->semanticScope, name, options,
nullptr))
1358 if (
auto scope = propertyFromReferrerScope(owner->semanticScope, name, options))
1366 if (
const auto scope = owner->semanticScope->isComposite()
1367 ? item.goToFile(owner->semanticScope->filePath())
1368 .rootQmlObject(GoTo::MostLikely)
1370 : owner->semanticScope) {
1371 if (scope->hasEnumerationKey(name)) {
1375 else if (scope->hasEnumeration(name)) {
1381 for (
auto it = owner->semanticScope->childScopesBegin(),
1382 end = owner->semanticScope->childScopesEnd();
1384 if ((*it)->inlineComponentName() == name) {
1391 qCDebug(QQmlLSUtilsLog) <<
"Could not find identifier expression for" << item.internalKindStr();
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1408 if (functionExpression.internalKind() != DomType::ScriptFunctionExpression)
1411 const DomItem parent = functionExpression.directParent();
1412 if (parent.internalKind() != DomType::ScriptExpression)
1415 const DomItem grandParent = parent.directParent();
1416 if (grandParent.internalKind() != DomType::Binding)
1419 auto bindingType = resolveExpressionType(grandParent, ResolveOwnerType);
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1440 const std::optional<QQmlJSScope::JavaScriptIdentifier> jsIdentifier =
1441 parameterDefinition.semanticScope()->jsIdentifier(name);
1442 if (!jsIdentifier || jsIdentifier->kind != QQmlJSScope::JavaScriptIdentifier::Parameter)
1445 const DomItem handlerFunctionExpression =
1446 parameterDefinition.internalKind() == DomType::ScriptBlockStatement
1447 ? parameterDefinition.directParent()
1448 : parameterDefinition;
1451 resolveBindingIfSignalHandler(handlerFunctionExpression);
1456 return ExpressionType{};
1461 const DomItem parameters = handlerFunctionExpression[Fields::parameters];
1462 const int indexOfParameter = [¶meters, &name]() {
1463 for (
int i = 0; i < parameters.indexes(); ++i) {
1464 if (parameters[i][Fields::identifier].value().toString() == name)
1467 Q_ASSERT_X(
false,
"resolveSignalHandlerParameter",
1468 "can't find JS identifier with Parameter kind in the parameters");
1469 Q_UNREACHABLE_RETURN(-1);
1472 const std::optional<QString> signalName =
1473 QQmlSignalNames::handlerNameToSignalName(*bindingType->name);
1474 Q_ASSERT_X(signalName.has_value(),
"resolveSignalHandlerParameterType",
1475 "handlerNameToSignalName failed on a SignalHandler");
1477 const QQmlJSMetaMethod signalDefinition =
1478 bindingType->semanticScope->methods(*signalName).front();
1479 const QList<QQmlJSMetaParameter> parameterList = signalDefinition.parameters();
1482 if (parameterList.size() <= indexOfParameter)
1490 const QQmlJSScope::ConstPtr parameterType = parameterList[indexOfParameter].type();
1496
1497
1498
1499
1500
1501
1502
1503
1507 if (isFieldMemberAccess(item) || isFieldMemberExpression(item)) {
1508 return resolveFieldMemberExpressionType(item, options);
1511 const QString name = item.field(Fields::identifier).value().toString();
1513 if (
const DomItem definitionOfItem = findJSIdentifierDefinition(item, name)) {
1514 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
1515 && definitionOfItem.semanticScope()->ownJSIdentifier(name),
1516 "QQmlLSUtils::findDefinitionOf",
1517 "JS definition does not actually define the JS identifer. "
1518 "It should be empty.");
1519 if (
auto parameter = resolveSignalHandlerParameterType(definitionOfItem, name, options))
1522 const auto scope = definitionOfItem.semanticScope();
1523 return ExpressionType{ name,
1524 options == ResolveOwnerType
1526 : QQmlJSScope::ConstPtr(
1527 scope->ownJSIdentifier(name)->scope.toStrongRef()),
1528 IdentifierType::JavaScriptIdentifier };
1531 const auto referrerScope = item.nearestSemanticScope();
1535 const auto qmlFile = item.containingFile().ownerAs<QmlFile>();
1539 const auto resolver = qmlFile->typeResolver();
1544 if (
const QQmlJSScope::ConstPtr fromId =
1545 resolver->typeForId(referrerScope, name, QQmlJSScopesByIdOption::AssumeComponentsAreBound)) {
1550 if (
const auto scope = propertyBindingFromReferrerScope(
1551 referrerScope, name, options, resolver.get())) {
1556 if (
const auto scope = propertyFromReferrerScope(referrerScope, name, options))
1560 if (
auto scope = methodFromReferrerScope(referrerScope, name, options))
1563 if (resolver->seenModuleQualifiers().contains(name))
1566 if (
const auto scope = resolveTypeName(resolver, name, item, options))
1571 const QQmlJSScope::ConstPtr jsGlobal = resolver->jsGlobalObject();
1573 if (
auto scope = methodFromReferrerScope(jsGlobal, name, options)) {
1579 if (
auto scope = propertyFromReferrerScope(jsGlobal, name, options)) {
1591 const auto signalOrProperty = resolveNameInQmlScope(name, scope);
1592 if (!signalOrProperty)
1595 switch (signalOrProperty->type) {
1598 case ResolveOwnerType:
1599 return ExpressionType{ name, findDefiningScopeForProperty(scope, name),
1600 signalOrProperty->type };
1602 return ExpressionType{ name, scope->property(name).type(), signalOrProperty->type };
1604 Q_UNREACHABLE_RETURN({});
1607 case ResolveOwnerType:
1608 return ExpressionType{ name,
1609 findDefiningScopeForProperty(scope, signalOrProperty->name),
1610 signalOrProperty->type };
1613 Q_UNREACHABLE_RETURN({});
1615 Q_UNREACHABLE_RETURN({});
1622 return ExpressionType{ name, findDefiningScopeForMethod(scope, signalOrProperty->name),
1623 signalOrProperty->type };
1627 Q_UNREACHABLE_RETURN({});
1629 Q_UNREACHABLE_RETURN({});
1631 Q_UNREACHABLE_RETURN({});
1636
1637
1638
1639
1640
1641
1642
1643
1644
1648 switch (item.internalKind()) {
1649 case DomType::ScriptIdentifierExpression: {
1650 return resolveIdentifierExpressionType(item, options);
1652 case DomType::PropertyDefinition: {
1653 auto propertyDefinition = item.as<PropertyDefinition>();
1654 if (propertyDefinition && propertyDefinition->semanticScope()) {
1655 const auto &scope = propertyDefinition->semanticScope();
1661 Q_UNREACHABLE_RETURN({});
1663 Q_UNREACHABLE_RETURN({});
1667 case DomType::Binding: {
1668 auto binding = item.as<Binding>();
1670 std::optional<QQmlJSScope::ConstPtr> owner = item.qmlObject().semanticScope();
1673 const QString name = binding->name();
1678 if (
const QQmlJSScope::ConstPtr targetScope
1679 = findScopeOfSpecialItems(owner.value(), item)) {
1680 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1681 if (!signalOrProperty)
1684 case ResolveOwnerType:
1685 return ExpressionType{
1686 name, findDefiningScopeForBinding(targetScope, signalOrProperty->name),
1687 signalOrProperty->type
1691 Q_UNREACHABLE_RETURN({});
1694 if (
auto result = resolveSignalOrPropertyExpressionType(name, owner.value(), options)) {
1697 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1698 "type of a Binding.";
1703 case DomType::QmlObject: {
1704 auto object = item.as<QmlObject>();
1707 if (
auto scope = object->semanticScope()) {
1708 const auto name = item.name();
1709 const bool isComponent = name.front().isUpper();
1711 scope = scope->baseType();
1723 case DomType::QmlComponent: {
1724 auto component = item.as<QmlComponent>();
1727 const auto scope = component->semanticScope();
1731 QString name = item.name();
1732 if (
auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1733 name = name.sliced(dotIndex + 1);
1740 Q_UNREACHABLE_RETURN({});
1742 case DomType::ScriptFunctionExpression: {
1745 case DomType::MethodInfo: {
1746 const auto object = item.as<MethodInfo>();
1747 if (object && object->semanticScope()) {
1748 std::optional<QQmlJSScope::ConstPtr> scope = object->semanticScope();
1752 if (
const QQmlJSScope::ConstPtr targetScope =
1753 findScopeOfSpecialItems(scope.value()->parentScope(), item)) {
1754 const auto signalOrProperty = resolveNameInQmlScope(object->name, targetScope);
1755 if (!signalOrProperty)
1759 case ResolveOwnerType:
1760 return ExpressionType{ object->name,
1761 findDefiningScopeForMethod(targetScope,
1762 signalOrProperty->name),
1763 signalOrProperty->type };
1772 if (QQmlSA::isFunctionScope(scope.value()->scopeType()))
1773 scope = scope.value()->parentScope();
1775 if (
const auto result = resolveSignalOrPropertyExpressionType(
1776 object->name, scope.value(), options)) {
1779 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1780 "type of a MethodInfo.";
1785 case DomType::ScriptBinaryExpression: {
1786 if (isFieldMemberExpression(item)) {
1787 return resolveExpressionType(item.field(Fields::right), options);
1791 case DomType::ScriptLiteral: {
1793
1794
1795 const auto scope = item.qmlObject().semanticScope();
1796 const auto name = item.field(Fields::value).value().toString();
1797 if (
const QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(scope, item)) {
1798 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1799 if (!signalOrProperty)
1802 case ResolveOwnerType:
1803 return ExpressionType{
1804 name, findDefiningScopeForProperty(targetScope, signalOrProperty->name),
1805 signalOrProperty->type
1810 Q_UNREACHABLE_RETURN({});
1815 case DomType::EnumItem: {
1816 const QString enumValue = item.field(Fields::name).value().toString();
1817 const QQmlJSScope::ConstPtr referrerScope
1818 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1819 if (!referrerScope->hasEnumerationKey(enumValue))
1824 case ResolveActualTypeForFieldMemberExpression:
1825 case ResolveOwnerType:
1826 return ExpressionType {
1828 findDefiningScopeForEnumerationKey(referrerScope, enumValue),
1829 EnumeratorValueIdentifier
1832 Q_UNREACHABLE_RETURN({});
1834 case DomType::EnumDecl: {
1835 const QString enumName = item.field(Fields::name).value().toString();
1836 const QQmlJSScope::ConstPtr referrerScope
1837 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1838 if (!referrerScope->hasEnumeration(enumName))
1842 case ResolveActualTypeForFieldMemberExpression:
1843 case ResolveOwnerType:
1844 return ExpressionType {
1846 findDefiningScopeForEnumeration(referrerScope, enumName),
1847 EnumeratorIdentifier
1851 Q_UNREACHABLE_RETURN({});
1853 case DomType::Import: {
1855 if (!item[Fields::importId])
1857 return ExpressionType{ item[Fields::importId].value().toString(),
1859 QualifiedModuleIdentifier };
1861 case DomType::ScriptNewMemberExpression: {
1862 const auto name = item.field(Fields::base).value().toString();
1865 case DomType::ScriptCallExpression: {
1866 const DomItem callee = item.field(Fields::callee);
1867 const auto calleeExpressionType = resolveExpressionType(callee, ResolveOwnerType);
1869 if (!calleeExpressionType || !calleeExpressionType->semanticScope
1870 || !calleeExpressionType->name || calleeExpressionType->type !=
MethodIdentifier) {
1874 const auto methods =
1875 calleeExpressionType->semanticScope->methods(*calleeExpressionType->name);
1876 if (methods.isEmpty())
1879 const auto returnType = methods.front().returnType();
1883 qCDebug(QQmlLSUtilsLog) <<
"Type" << item.internalKindStr()
1884 <<
"is unimplemented in QQmlLSUtils::resolveExpressionType";
1894 auto items = QQmlLSUtils::itemsFromTextLocation(file, location.startLine - 1,
1895 location.startColumn - 1);
1896 switch (items.size()) {
1900 return items.front().domItem;
1906 auto &first = items.front();
1907 auto &second = items.back();
1908 Q_ASSERT_X(first.fileLocation->info().fullRegion.startLine
1909 == second.fileLocation->info().fullRegion.startLine,
1910 "QQmlLSUtils::findTypeDefinitionOf(DomItem)",
1911 "QQmlLSUtils::itemsFromTextLocation returned non-adjacent items.");
1912 if (first.fileLocation->info().fullRegion.startColumn
1913 > second.fileLocation->info().fullRegion.startColumn)
1914 return first.domItem;
1916 return second.domItem;
1920 qDebug() <<
"Found multiple candidates for type of scriptidentifierexpression";
1929 const DomItem enumeration = [&file, &location, &name]() -> DomItem {
1930 const DomItem enumerations = QQmlLSUtils::sourceLocationToDomItem(file, location)
1933 .field(Fields::enumerations);
1934 const QSet<QString> enumerationNames = enumerations.keys();
1935 for (
const QString &enumName : enumerationNames) {
1936 const DomItem currentKey = enumerations.key(enumName).index(0);
1937 if (enumName == name)
1939 const DomItem values = currentKey.field(Fields::values);
1940 for (
int i = 0, end = values.size(); i < end; ++i) {
1941 const DomItem currentValue = values.index(i);
1942 if (currentValue.field(Fields::name).value().toStringView() == name)
1943 return currentValue;
1949 auto fileLocation = FileLocations::treeOf(enumeration);
1954 auto regions = fileLocation->info().regions;
1956 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1957 return Location::tryFrom(enumeration.canonicalFilePath(), *it, file);
1966 DomItem owner = QQmlLSUtils::sourceLocationToDomItem(file, location).qmlObject();
1967 DomItem method = owner.field(Fields::methods).key(name).index(0);
1968 auto fileLocation = FileLocations::treeOf(method);
1972 auto regions = fileLocation->info().regions;
1974 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1975 return Location::tryFrom(method.canonicalFilePath(), *it, file);
1983 const QString &name)
1985 DomItem propertyOwner =
1986 QQmlLSUtils::sourceLocationToDomItem(file, propertyDefinitionLocation).qmlObject();
1987 DomItem propertyDefinition = propertyOwner.field(Fields::propertyDefs).key(name).index(0);
1988 auto fileLocation = FileLocations::treeOf(propertyDefinition);
1992 auto regions = fileLocation->info().regions;
1994 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1995 return Location::tryFrom(propertyDefinition.canonicalFilePath(), *it, file);
2003 return location.startLine == 0 ? QQmlJS::s_documentOrigin : location;
2007 const QStringList &headerLocations,
2008 const QQmlJS::SourceLocation &location)
2010 const TextPosition endPosition{
static_cast<
int>(location.startLine) + 1, 1 };
2011 const QFileInfo filePathInfo(fileName);
2012 if (filePathInfo.isAbsolute() && filePathInfo.exists())
2013 return Location{ fileName, location, endPosition };
2015 const QString filePath = findFilePathFromFileName(headerLocations, fileName, { });
2016 if (filePath.isEmpty()) {
2017 qCWarning(QQmlLSUtilsLog) <<
"Couldn't find file '%1'."_L1.arg(fileName);
2021 return Location{ filePath, location, endPosition };
2025
2026
2027
2028
2030 const QStringList &headerDirectories)
2034 if (scope->isComposite()) {
2035 if (
const auto result =
2036 Location::tryFrom(scope->filePath(), scope->sourceLocation(), item)) {
2040 QList<Location> result;
2041 auto locationOfTypeOrForeign = createCppTypeLocation(
2042 scope->filePath(), headerDirectories, sourceLocationOrDefault(scope->sourceLocation()));
2043 if (locationOfTypeOrForeign)
2044 result << *locationOfTypeOrForeign;
2046 auto resolvedTypeLocation = createCppTypeLocation(
2047 scope->resolvedFilePath(), headerDirectories,
2048 QQmlJS::SourceLocation{ 0u, 0u, scope->lineNumberInResolvedFile(), 1u });
2049 if (resolvedTypeLocation)
2050 result << *resolvedTypeLocation;
2055template <
typename T>
2059 return { *optional };
2065 auto resolvedExpression = resolveExpressionType(item, ResolveOptions::ResolveOwnerType);
2067 if (!resolvedExpression || !resolvedExpression->name
2068 || (!resolvedExpression->semanticScope
2070 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf: Type could not be resolved.";
2074 switch (resolvedExpression->type) {
2076 const auto jsIdentifier =
2077 resolvedExpression->semanticScope->ownJSIdentifier(*resolvedExpression->name);
2081 return optionalToList(Location::tryFrom(resolvedExpression->semanticScope->filePath(),
2082 jsIdentifier->location, item));
2087 if (!resolvedExpression->semanticScope->isComposite()) {
2088 return optionalToList(createCppTypeLocation(
2089 resolvedExpression->semanticScope->filePath(), headerDirectories,
2090 resolvedExpression->semanticScope->property(*resolvedExpression->name)
2091 .sourceLocation()));
2093 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2094 const QQmlJS::SourceLocation ownerLocation =
2095 resolvedExpression->semanticScope->sourceLocation();
2096 return optionalToList(
2097 findPropertyDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2104 if (!resolvedExpression->semanticScope->isComposite()) {
2105 const auto methods =
2106 resolvedExpression->semanticScope->methods(*resolvedExpression->name);
2107 if (methods.isEmpty())
2109 return optionalToList(
2110 createCppTypeLocation(resolvedExpression->semanticScope->filePath(),
2111 headerDirectories, methods.front().sourceLocation()));
2113 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2114 const QQmlJS::SourceLocation ownerLocation =
2115 resolvedExpression->semanticScope->sourceLocation();
2116 return optionalToList(
2117 findMethodDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2120 DomItem qmlObject = QQmlLSUtils::sourceLocationToDomItem(
2121 item.containingFile(), resolvedExpression->semanticScope->sourceLocation());
2123 const DomItem domId = qmlObject.component()
2125 .key(*resolvedExpression->name)
2127 .field(Fields::value);
2129 qCDebug(QQmlLSUtilsLog)
2130 <<
"QmlComponent in Dom structure has no id, was it misconstructed?";
2134 return optionalToList(Location::tryFrom(
2135 domId.canonicalFilePath(), FileLocations::treeOf(domId)->info().fullRegion, domId));
2137 case AttachedTypeIdentifier:
2138 return findDefinitionOfType(resolvedExpression->semanticScope->attachedType(), item,
2140 case AttachedTypeIdentifierInBindingTarget:
2141 return findDefinitionOfType(resolvedExpression->semanticScope->baseType(), item,
2143 case QmlComponentIdentifier:
2144 case SingletonIdentifier:
2145 return findDefinitionOfType(resolvedExpression->semanticScope, item, headerDirectories);
2147 const DomItem imports = item.fileObject().field(Fields::imports);
2148 for (
int i = 0; i < imports.indexes(); ++i) {
2149 if (imports[i][Fields::importId].value().toString() == resolvedExpression->name) {
2150 const auto fileLocations = FileLocations::treeOf(imports[i]);
2154 return optionalToList(Location::tryFrom(item.canonicalFilePath(),
2155 fileLocations->info().regions[IdNameRegion],
2163 if (!resolvedExpression->semanticScope->isComposite()) {
2164 const auto enumerations = resolvedExpression->semanticScope->enumerations();
2165 for (
const auto &enumeration : enumerations) {
2166 if (enumeration.hasKey(*resolvedExpression->name)
2167 || enumeration.name() == *resolvedExpression->name) {
2168 return optionalToList(createCppTypeLocation(
2169 resolvedExpression->semanticScope->filePath(), headerDirectories,
2170 sourceLocationOrDefault(QQmlJS::SourceLocation::fromQSizeType(
2171 0, 0, enumeration.lineNumber(), 1))));
2176 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2177 const QQmlJS::SourceLocation ownerLocation =
2178 resolvedExpression->semanticScope->sourceLocation();
2179 return optionalToList(
2180 findEnumDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2185 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf was not implemented for type"
2186 << resolvedExpression->type;
2189 Q_UNREACHABLE_RETURN({});
2193 const QString &name)
2195 Q_ASSERT(!name.isEmpty());
2198 QQmlJSScope::ConstPtr typeWithDefinition = type;
2199 while (typeWithDefinition && !typeWithDefinition->hasOwnProperty(name))
2200 typeWithDefinition = typeWithDefinition->baseType();
2202 if (!typeWithDefinition) {
2203 qCDebug(QQmlLSUtilsLog)
2204 <<
"QQmlLSUtils::checkNameForRename cannot find property definition,"
2208 return typeWithDefinition;
2212 const QString &name)
2214 Q_ASSERT(!name.isEmpty());
2217 QQmlJSScope::ConstPtr typeWithDefinition = type;
2218 while (typeWithDefinition && !typeWithDefinition->hasOwnMethod(name))
2219 typeWithDefinition = typeWithDefinition->baseType();
2221 if (!typeWithDefinition) {
2222 qCDebug(QQmlLSUtilsLog)
2223 <<
"QQmlLSUtils::checkNameForRename cannot find method definition,"
2227 return typeWithDefinition;
2232 switch (ownerType
.type) {
2233 case PropertyIdentifier:
2234 return propertyOwnerFrom(ownerType.semanticScope, *ownerType.name);
2236 const auto propertyName =
2237 QQmlSignalNames::changedHandlerNameToPropertyName(*ownerType.name);
2238 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2242 const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(*ownerType.name);
2243 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2245 case MethodIdentifier:
2246 case SignalIdentifier:
2247 return methodOwnerFrom(ownerType.semanticScope, *ownerType.name);
2249 const auto signalName = QQmlSignalNames::handlerNameToSignalName(*ownerType.name);
2250 return methodOwnerFrom(ownerType.semanticScope, *signalName);
2264 return ownerType.semanticScope;
2273 if (
const auto resolved = resolveExpressionType(item, ResolveOwnerType))
2274 return checkNameForRename(item, dirtyNewName, resolved);
2278 if (!isValidEcmaScriptIdentifier(dirtyNewName))
2279 return ErrorMessage{ 0, u"Invalid EcmaScript identifier!"_s };
2281 const auto userSemanticScope = item.nearestSemanticScope();
2283 if (!ownerType || !userSemanticScope) {
2284 return ErrorMessage{ 0, u"Requested item cannot be renamed."_s };
2288 switch (ownerType->type) {
2290 if (!QQmlSignalNames::isChangedSignalName(dirtyNewName)) {
2291 return ErrorMessage{ 0, u"Invalid name for a property changed signal."_s };
2296 if (!QQmlSignalNames::isChangedHandlerName(dirtyNewName)) {
2297 return ErrorMessage{ 0, u"Invalid name for a property changed handler identifier."_s };
2302 if (!QQmlSignalNames::isHandlerName(dirtyNewName)) {
2303 return ErrorMessage{ 0, u"Invalid name for a signal handler identifier."_s };
2309 if (dirtyNewName.front().isLetter() && !dirtyNewName.front().isLower()) {
2310 return ErrorMessage{ 0, u"Object id names cannot start with an upper case letter."_s };
2321 auto typeWithDefinition = expressionTypeWithDefinition(*ownerType);
2323 if (!typeWithDefinition) {
2324 return ErrorMessage{
2326 u"Renaming has not been implemented for the requested item."_s,
2331 if (!typeWithDefinition->isComposite()) {
2332 return ErrorMessage{ 0, u"Cannot rename items defined in non-QML files."_s };
2336 const QString moduleOfDefinition = ownerType->semanticScope->moduleName();
2337 const QString moduleOfCurrentItem = userSemanticScope->moduleName();
2338 if (moduleOfDefinition != moduleOfCurrentItem) {
2339 return ErrorMessage{
2341 u"Cannot rename items defined in the \"%1\" module from a usage in the \"%2\" module."_s
2342 .arg(moduleOfDefinition, moduleOfCurrentItem),
2351 switch (item.internalKind()) {
2352 case DomType::ScriptIdentifierExpression: {
2353 return item.field(Fields::identifier).value().toString();
2355 case DomType::ScriptVariableDeclarationEntry: {
2356 return item.field(Fields::identifier).value().toString();
2358 case DomType::PropertyDefinition:
2359 case DomType::Binding:
2360 case DomType::MethodInfo: {
2361 return item.field(Fields::name).value().toString();
2364 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
2365 <<
"was not implemented for QQmlLSUtils::renameUsagesOf";
2366 return std::nullopt;
2368 Q_UNREACHABLE_RETURN(
std::nullopt);
2376 switch (alternative) {
2378 return QQmlSignalNames::handlerNameToSignalName(dirtyNewName);
2381 return QQmlSignalNames::changedHandlerNameToPropertyName(dirtyNewName);
2384 return QQmlSignalNames::changedSignalNameToPropertyName(dirtyNewName);
2389 return std::nullopt;
2391 Q_UNREACHABLE_RETURN(
std::nullopt);
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2416 const Usages locations = findUsagesOf(item);
2420 auto oldName = oldNameFrom(item);
2424 QQmlJSScope::ConstPtr semanticScope;
2426 semanticScope = targetType->semanticScope;
2427 }
else if (
const auto resolved =
2428 QQmlLSUtils::resolveExpressionType(item, ResolveOptions::ResolveOwnerType)) {
2429 semanticScope = resolved->semanticScope;
2435 if (
const auto resolved = resolveNameInQmlScope(*oldName, semanticScope)) {
2436 newName = newNameFrom(dirtyNewName, resolved->type).value_or(dirtyNewName);
2437 oldName = resolved->name;
2439 newName = dirtyNewName;
2442 const qsizetype oldNameLength = oldName->length();
2443 const qsizetype oldHandlerNameLength =
2444 QQmlSignalNames::signalNameToHandlerName(*oldName).length();
2445 const qsizetype oldChangedSignalNameLength =
2446 QQmlSignalNames::propertyNameToChangedSignalName(*oldName).length();
2447 const qsizetype oldChangedHandlerNameLength =
2448 QQmlSignalNames::propertyNameToChangedHandlerName(*oldName).length();
2450 const QString newHandlerName = QQmlSignalNames::signalNameToHandlerName(newName);
2451 const QString newChangedSignalName = QQmlSignalNames::propertyNameToChangedSignalName(newName);
2452 const QString newChangedHandlerName =
2453 QQmlSignalNames::propertyNameToChangedHandlerName(newName);
2456 for (
const auto &location : locations.usagesInFile()) {
2457 const qsizetype currentLength = location.sourceLocation().length;
2459 edit.location = location;
2460 if (oldNameLength == currentLength) {
2462 edit.replacement = newName;
2464 }
else if (oldHandlerNameLength == currentLength) {
2466 edit.replacement = newHandlerName;
2468 }
else if (oldChangedSignalNameLength == currentLength) {
2470 edit.replacement = newChangedSignalName;
2472 }
else if (oldChangedHandlerNameLength == currentLength) {
2474 edit.replacement = newChangedHandlerName;
2477 qCDebug(QQmlLSUtilsLog) <<
"Found usage with wrong identifier length, ignoring...";
2480 result.appendRename(edit);
2483 for (
const auto &filename : locations.usagesInFilename()) {
2486 if (filename.endsWith(u".ui.qml"_s))
2487 extension = u".ui.qml"_s;
2488 else if (filename.endsWith(u".qml"_s))
2489 extension = u".qml"_s;
2493 QFileInfo info(filename);
2495 if (!info.isFile() || info.baseName() != oldName)
2498 const QString newFilename =
2499 QDir::cleanPath(filename +
"/.."_L1) +
'/'_L1 + newName + extension;
2500 result.appendRename({ filename, newFilename });
2507 const QQmlJS::SourceLocation &sourceLocation,
2508 const QQmlJS::Dom::DomItem &someItem)
2510 auto qmlFile = someItem.goToFile(fileName).ownerAs<QQmlJS::Dom::QmlFile>();
2512 qDebug() <<
"Could not find file" << fileName <<
"in the dom!";
2515 return Location{ fileName, sourceLocation,
2516 textRowAndColumnFrom(qmlFile->code(), sourceLocation.end()) };
2519Location Location::from(
const QString &fileName,
const QQmlJS::SourceLocation &sourceLocation,
const QString &code)
2521 return Location{ fileName, sourceLocation, textRowAndColumnFrom(code, sourceLocation.end()) };
2525 qsizetype startCharacter, qsizetype length)
2527 const auto offset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
2528 return from(fileName,
2529 QQmlJS::SourceLocation::fromQSizeType(offset, length, startLine, startCharacter),
2533Edit Edit::from(
const QString &fileName,
const QString &code, qsizetype startLine,
2534 qsizetype startCharacter, qsizetype length,
const QString &newName)
2537 rename.location = Location::from(fileName, code, startLine, startCharacter, length);
2538 rename.replacement = newName;
2544 QQmlJS::Lexer lexer(
nullptr);
2545 lexer.setCode(identifier.toString(), 0);
2546 const int token = lexer.lex();
2547 if (token !=
static_cast<
int>(QQmlJS::Lexer::T_IDENTIFIER))
2550 const int eofToken = lexer.lex();
2551 return eofToken ==
static_cast<
int>(QQmlJS::Lexer::EOF_SYMBOL);
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2570 std::pair<QString, QStringList> result{
2571 u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
2574 result.second <<
"-j"_L1 << QString::number(cmakeJobs);
2580 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2581 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2586 return m_usagesInFilename.isEmpty() && m_usagesInFile.isEmpty();
2589Usages::
Usages(
const QList<Location> &usageInFile,
const QList<QString> &usageInFilename)
2592 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2593 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2597 const QList<FileRename> &renamesInFilename)
2600 std::sort(m_renamesInFile.begin(), m_renamesInFile.end());
2601 std::sort(m_renamesInFilename.begin(), m_renamesInFilename.end());
2606 const QStringList &fileNamesToSearch,
2608 const QSet<QString> &ignoredFilePaths)
2610 if (fileNamesToSearch.isEmpty() || rootDirs.isEmpty())
2613 const qint64 maxFilesToSearch =
2614 qEnvironmentVariableIntegerValue(
"QMLLS_MAX_FILES_TO_SEARCH")
2616 qint64 visitedItems = 0;
2619 std::queue<QString> toVisit;
2620 for (
const QString &rootDir : rootDirs)
2621 toVisit.push(rootDir);
2623 while (!toVisit.empty()) {
2624 const QString current = toVisit.front();
2627 for (
const auto &entry : QDirListing{ current, QDirListing::IteratorFlag::ExcludeOther }) {
2630 if (++visitedItems > maxFilesToSearch) {
2631 qInfo(QQmlLSUtilsLog).noquote().nospace()
2632 <<
"Aborting search for \"" << fileNamesToSearch.join(
"\", \""_L1)
2633 <<
"\" inside \"" << rootDirs.join(
"\", \""_L1)
2634 <<
"\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to "
2636 <<
"). Set the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a "
2637 "higher value to spend more time on searching.";
2641 if (entry.isDir()) {
2642 toVisit.push(entry.filePath());
2645 Q_ASSERT(entry.isFile());
2646 if (!fileNamesToSearch.contains(entry.fileName()))
2649 if (ignoredFilePaths.contains(entry.absoluteFilePath()))
2652 if (option == FindFirst)
2653 return { entry.absoluteFilePath() };
2655 result << entry.absoluteFilePath();
2663 const QSet<QString> &ignoredFilePaths)
2665 return findFilePathsFromFileNamesImpl({ rootDir }, fileNamesToSearch, FindAll,
2670 const QSet<QString> &ignoredFilePaths)
2672 const QStringList result = findFilePathsFromFileNamesImpl(rootDirs, { fileNameToSearch },
2673 FindFirst, ignoredFilePaths);
2674 return result.isEmpty() ? QString{} : result.front();
Represents the locations where a renaming should take place.
RenameUsages(const QList< Edit > &renamesInFile, const QList< FileRename > &renamesInFilename)
Represents the locations where some highlighting should take place, like in the "find allreferences" ...
void appendUsage(const Location &edit)
Usages(const QList< Location > &usageInFile, const QList< QString > &usageInFilename)
static std::optional< QString > oldNameFrom(const DomItem &item)
RenameUsages renameUsagesOf(const DomItem &item, const QString &newName, const std::optional< ExpressionType > &targetType=std::nullopt)
Rename the appearance of item to newName.
static std::optional< Location > createCppTypeLocation(const QString &fileName, const QStringList &headerLocations, const QQmlJS::SourceLocation &location)
static std::optional< ExpressionType > resolveFieldMemberExpressionType(const DomItem &item, ResolveOptions options)
static QList< Location > findDefinitionOfType(const QQmlJSScope::ConstPtr &scope, const DomItem &item, const QStringList &headerDirectories)
QQmlJSScope::ConstPtr findDefiningScopeForProperty(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
Finds the scope where a property is first defined.
bool isFieldMemberBase(const DomItem &item)
QStringList findFilePathsFromFileNames(const QString &rootDir, const QStringList &fileNamesToSearch, const QSet< QString > &ignoredPaths)
@ EnumeratorValueIdentifier
@ GroupedPropertyIdentifier
@ AttachedTypeIdentifierInBindingTarget
@ QualifiedModuleIdentifier
@ PropertyChangedHandlerIdentifier
@ PropertyChangedSignalIdentifier
@ SignalHandlerIdentifier
bool isValidEcmaScriptIdentifier(QStringView view)
QLspSpecification::Range qmlLocationToLspLocation(Location qmlLocation)
Converts a QQmlJS::SourceLocation to a LSP Range.
static FieldFilter filterForFindUsages()
Filter away the parts of the Dom not needed for find usages, by following the profiler's information.
static QStringList findFilePathsFromFileNamesImpl(const QStringList &rootDirs, const QStringList &fileNamesToSearch, SearchOption option, const QSet< QString > &ignoredFilePaths)
static bool findDefinitionFromItem(const DomItem &item, const QString &name)
static std::optional< Location > findPropertyDefinitionOf(const DomItem &file, QQmlJS::SourceLocation propertyDefinitionLocation, const QString &name)
QQmlJSScope::ConstPtr findDefiningScopeForMethod(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
static std::optional< SignalOrProperty > resolveNameInQmlScope(const QString &name, const QQmlJSScope::ConstPtr &owner)
Find out if {name} is a signal, signal handler, property, property changed signal,...
static std::optional< QString > newNameFrom(const QString &dirtyNewName, IdentifierType alternative)
QQmlJSScope::ConstPtr findDefiningScopeForBinding(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
static QQmlJSScope::ConstPtr findScopeOfSpecialItems(const QQmlJSScope::ConstPtr &scope, const DomItem &item)
qsizetype textOffsetFrom(const QString &code, int row, int character)
Convert a text position from (line, column) into an offset.
static std::optional< ExpressionType > resolveSignalHandlerParameterType(const DomItem ¶meterDefinition, const QString &name, ResolveOptions options)
static std::optional< Location > locationFromDomItem(const DomItem &item, FileLocationRegion region)
static std::optional< ExpressionType > propertyBindingFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name, ResolveOptions options, QQmlJSTypeResolver *resolverForIds)
DomItem baseObject(const DomItem &qmlObject)
QByteArray lspUriToQmlUrl(const QByteArray &uri)
DomItem sourceLocationToDomItem(const DomItem &file, const QQmlJS::SourceLocation &location)
static std::optional< Location > findEnumDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
static std::optional< ExpressionType > resolveBindingIfSignalHandler(const DomItem &functionExpression)
QStringList fieldMemberExpressionBits(const DomItem &item, const DomItem &stopAtChild={})
QByteArray qmlUrlToLspUri(const QByteArray &url)
static QList< ItemLocation >::const_iterator handlePropertyDefinitionAndBindingOverlap(const QList< ItemLocation > &items, qsizetype offsetInFile)
@ ResolveActualTypeForFieldMemberExpression
static std::optional< IdentifierType > hasMethodOrSignal(const QQmlJSScope::ConstPtr &scope, const QString &name)
TextPosition textRowAndColumnFrom(const QString &code, qsizetype offset)
Convert a text position from an offset into (line, column).
QList< Location > findDefinitionOf(const DomItem &item, const QStringList &headerDirectories)
std::optional< ExpressionType > resolveExpressionType(const DomItem &item, ResolveOptions)
Resolves the type of the given DomItem, when possible (e.g., when there are enough type annotations).
static void findUsagesOfNonJSIdentifiers(const DomItem &item, const QString &name, Usages &result)
bool isFieldMemberExpression(const DomItem &item)
bool isFieldMemberAccess(const DomItem &item)
static std::optional< ExpressionType > resolveSignalOrPropertyExpressionType(const QString &name, const QQmlJSScope::ConstPtr &scope, ResolveOptions options)
static void findUsagesHelper(const DomItem &item, const QString &name, Usages &result)
QQmlJSScope::ConstPtr findDefiningScopeForEnumeration(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
QList< T > optionalToList(std::optional< T > &&optional)
static QQmlJSScope::ConstPtr propertyOwnerFrom(const QQmlJSScope::ConstPtr &type, const QString &name)
static std::optional< Location > findMethodDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
static std::optional< ExpressionType > propertyFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &propertyName, ResolveOptions options)
QList< ItemLocation > itemsFromTextLocation(const DomItem &file, int line, int character)
Find the DomItem representing the object situated in file at given line and character/column.
static std::optional< ExpressionType > resolveTypeName(const std::shared_ptr< QQmlJSTypeResolver > &resolver, const QString &name, const DomItem &item, ResolveOptions options)
Distinguishes singleton types from attached types and "regular" qml components.
static std::optional< Location > locationFromJSIdentifierDefinition(const DomItem &definitionOfItem, const QString &name)
QString qualifiersFrom(const DomItem &el)
static QList< ItemLocation > filterItemsFromTextLocation(const QList< ItemLocation > &items, qsizetype offsetInFile)
QQmlJSScope::ConstPtr findDefiningScopeForEnumerationKey(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
static QQmlJSScope::ConstPtr expressionTypeWithDefinition(const ExpressionType &ownerType)
Usages findUsagesOf(const DomItem &item)
static std::optional< ExpressionType > resolveIdentifierExpressionType(const DomItem &item, ResolveOptions options)
std::optional< Location > findTypeDefinitionOf(const DomItem &item)
Returns the location of the type definition pointed by object.
QString findFilePathFromFileName(const QStringList &rootDirs, const QString &fileNameToSearch, const QSet< QString > &ignoredPaths)
static QQmlJSScope::ConstPtr methodOwnerFrom(const QQmlJSScope::ConstPtr &type, const QString &name)
static std::optional< ExpressionType > methodFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name, ResolveOptions options)
QQmlJSScope::ConstPtr findDefiningScopeIf(const QQmlJSScope::ConstPtr &referrerScope, Predicate &&check)
static QStringList namesOfPossibleUsages(const QString &name, const DomItem &item, const QQmlJSScope::ConstPtr &targetType)
std::optional< ErrorMessage > checkNameForRename(const DomItem &item, const QString &newName, const std::optional< ExpressionType > &targetType=std::nullopt)
static QQmlJS::SourceLocation sourceLocationOrDefault(const QQmlJS::SourceLocation &location)
std::pair< QString, QStringList > cmakeBuildCommand(const QString &path, int cmakeJobs=0)
static DomItem findJSIdentifierDefinition(const DomItem &item, const QString &name)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")