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))
988 result.appendUsage(*location);
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, propertyName),
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)
1180 return ExpressionType {
1182 resolverForIds->typeForId(referrerScope, name, AssumeComponentsAreBound),
1183 QmlObjectIdIdentifier
1187 const auto typeIdentifier =
1190 const auto getScope = [bindingIsAttached, binding]() -> QQmlJSScope::ConstPtr {
1191 if (bindingIsAttached)
1192 return binding->attachedType();
1194 return binding->groupType();
1199 return ExpressionType{ name,
1205 : findDefiningScopeForProperty(referrerScope, name),
1211 Q_UNREACHABLE_RETURN({});
1215
1216
1217
1219 const QQmlJSScope::ConstPtr &scope,
const DomItem &item)
1224 const QSet<QString> specialItems = {u"QQmlConnections"_s,
1225 u"QQuickPropertyChanges"_s,
1227 u"QQuickAnchorChanges"_s};
1229 const auto special = QQmlJSUtils::searchBaseAndExtensionTypes(
1230 scope, [&specialItems](
const QQmlJSScope::ConstPtr &visitedScope) {
1231 const auto typeName = visitedScope->internalName();
1232 if (specialItems.contains(typeName))
1242 if (scope->hasOwnPropertyBindings(u"target"_s)) {
1246 DomItem current = item.qmlObject();
1247 auto target = current.bindings().key(u"target"_s).index(0);
1249 targetName = target.field(Fields::value)
1250 .field(Fields::scriptElement)
1251 .field(Fields::identifier)
1257 if (!targetName.isEmpty()) {
1259 auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
1264 return resolver->scopedType(scope, targetName);
1267 if (item.internalKind() == DomType::Binding &&
1268 item.field(Fields::bindingType).value().toInteger() ==
int(BindingType::OnBinding)) {
1273 return scope->parentScope();
1277
1278
1279
1284 const auto scope = resolver->typeForName(name);
1288 if (scope->isSingleton())
1293 if (!isFieldMemberBase(item))
1297 const DomItem rightHandSide = [&item]() {
1298 const DomItem candidate = item.directParent().field(Fields::right);
1300 if (candidate != item)
1303 return item.directParent().directParent().field(Fields::right);
1306 if (rightHandSide.internalKind() != DomType::ScriptIdentifierExpression)
1309 const QString fieldMemberAccessName = rightHandSide.value().toString();
1310 if (fieldMemberAccessName.isEmpty() || !fieldMemberAccessName.front().isLower())
1313 if (scope->attachedType()) {
1323 const QString name = item.field(Fields::identifier).value().toString();
1324 DomItem parent = item.directParent();
1325 auto owner = resolveExpressionType(parent.field(Fields::left),
1326 ResolveOptions::ResolveActualTypeForFieldMemberExpression);
1330 if (!owner->semanticScope) {
1337 const QmlFile *qmlFile = item.fileObject().as<QmlFile>();
1340 if (
auto scope = resolveTypeName(
1341 qmlFile->typeResolver(), u"%1.%2"_s.arg(*owner->name, name), item,
1351 if (
auto scope = methodFromReferrerScope(owner->semanticScope, name, options))
1354 if (
auto scope = propertyBindingFromReferrerScope(owner->semanticScope, name, options,
nullptr))
1357 if (
auto scope = propertyFromReferrerScope(owner->semanticScope, name, options))
1365 if (
const auto scope = owner->semanticScope->isComposite()
1366 ? item.goToFile(owner->semanticScope->filePath())
1367 .rootQmlObject(GoTo::MostLikely)
1369 : owner->semanticScope) {
1370 if (scope->hasEnumerationKey(name)) {
1374 else if (scope->hasEnumeration(name)) {
1380 for (
auto it = owner->semanticScope->childScopesBegin(),
1381 end = owner->semanticScope->childScopesEnd();
1383 if ((*it)->inlineComponentName() == name) {
1390 qCDebug(QQmlLSUtilsLog) <<
"Could not find identifier expression for" << item.internalKindStr();
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1407 if (functionExpression.internalKind() != DomType::ScriptFunctionExpression)
1410 const DomItem parent = functionExpression.directParent();
1411 if (parent.internalKind() != DomType::ScriptExpression)
1414 const DomItem grandParent = parent.directParent();
1415 if (grandParent.internalKind() != DomType::Binding)
1418 auto bindingType = resolveExpressionType(grandParent, ResolveOwnerType);
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1439 const std::optional<QQmlJSScope::JavaScriptIdentifier> jsIdentifier =
1440 parameterDefinition.semanticScope()->jsIdentifier(name);
1441 if (!jsIdentifier || jsIdentifier->kind != QQmlJSScope::JavaScriptIdentifier::Parameter)
1444 const DomItem handlerFunctionExpression =
1445 parameterDefinition.internalKind() == DomType::ScriptBlockStatement
1446 ? parameterDefinition.directParent()
1447 : parameterDefinition;
1450 resolveBindingIfSignalHandler(handlerFunctionExpression);
1455 return ExpressionType{};
1460 const DomItem parameters = handlerFunctionExpression[Fields::parameters];
1461 const int indexOfParameter = [¶meters, &name]() {
1462 for (
int i = 0; i < parameters.indexes(); ++i) {
1463 if (parameters[i][Fields::identifier].value().toString() == name)
1466 Q_ASSERT_X(
false,
"resolveSignalHandlerParameter",
1467 "can't find JS identifier with Parameter kind in the parameters");
1468 Q_UNREACHABLE_RETURN(-1);
1471 const std::optional<QString> signalName =
1472 QQmlSignalNames::handlerNameToSignalName(*bindingType->name);
1473 Q_ASSERT_X(signalName.has_value(),
"resolveSignalHandlerParameterType",
1474 "handlerNameToSignalName failed on a SignalHandler");
1476 const QQmlJSMetaMethod signalDefinition =
1477 bindingType->semanticScope->methods(*signalName).front();
1478 const QList<QQmlJSMetaParameter> parameterList = signalDefinition.parameters();
1481 if (parameterList.size() <= indexOfParameter)
1489 const QQmlJSScope::ConstPtr parameterType = parameterList[indexOfParameter].type();
1497 if (isFieldMemberAccess(item) || isFieldMemberExpression(item)) {
1498 return resolveFieldMemberExpressionType(item, options);
1501 const QString name = item.field(Fields::identifier).value().toString();
1503 if (
const DomItem definitionOfItem = findJSIdentifierDefinition(item, name)) {
1504 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
1505 && definitionOfItem.semanticScope()->ownJSIdentifier(name),
1506 "QQmlLSUtils::findDefinitionOf",
1507 "JS definition does not actually define the JS identifer. "
1508 "It should be empty.");
1509 if (
auto parameter = resolveSignalHandlerParameterType(definitionOfItem, name, options))
1512 const auto scope = definitionOfItem.semanticScope();
1513 return ExpressionType{ name,
1514 options == ResolveOwnerType
1516 : QQmlJSScope::ConstPtr(
1517 scope->ownJSIdentifier(name)->scope.toStrongRef()),
1518 IdentifierType::JavaScriptIdentifier };
1521 const auto referrerScope = item.nearestSemanticScope();
1526 if (
auto scope = methodFromReferrerScope(referrerScope, name, options))
1529 const auto qmlFile = item.containingFile().ownerAs<QmlFile>();
1533 const auto resolver = qmlFile->typeResolver();
1538 if (
const auto scope = propertyBindingFromReferrerScope(
1539 referrerScope, name, options, resolver.get())) {
1544 if (
const auto scope = propertyFromReferrerScope(referrerScope, name, options))
1547 if (resolver->seenModuleQualifiers().contains(name))
1550 if (
const auto scope = resolveTypeName(resolver, name, item, options))
1554 if (
const QQmlJSScope::ConstPtr fromId
1555 = resolver->typeForId(referrerScope, name, AssumeComponentsAreBound)) {
1559 const QQmlJSScope::ConstPtr jsGlobal = resolver->jsGlobalObject();
1561 if (
auto scope = methodFromReferrerScope(jsGlobal, name, options)) {
1567 if (
auto scope = propertyFromReferrerScope(jsGlobal, name, options)) {
1579 const auto signalOrProperty = resolveNameInQmlScope(name, scope);
1580 if (!signalOrProperty)
1583 switch (signalOrProperty->type) {
1586 case ResolveOwnerType:
1587 return ExpressionType{ name, findDefiningScopeForProperty(scope, name),
1588 signalOrProperty->type };
1590 return ExpressionType{ name, scope->property(name).type(), signalOrProperty->type };
1592 Q_UNREACHABLE_RETURN({});
1595 case ResolveOwnerType:
1596 return ExpressionType{ name,
1597 findDefiningScopeForProperty(scope, signalOrProperty->name),
1598 signalOrProperty->type };
1601 Q_UNREACHABLE_RETURN({});
1603 Q_UNREACHABLE_RETURN({});
1610 return ExpressionType{ name, findDefiningScopeForMethod(scope, signalOrProperty->name),
1611 signalOrProperty->type };
1615 Q_UNREACHABLE_RETURN({});
1617 Q_UNREACHABLE_RETURN({});
1619 Q_UNREACHABLE_RETURN({});
1624
1625
1626
1627
1628
1629
1630
1631
1632
1636 switch (item.internalKind()) {
1637 case DomType::ScriptIdentifierExpression: {
1638 return resolveIdentifierExpressionType(item, options);
1640 case DomType::PropertyDefinition: {
1641 auto propertyDefinition = item.as<PropertyDefinition>();
1642 if (propertyDefinition && propertyDefinition->semanticScope()) {
1643 const auto &scope = propertyDefinition->semanticScope();
1649 Q_UNREACHABLE_RETURN({});
1651 Q_UNREACHABLE_RETURN({});
1655 case DomType::Binding: {
1656 auto binding = item.as<Binding>();
1658 std::optional<QQmlJSScope::ConstPtr> owner = item.qmlObject().semanticScope();
1661 const QString name = binding->name();
1666 if (
const QQmlJSScope::ConstPtr targetScope
1667 = findScopeOfSpecialItems(owner.value(), item)) {
1668 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1669 if (!signalOrProperty)
1672 case ResolveOwnerType:
1673 return ExpressionType{
1674 name, findDefiningScopeForBinding(targetScope, signalOrProperty->name),
1675 signalOrProperty->type
1679 Q_UNREACHABLE_RETURN({});
1682 if (
auto result = resolveSignalOrPropertyExpressionType(name, owner.value(), options)) {
1685 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1686 "type of a Binding.";
1691 case DomType::QmlObject: {
1692 auto object = item.as<QmlObject>();
1695 if (
auto scope = object->semanticScope()) {
1696 const auto name = item.name();
1697 const bool isComponent = name.front().isUpper();
1699 scope = scope->baseType();
1711 case DomType::QmlComponent: {
1712 auto component = item.as<QmlComponent>();
1715 const auto scope = component->semanticScope();
1719 QString name = item.name();
1720 if (
auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1721 name = name.sliced(dotIndex + 1);
1728 Q_UNREACHABLE_RETURN({});
1730 case DomType::ScriptFunctionExpression: {
1733 case DomType::MethodInfo: {
1734 const auto object = item.as<MethodInfo>();
1735 if (object && object->semanticScope()) {
1736 std::optional<QQmlJSScope::ConstPtr> scope = object->semanticScope();
1740 if (
const QQmlJSScope::ConstPtr targetScope =
1741 findScopeOfSpecialItems(scope.value()->parentScope(), item)) {
1742 const auto signalOrProperty = resolveNameInQmlScope(object->name, targetScope);
1743 if (!signalOrProperty)
1747 case ResolveOwnerType:
1748 return ExpressionType{ object->name,
1749 findDefiningScopeForMethod(targetScope,
1750 signalOrProperty->name),
1751 signalOrProperty->type };
1760 if (QQmlSA::isFunctionScope(scope.value()->scopeType()))
1761 scope = scope.value()->parentScope();
1763 if (
const auto result = resolveSignalOrPropertyExpressionType(
1764 object->name, scope.value(), options)) {
1767 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1768 "type of a MethodInfo.";
1773 case DomType::ScriptBinaryExpression: {
1774 if (isFieldMemberExpression(item)) {
1775 return resolveExpressionType(item.field(Fields::right), options);
1779 case DomType::ScriptLiteral: {
1781
1782
1783 const auto scope = item.qmlObject().semanticScope();
1784 const auto name = item.field(Fields::value).value().toString();
1785 if (
const QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(scope, item)) {
1786 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1787 if (!signalOrProperty)
1790 case ResolveOwnerType:
1791 return ExpressionType{
1792 name, findDefiningScopeForProperty(targetScope, signalOrProperty->name),
1793 signalOrProperty->type
1798 Q_UNREACHABLE_RETURN({});
1803 case DomType::EnumItem: {
1804 const QString enumValue = item.field(Fields::name).value().toString();
1805 const QQmlJSScope::ConstPtr referrerScope
1806 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1807 if (!referrerScope->hasEnumerationKey(enumValue))
1812 case ResolveActualTypeForFieldMemberExpression:
1813 case ResolveOwnerType:
1814 return ExpressionType {
1816 findDefiningScopeForEnumerationKey(referrerScope, enumValue),
1817 EnumeratorValueIdentifier
1820 Q_UNREACHABLE_RETURN({});
1822 case DomType::EnumDecl: {
1823 const QString enumName = item.field(Fields::name).value().toString();
1824 const QQmlJSScope::ConstPtr referrerScope
1825 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1826 if (!referrerScope->hasEnumeration(enumName))
1830 case ResolveActualTypeForFieldMemberExpression:
1831 case ResolveOwnerType:
1832 return ExpressionType {
1834 findDefiningScopeForEnumeration(referrerScope, enumName),
1835 EnumeratorIdentifier
1839 Q_UNREACHABLE_RETURN({});
1841 case DomType::Import: {
1843 if (!item[Fields::importId])
1845 return ExpressionType{ item[Fields::importId].value().toString(),
1847 QualifiedModuleIdentifier };
1849 case DomType::ScriptNewMemberExpression: {
1850 const auto name = item.field(Fields::base).value().toString();
1853 case DomType::ScriptCallExpression: {
1854 const DomItem callee = item.field(Fields::callee);
1855 const auto calleeExpressionType = resolveExpressionType(callee, ResolveOwnerType);
1857 if (!calleeExpressionType || !calleeExpressionType->semanticScope
1858 || !calleeExpressionType->name || calleeExpressionType->type !=
MethodIdentifier) {
1862 const auto methods =
1863 calleeExpressionType->semanticScope->methods(*calleeExpressionType->name);
1864 if (methods.isEmpty())
1867 const auto returnType = methods.front().returnType();
1871 qCDebug(QQmlLSUtilsLog) <<
"Type" << item.internalKindStr()
1872 <<
"is unimplemented in QQmlLSUtils::resolveExpressionType";
1882 auto items = QQmlLSUtils::itemsFromTextLocation(file, location.startLine - 1,
1883 location.startColumn - 1);
1884 switch (items.size()) {
1888 return items.front().domItem;
1894 auto &first = items.front();
1895 auto &second = items.back();
1896 Q_ASSERT_X(first.fileLocation->info().fullRegion.startLine
1897 == second.fileLocation->info().fullRegion.startLine,
1898 "QQmlLSUtils::findTypeDefinitionOf(DomItem)",
1899 "QQmlLSUtils::itemsFromTextLocation returned non-adjacent items.");
1900 if (first.fileLocation->info().fullRegion.startColumn
1901 > second.fileLocation->info().fullRegion.startColumn)
1902 return first.domItem;
1904 return second.domItem;
1908 qDebug() <<
"Found multiple candidates for type of scriptidentifierexpression";
1917 const DomItem enumeration = [&file, &location, &name]() -> DomItem {
1918 const DomItem enumerations = QQmlLSUtils::sourceLocationToDomItem(file, location)
1921 .field(Fields::enumerations);
1922 const QSet<QString> enumerationNames = enumerations.keys();
1923 for (
const QString &enumName : enumerationNames) {
1924 const DomItem currentKey = enumerations.key(enumName).index(0);
1925 if (enumName == name)
1927 const DomItem values = currentKey.field(Fields::values);
1928 for (
int i = 0, end = values.size(); i < end; ++i) {
1929 const DomItem currentValue = values.index(i);
1930 if (currentValue.field(Fields::name).value().toStringView() == name)
1931 return currentValue;
1937 auto fileLocation = FileLocations::treeOf(enumeration);
1942 auto regions = fileLocation->info().regions;
1944 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1945 return Location::tryFrom(enumeration.canonicalFilePath(), *it, file);
1954 DomItem owner = QQmlLSUtils::sourceLocationToDomItem(file, location).qmlObject();
1955 DomItem method = owner.field(Fields::methods).key(name).index(0);
1956 auto fileLocation = FileLocations::treeOf(method);
1960 auto regions = fileLocation->info().regions;
1962 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1963 return Location::tryFrom(method.canonicalFilePath(), *it, file);
1971 const QString &name)
1973 DomItem propertyOwner =
1974 QQmlLSUtils::sourceLocationToDomItem(file, propertyDefinitionLocation).qmlObject();
1975 DomItem propertyDefinition = propertyOwner.field(Fields::propertyDefs).key(name).index(0);
1976 auto fileLocation = FileLocations::treeOf(propertyDefinition);
1980 auto regions = fileLocation->info().regions;
1982 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1983 return Location::tryFrom(propertyDefinition.canonicalFilePath(), *it, file);
1991 return location.startLine == 0 ? QQmlJS::SourceLocation{ 0, 0, 1, 1 } : location;
1995 const QStringList &headerLocations,
1996 const QQmlJS::SourceLocation &location)
1998 const QString filePath = findFilePathFromFileName(headerLocations, type->filePath(), {});
1999 if (filePath.isEmpty()) {
2000 qCWarning(QQmlLSUtilsLog) <<
"Couldn't find the C++ file '%1'."_L1.arg(type->filePath());
2004 const TextPosition endPosition{
static_cast<
int>(location.startLine) + 1, 1 };
2005 return Location{ filePath, location, endPosition };
2009 const DomItem &item,
2010 const QStringList &headerDirectories)
2014 if (scope->isComposite())
2015 if (
const auto result = Location::tryFrom(scope->filePath(), scope->sourceLocation(), item))
2017 return createCppTypeLocation(scope, headerDirectories,
2018 sourceLocationOrDefault(scope->sourceLocation()));
2023 auto resolvedExpression = resolveExpressionType(item, ResolveOptions::ResolveOwnerType);
2025 if (!resolvedExpression || !resolvedExpression->name
2026 || (!resolvedExpression->semanticScope
2028 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf: Type could not be resolved.";
2032 switch (resolvedExpression->type) {
2034 const auto jsIdentifier =
2035 resolvedExpression->semanticScope->ownJSIdentifier(*resolvedExpression->name);
2039 return Location::tryFrom(resolvedExpression->semanticScope->filePath(),
2040 jsIdentifier->location, item);
2045 if (!resolvedExpression->semanticScope->isComposite()) {
2046 return createCppTypeLocation(
2047 resolvedExpression->semanticScope, headerDirectories,
2048 resolvedExpression->semanticScope->property(*resolvedExpression->name)
2051 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2052 const QQmlJS::SourceLocation ownerLocation =
2053 resolvedExpression->semanticScope->sourceLocation();
2054 return findPropertyDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
2061 if (!resolvedExpression->semanticScope->isComposite()) {
2062 const auto methods =
2063 resolvedExpression->semanticScope->methods(*resolvedExpression->name);
2064 if (methods.isEmpty())
2066 return createCppTypeLocation(resolvedExpression->semanticScope, headerDirectories,
2067 methods.front().sourceLocation());
2069 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2070 const QQmlJS::SourceLocation ownerLocation =
2071 resolvedExpression->semanticScope->sourceLocation();
2072 return findMethodDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
2075 DomItem qmlObject = QQmlLSUtils::sourceLocationToDomItem(
2076 item.containingFile(), resolvedExpression->semanticScope->sourceLocation());
2078 const DomItem domId = qmlObject.component()
2080 .key(*resolvedExpression->name)
2082 .field(Fields::value);
2084 qCDebug(QQmlLSUtilsLog)
2085 <<
"QmlComponent in Dom structure has no id, was it misconstructed?";
2089 return Location::tryFrom(domId.canonicalFilePath(),
2090 FileLocations::treeOf(domId)->info().fullRegion, domId);
2092 case AttachedTypeIdentifier:
2093 return findDefinitionOfType(resolvedExpression->semanticScope->attachedType(), item,
2095 case AttachedTypeIdentifierInBindingTarget:
2096 return findDefinitionOfType(resolvedExpression->semanticScope->baseType(), item,
2098 case QmlComponentIdentifier:
2099 case SingletonIdentifier:
2100 return findDefinitionOfType(resolvedExpression->semanticScope, item, headerDirectories);
2102 const DomItem imports = item.fileObject().field(Fields::imports);
2103 for (
int i = 0; i < imports.indexes(); ++i) {
2104 if (imports[i][Fields::importId].value().toString() == resolvedExpression->name) {
2105 const auto fileLocations = FileLocations::treeOf(imports[i]);
2109 return Location::tryFrom(item.canonicalFilePath(), fileLocations->info().regions[IdNameRegion], item);
2116 if (!resolvedExpression->semanticScope->isComposite()) {
2117 const auto enumerations = resolvedExpression->semanticScope->enumerations();
2118 for (
const auto &enumeration : enumerations) {
2119 if (enumeration.hasKey(*resolvedExpression->name)
2120 || enumeration.name() == *resolvedExpression->name) {
2121 return createCppTypeLocation(
2122 resolvedExpression->semanticScope, headerDirectories,
2123 sourceLocationOrDefault(QQmlJS::SourceLocation::fromQSizeType(
2124 0, 0, enumeration.lineNumber(), 1)));
2129 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2130 const QQmlJS::SourceLocation ownerLocation =
2131 resolvedExpression->semanticScope->sourceLocation();
2132 return findEnumDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
2135 case LambdaMethodIdentifier:
2136 case NotAnIdentifier:
2137 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf was not implemented for type"
2138 << resolvedExpression->type;
2141 Q_UNREACHABLE_RETURN({});
2145 const QString &name)
2147 Q_ASSERT(!name.isEmpty());
2150 QQmlJSScope::ConstPtr typeWithDefinition = type;
2151 while (typeWithDefinition && !typeWithDefinition->hasOwnProperty(name))
2152 typeWithDefinition = typeWithDefinition->baseType();
2154 if (!typeWithDefinition) {
2155 qCDebug(QQmlLSUtilsLog)
2156 <<
"QQmlLSUtils::checkNameForRename cannot find property definition,"
2160 return typeWithDefinition;
2164 const QString &name)
2166 Q_ASSERT(!name.isEmpty());
2169 QQmlJSScope::ConstPtr typeWithDefinition = type;
2170 while (typeWithDefinition && !typeWithDefinition->hasOwnMethod(name))
2171 typeWithDefinition = typeWithDefinition->baseType();
2173 if (!typeWithDefinition) {
2174 qCDebug(QQmlLSUtilsLog)
2175 <<
"QQmlLSUtils::checkNameForRename cannot find method definition,"
2179 return typeWithDefinition;
2184 switch (ownerType
.type) {
2185 case PropertyIdentifier:
2186 return propertyOwnerFrom(ownerType.semanticScope, *ownerType.name);
2188 const auto propertyName =
2189 QQmlSignalNames::changedHandlerNameToPropertyName(*ownerType.name);
2190 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2194 const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(*ownerType.name);
2195 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2197 case MethodIdentifier:
2198 case SignalIdentifier:
2199 return methodOwnerFrom(ownerType.semanticScope, *ownerType.name);
2201 const auto signalName = QQmlSignalNames::handlerNameToSignalName(*ownerType.name);
2202 return methodOwnerFrom(ownerType.semanticScope, *signalName);
2216 return ownerType.semanticScope;
2225 if (
const auto resolved = resolveExpressionType(item, ResolveOwnerType))
2226 return checkNameForRename(item, dirtyNewName, resolved);
2230 if (!isValidEcmaScriptIdentifier(dirtyNewName))
2231 return ErrorMessage{ 0, u"Invalid EcmaScript identifier!"_s };
2233 const auto userSemanticScope = item.nearestSemanticScope();
2235 if (!ownerType || !userSemanticScope) {
2236 return ErrorMessage{ 0, u"Requested item cannot be renamed."_s };
2240 switch (ownerType->type) {
2242 if (!QQmlSignalNames::isChangedSignalName(dirtyNewName)) {
2243 return ErrorMessage{ 0, u"Invalid name for a property changed signal."_s };
2248 if (!QQmlSignalNames::isChangedHandlerName(dirtyNewName)) {
2249 return ErrorMessage{ 0, u"Invalid name for a property changed handler identifier."_s };
2254 if (!QQmlSignalNames::isHandlerName(dirtyNewName)) {
2255 return ErrorMessage{ 0, u"Invalid name for a signal handler identifier."_s };
2261 if (dirtyNewName.front().isLetter() && !dirtyNewName.front().isLower()) {
2262 return ErrorMessage{ 0, u"Object id names cannot start with an upper case letter."_s };
2273 auto typeWithDefinition = expressionTypeWithDefinition(*ownerType);
2275 if (!typeWithDefinition) {
2276 return ErrorMessage{
2278 u"Renaming has not been implemented for the requested item."_s,
2283 if (!typeWithDefinition->isComposite()) {
2284 return ErrorMessage{ 0, u"Cannot rename items defined in non-QML files."_s };
2288 const QString moduleOfDefinition = ownerType->semanticScope->moduleName();
2289 const QString moduleOfCurrentItem = userSemanticScope->moduleName();
2290 if (moduleOfDefinition != moduleOfCurrentItem) {
2291 return ErrorMessage{
2293 u"Cannot rename items defined in the \"%1\" module from a usage in the \"%2\" module."_s
2294 .arg(moduleOfDefinition, moduleOfCurrentItem),
2303 switch (item.internalKind()) {
2304 case DomType::ScriptIdentifierExpression: {
2305 return item.field(Fields::identifier).value().toString();
2307 case DomType::ScriptVariableDeclarationEntry: {
2308 return item.field(Fields::identifier).value().toString();
2310 case DomType::PropertyDefinition:
2311 case DomType::Binding:
2312 case DomType::MethodInfo: {
2313 return item.field(Fields::name).value().toString();
2316 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
2317 <<
"was not implemented for QQmlLSUtils::renameUsagesOf";
2318 return std::nullopt;
2320 Q_UNREACHABLE_RETURN(
std::nullopt);
2328 switch (alternative) {
2330 return QQmlSignalNames::handlerNameToSignalName(dirtyNewName);
2333 return QQmlSignalNames::changedHandlerNameToPropertyName(dirtyNewName);
2336 return QQmlSignalNames::changedSignalNameToPropertyName(dirtyNewName);
2341 return std::nullopt;
2343 Q_UNREACHABLE_RETURN(
std::nullopt);
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2368 const Usages locations = findUsagesOf(item);
2372 auto oldName = oldNameFrom(item);
2376 QQmlJSScope::ConstPtr semanticScope;
2378 semanticScope = targetType->semanticScope;
2379 }
else if (
const auto resolved =
2380 QQmlLSUtils::resolveExpressionType(item, ResolveOptions::ResolveOwnerType)) {
2381 semanticScope = resolved->semanticScope;
2387 if (
const auto resolved = resolveNameInQmlScope(*oldName, semanticScope)) {
2388 newName = newNameFrom(dirtyNewName, resolved->type).value_or(dirtyNewName);
2389 oldName = resolved->name;
2391 newName = dirtyNewName;
2394 const qsizetype oldNameLength = oldName->length();
2395 const qsizetype oldHandlerNameLength =
2396 QQmlSignalNames::signalNameToHandlerName(*oldName).length();
2397 const qsizetype oldChangedSignalNameLength =
2398 QQmlSignalNames::propertyNameToChangedSignalName(*oldName).length();
2399 const qsizetype oldChangedHandlerNameLength =
2400 QQmlSignalNames::propertyNameToChangedHandlerName(*oldName).length();
2402 const QString newHandlerName = QQmlSignalNames::signalNameToHandlerName(newName);
2403 const QString newChangedSignalName = QQmlSignalNames::propertyNameToChangedSignalName(newName);
2404 const QString newChangedHandlerName =
2405 QQmlSignalNames::propertyNameToChangedHandlerName(newName);
2408 for (
const auto &location : locations.usagesInFile()) {
2409 const qsizetype currentLength = location.sourceLocation().length;
2411 edit.location = location;
2412 if (oldNameLength == currentLength) {
2414 edit.replacement = newName;
2416 }
else if (oldHandlerNameLength == currentLength) {
2418 edit.replacement = newHandlerName;
2420 }
else if (oldChangedSignalNameLength == currentLength) {
2422 edit.replacement = newChangedSignalName;
2424 }
else if (oldChangedHandlerNameLength == currentLength) {
2426 edit.replacement = newChangedHandlerName;
2429 qCDebug(QQmlLSUtilsLog) <<
"Found usage with wrong identifier length, ignoring...";
2432 result.appendRename(edit);
2435 for (
const auto &filename : locations.usagesInFilename()) {
2438 if (filename.endsWith(u".ui.qml"_s))
2439 extension = u".ui.qml"_s;
2440 else if (filename.endsWith(u".qml"_s))
2441 extension = u".qml"_s;
2445 QFileInfo info(filename);
2447 if (!info.isFile() || info.baseName() != oldName)
2450 const QString newFilename =
2451 QDir::cleanPath(filename +
"/.."_L1) +
'/'_L1 + newName + extension;
2452 result.appendRename({ filename, newFilename });
2459 const QQmlJS::SourceLocation &sourceLocation,
2460 const QQmlJS::Dom::DomItem &someItem)
2462 auto qmlFile = someItem.goToFile(fileName).ownerAs<QQmlJS::Dom::QmlFile>();
2464 qDebug() <<
"Could not find file" << fileName <<
"in the dom!";
2467 return Location{ fileName, sourceLocation,
2468 textRowAndColumnFrom(qmlFile->code(), sourceLocation.end()) };
2471Location Location::from(
const QString &fileName,
const QQmlJS::SourceLocation &sourceLocation,
const QString &code)
2473 return Location{ fileName, sourceLocation, textRowAndColumnFrom(code, sourceLocation.end()) };
2477 qsizetype startCharacter, qsizetype length)
2479 const auto offset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
2480 return from(fileName,
2481 QQmlJS::SourceLocation::fromQSizeType(offset, length, startLine, startCharacter),
2485Edit Edit::from(
const QString &fileName,
const QString &code, qsizetype startLine,
2486 qsizetype startCharacter, qsizetype length,
const QString &newName)
2489 rename.location = Location::from(fileName, code, startLine, startCharacter, length);
2490 rename.replacement = newName;
2496 QQmlJS::Lexer lexer(
nullptr);
2497 lexer.setCode(identifier.toString(), 0);
2498 const int token = lexer.lex();
2499 if (token !=
static_cast<
int>(QQmlJS::Lexer::T_IDENTIFIER))
2502 const int eofToken = lexer.lex();
2503 return eofToken ==
static_cast<
int>(QQmlJS::Lexer::EOF_SYMBOL);
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2522 const std::pair<QString, QStringList> result{
2523 u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
2530 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2531 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2536 return m_usagesInFilename.isEmpty() && m_usagesInFile.isEmpty();
2539Usages::
Usages(
const QList<Location> &usageInFile,
const QList<QString> &usageInFilename)
2542 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2543 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2547 const QList<FileRename> &renamesInFilename)
2550 std::sort(m_renamesInFile.begin(), m_renamesInFile.end());
2551 std::sort(m_renamesInFilename.begin(), m_renamesInFilename.end());
2556 const QStringList &fileNamesToSearch,
2558 const QSet<QString> &ignoredFilePaths)
2560 if (fileNamesToSearch.isEmpty() || rootDirs.isEmpty())
2563 const qint64 maxFilesToSearch =
2564 qEnvironmentVariableIntegerValue(
"QMLLS_MAX_FILES_TO_SEARCH")
2566 qint64 visitedItems = 0;
2569 std::queue<QString> toVisit;
2570 for (
const QString &rootDir : rootDirs)
2571 toVisit.push(rootDir);
2573 while (!toVisit.empty()) {
2574 const QString current = toVisit.front();
2577 for (
const auto &entry : QDirListing{ current, QDirListing::IteratorFlag::ExcludeOther }) {
2580 if (++visitedItems > maxFilesToSearch) {
2581 qInfo(QQmlLSUtilsLog).noquote().nospace()
2582 <<
"Aborting search for \"" << fileNamesToSearch.join(
"\", \""_L1)
2583 <<
"\" inside \"" << rootDirs.join(
"\", \""_L1)
2584 <<
"\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to "
2586 <<
"). Set the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a "
2587 "higher value to spend more time on searching.";
2591 if (entry.isDir()) {
2592 toVisit.push(entry.filePath());
2595 Q_ASSERT(entry.isFile());
2596 if (!fileNamesToSearch.contains(entry.fileName()))
2599 if (ignoredFilePaths.contains(entry.absoluteFilePath()))
2602 if (option == FindFirst)
2603 return { entry.absoluteFilePath() };
2605 result << entry.absoluteFilePath();
2613 const QSet<QString> &ignoredFilePaths)
2615 return findFilePathsFromFileNamesImpl({ rootDir }, fileNamesToSearch, FindAll,
2620 const QSet<QString> &ignoredFilePaths)
2622 const QStringList result = findFilePathsFromFileNamesImpl(rootDirs, { fileNameToSearch },
2623 FindFirst, ignoredFilePaths);
2624 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 QQmlJSScope::ConstPtr &type, const QStringList &headerLocations, const QQmlJS::SourceLocation &location)
static std::optional< ExpressionType > resolveFieldMemberExpressionType(const DomItem &item, ResolveOptions options)
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)
std::optional< Location > findDefinitionOf(const DomItem &item, const QStringList &headerDirectories)
DomItem baseObject(const DomItem &qmlObject)
QByteArray lspUriToQmlUrl(const QByteArray &uri)
DomItem sourceLocationToDomItem(const DomItem &file, const QQmlJS::SourceLocation &location)
static std::optional< Location > findDefinitionOfType(const QQmlJSScope::ConstPtr &scope, const DomItem &item, const QStringList &headerDirectories)
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).
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)
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)
std::pair< QString, QStringList > cmakeBuildCommand(const QString &path)
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)
static DomItem findJSIdentifierDefinition(const DomItem &item, const QString &name)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")