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);
395
396
397
398
399
400
401
402
403
404
405
408 auto expressionType = resolveExpressionType(object, ResolveOptions::ResolveOwnerType);
413 QQmlJSScope::ConstPtr scope = expressionType->semanticScope;
416 switch (expressionType->type) {
418 if (!expressionType->name)
420 QQmlJSScope::ConstPtr propertyType = scope->property(*expressionType->name).type();
423 return Location::tryFrom(propertyType->filePath(), propertyType->sourceLocation(), object);
426 if (!expressionType->name)
428 auto jsIdentifier = scope->jsIdentifier(*expressionType->name);
429 if (!jsIdentifier || !jsIdentifier->typeName)
431 auto qmlFile = object.fileObject().as<QmlFile>();
434 auto definition = qmlFile->typeResolver()->typeForName(*jsIdentifier->typeName);
437 return Location::tryFrom(definition->filePath(), definition->sourceLocation(), object);
455 return Location::tryFrom(scope->filePath(), scope->sourceLocation(), object);
457 Q_UNREACHABLE_RETURN({ });
462 if (
const QQmlJSScope::ConstPtr &scope = item.semanticScope()) {
463 qCDebug(QQmlLSUtilsLog) <<
"Searching for definition in" << item.internalKindStr();
464 if (
auto jsIdentifier = scope->ownJSIdentifier(name)) {
465 qCDebug(QQmlLSUtilsLog) <<
"Found scope" << scope->baseTypeName();
474 DomItem definitionOfItem;
475 item.visitUp([&name, &definitionOfItem](
const DomItem &i) {
476 if (findDefinitionFromItem(i, name)) {
477 definitionOfItem = i;
481 if (i.internalKind() == DomType::ScriptExpression)
486 if (definitionOfItem)
487 return definitionOfItem;
491 if (DomItem res = item.filterUp([](DomType k,
const DomItem &) {
return k == DomType::MethodInfo; },
492 FilterUpOptions::ReturnOuter)) {
493 DomItem candidate = res.field(Fields::body).field(Fields::scriptElement);
494 if (findDefinitionFromItem(candidate, name)) {
500 if (DomItem res = item.filterUp(
501 [](DomType k,
const DomItem &) {
return k == DomType::ScriptFunctionExpression; },
502 FilterUpOptions::ReturnOuter)) {
503 if (findDefinitionFromItem(res, name)) {
508 return definitionOfItem;
512
513
514
515
527
528
529
530
531
532
533
534
535
536
537
538
539
541 const QQmlJSScope::ConstPtr &owner)
543 if (owner->hasProperty(name)) {
547 if (
const auto propertyName = QQmlSignalNames::changedHandlerNameToPropertyName(name)) {
548 if (owner->hasProperty(*propertyName)) {
549 const QString signalName = *QQmlSignalNames::changedHandlerNameToSignalName(name);
550 const QQmlJSMetaMethod signal = owner->methods(signalName).front();
553 if (signal.parameterNames().size() == 0)
560 if (
const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
561 if (
auto methods = owner->methods(*signalName); !methods.isEmpty()) {
562 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
568 if (
const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
569 if (owner->hasProperty(*propertyName)) {
574 if (
auto methods = owner->methods(name); !methods.isEmpty()) {
575 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
584
585
586
587
590 const QQmlJSScope::ConstPtr &targetType)
592 QStringList namesToCheck = { name };
593 if (item.internalKind() == DomType::EnumItem || item.internalKind() == DomType::EnumDecl)
596 auto namings = resolveNameInQmlScope(name, targetType);
599 switch (namings->type) {
603 const QString propertyChangedHandler =
604 QQmlSignalNames::propertyNameToChangedHandlerName(namings->name);
605 namesToCheck.append(propertyChangedHandler);
607 const QString propertyChangedSignal =
608 QQmlSignalNames::propertyNameToChangedSignalName(namings->name);
609 namesToCheck.append(propertyChangedSignal);
615 namesToCheck.append(namings->name);
616 namesToCheck.append(QQmlSignalNames::propertyNameToChangedSignalName(namings->name));
622 namesToCheck.append(namings->name);
623 namesToCheck.append(QQmlSignalNames::propertyNameToChangedHandlerName(namings->name));
628 namesToCheck.append(QQmlSignalNames::signalNameToHandlerName(namings->name));
633 namesToCheck.append(namings->name);
643template<
typename Predicate>
645 const QQmlJSScope::ConstPtr &referrerScope, Predicate &&check)
647 QQmlJSScope::ConstPtr result;
648 QQmlJSUtils::searchBaseAndExtensionTypes(
649 referrerScope, [&](
const QQmlJSScope::ConstPtr &scope) {
661
662
663
664
665
666
668 const QString &nameToCheck)
670 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
671 return scope->hasOwnProperty(nameToCheck);
676
677
678
679
680
682 const QString &nameToCheck)
684 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
685 return scope->hasOwnProperty(nameToCheck) || scope->hasOwnMethod(nameToCheck);
690
691
692
694 const QString &nameToCheck)
696 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
697 return scope->hasOwnMethod(nameToCheck);
702
703
704
706 const QString &nameToCheck)
708 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
709 return scope->hasOwnEnumeration(nameToCheck);
714
715
716
718 const QString &nameToCheck)
720 return findDefiningScopeIf(referrerScope, [&nameToCheck](
const QQmlJSScope::ConstPtr &scope) {
721 return scope->hasOwnEnumerationKey(nameToCheck);
726
727
728
729
730
731
732
733
734
735
738 FieldFilter filter{ {},
740 { QString(), Fields::propertyInfos.toString() },
741 { QString(), Fields::defaultPropertyName.toString() },
742 { QString(), Fields::get.toString() },
749 const auto expressionType = resolveExpressionType(item, ResolveOwnerType);
755 && !expressionType->semanticScope->isInlineComponent()) {
756 result.appendFilenameUsage(expressionType->semanticScope->filePath());
759 const QStringList namesToCheck = namesOfPossibleUsages(name, item, expressionType->semanticScope);
761 const auto addLocationIfTypeMatchesTarget =
762 [&result, &expressionType, &item](
const DomItem &toBeResolved, FileLocationRegion subRegion) {
763 const auto currentType =
764 resolveExpressionType(toBeResolved, ResolveOptions::ResolveOwnerType);
768 const QQmlJSScope::ConstPtr target = expressionType->semanticScope;
769 const QQmlJSScope::ConstPtr current = currentType->semanticScope;
770 if (target == current) {
771 auto tree = FileLocations::treeOf(toBeResolved);
772 QQmlJS::SourceLocation sourceLocation;
774 sourceLocation = FileLocations::region(tree, subRegion);
775 if (!sourceLocation.isValid())
778 if (
auto location = Location::tryFrom(toBeResolved.canonicalFilePath(),
779 sourceLocation, item)) {
785 auto findUsages = [&addLocationIfTypeMatchesTarget, &name,
786 &namesToCheck](Path,
const DomItem ¤t,
bool) ->
bool {
787 bool continueForChildren =
true;
788 if (
auto scope = current.semanticScope()) {
790 if (scope->ownJSIdentifier(name)) {
794 switch (current.internalKind()) {
795 case DomType::QmlObject:
796 case DomType::Binding:
797 case DomType::MethodInfo:
798 case DomType::PropertyDefinition: {
799 const QString propertyName = current.field(Fields::name).value().toString();
800 if (namesToCheck.contains(propertyName))
801 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
802 return continueForChildren;
804 case DomType::ScriptIdentifierExpression: {
805 const QString identifierName = current.field(Fields::identifier).value().toString();
806 if (namesToCheck.contains(identifierName))
807 addLocationIfTypeMatchesTarget(current, MainRegion);
808 return continueForChildren;
810 case DomType::ScriptLiteral: {
811 const QString literal = current.field(Fields::value).value().toString();
812 if (namesToCheck.contains(literal))
813 addLocationIfTypeMatchesTarget(current, MainRegion);
814 return continueForChildren;
816 case DomType::EnumItem: {
819 const auto parentPath = current.containingObject().pathFromOwner();
820 const auto index = parentPath.last().headIndex();
822 return continueForChildren;
823 const QString enumValue = current.field(Fields::name).value().toString();
824 if (namesToCheck.contains(enumValue))
825 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
826 return continueForChildren;
828 case DomType::EnumDecl: {
831 const auto parentPath = current.pathFromOwner();
832 const auto index = parentPath.last().headIndex();
834 return continueForChildren;
835 const QString enumValue = current.field(Fields::name).value().toString();
836 if (namesToCheck.contains(enumValue))
837 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
838 return continueForChildren;
841 return continueForChildren;
844 Q_UNREACHABLE_RETURN(continueForChildren);
847 const DomItem qmlFiles = item.top().field(Fields::qmlFileWithPath);
848 const auto filter = filterForFindUsages();
849 for (
const QString &file : qmlFiles.keys()) {
850 const DomItem currentFileComponents =
851 qmlFiles.key(file).field(Fields::currentItem).field(Fields::components);
852 currentFileComponents.visitTree(Path(), emptyChildrenVisitor,
853 VisitOption::Recurse | VisitOption::VisitSelf, findUsages,
854 emptyChildrenVisitor, filter);
861 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
862 && definitionOfItem.semanticScope()->ownJSIdentifier(name).has_value(),
863 "QQmlLSUtils::locationFromJSIdentifierDefinition",
864 "JS definition does not actually define the JS identifier. "
865 "Did you obtain definitionOfItem from findJSIdentifierDefinition() ?");
866 const QQmlJS::SourceLocation location =
867 definitionOfItem.semanticScope()->ownJSIdentifier(name).value().location;
869 return Location::tryFrom(definitionOfItem.canonicalFilePath(), location, definitionOfItem);
874 qCDebug(QQmlLSUtilsLog) <<
"Looking for JS identifier with name" << name;
875 DomItem definitionOfItem = findJSIdentifierDefinition(item, name);
878 if (!definitionOfItem) {
879 qCDebug(QQmlLSUtilsLog) <<
"No defining JS-Scope found!";
880 findUsagesOfNonJSIdentifiers(item, name, result);
884 definitionOfItem.visitTree(
885 Path(), emptyChildrenVisitor, VisitOption::VisitAdopted | VisitOption::Recurse,
886 [&name, &result](Path,
const DomItem &item,
bool) ->
bool {
887 qCDebug(QQmlLSUtilsLog) <<
"Visiting a " << item.internalKindStr();
888 if (item.internalKind() == DomType::ScriptIdentifierExpression
889 && item.field(Fields::identifier).value().toString() == name) {
891 auto fileLocation = FileLocations::treeOf(item);
893 qCWarning(QQmlLSUtilsLog) <<
"Failed finding filelocation of found usage";
896 const QQmlJS::SourceLocation sourceLocation = fileLocation->info().fullRegion;
897 const QString fileName = item.canonicalFilePath();
898 if (
auto location = Location::tryFrom(fileName, sourceLocation, item))
902 QQmlJSScope::ConstPtr scope = item.semanticScope();
904 if (scope && scope != item.directParent().semanticScope()
905 && scope->ownJSIdentifier(name)) {
914 emptyChildrenVisitor, filterForFindUsages());
916 if (
const auto definition = locationFromJSIdentifierDefinition(definitionOfItem, name))
924 switch (item.internalKind()) {
925 case DomType::ScriptIdentifierExpression: {
926 const QString name = item.field(Fields::identifier).value().toString();
927 findUsagesHelper(item, name, result);
930 case DomType::ScriptVariableDeclarationEntry: {
931 const QString name = item.field(Fields::identifier).value().toString();
932 findUsagesHelper(item, name, result);
935 case DomType::EnumDecl:
936 case DomType::EnumItem:
937 case DomType::QmlObject:
938 case DomType::PropertyDefinition:
939 case DomType::Binding:
940 case DomType::MethodInfo: {
941 const QString name = item.field(Fields::name).value().toString();
942 findUsagesHelper(item, name, result);
945 case DomType::QmlComponent: {
946 QString name = item.field(Fields::name).value().toString();
949 if (
const auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
950 name = name.sliced(dotIndex + 1);
951 findUsagesHelper(item, name, result);
955 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
956 <<
"was not implemented for QQmlLSUtils::findUsagesOf";
962 if (QQmlLSUtilsLog().isDebugEnabled()) {
963 qCDebug(QQmlLSUtilsLog) <<
"Found following usages in files:";
964 for (
auto r : result.usagesInFile()) {
965 qCDebug(QQmlLSUtilsLog) << r.filename() <<
" @ " << r.sourceLocation().startLine <<
":"
966 << r.sourceLocation().startColumn <<
" with length "
967 << r.sourceLocation().length;
969 qCDebug(QQmlLSUtilsLog) <<
"And following usages in file names:"
970 << result.usagesInFilename();
979 auto methods = scope->methods(name);
980 if (methods.isEmpty())
983 const bool isSignal = methods.front().methodType() == QQmlJSMetaMethodType::Signal;
990
991
992
993
994
995
996
997
998
1003 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1004 if (
auto type = hasMethodOrSignal(current, name)) {
1006 case ResolveOwnerType:
1007 return ExpressionType{ name, findDefiningScopeForMethod(current, name), *type };
1018 if (
const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
1019 if (
auto type = hasMethodOrSignal(current, *signalName)) {
1021 case ResolveOwnerType:
1022 return ExpressionType{ name, findDefiningScopeForMethod(current, *signalName),
1023 SignalHandlerIdentifier };
1036
1037
1038
1043 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1044 const auto resolved = resolveNameInQmlScope(propertyName, current);
1048 if (
auto property = current->property(resolved->name); property.isValid()) {
1050 case ResolveOwnerType:
1051 return ExpressionType{ propertyName,
1052 findDefiningScopeForProperty(current, resolved->name),
1055 return ExpressionType{ propertyName, property.type(), resolved->type };
1063
1064
1065
1066
1067
1068
1073 const auto bindings = referrerScope->propertyBindings(name);
1074 if (bindings.isEmpty())
1077 const auto binding = bindings.begin();
1078 const auto bindingType = binding->bindingType();
1079 const bool bindingIsAttached = bindingType == QQmlSA::BindingType::AttachedProperty;
1080 if (!bindingIsAttached && bindingType != QQmlSA::BindingType::GroupedProperty)
1086 if (!bindingIsAttached && !referrerScope->hasProperty(name)
1087 && referrerScope->isNameDeferred(name)) {
1088 if (!resolverForIds)
1093 resolverForIds->typeForId(referrerScope, name,
1094 QQmlJSScopesByIdOption::AssumeComponentsAreBound),
1099 const auto typeIdentifier =
1102 const auto getScope = [bindingIsAttached, binding]() -> QQmlJSScope::ConstPtr {
1103 if (bindingIsAttached)
1104 return binding->attachedType();
1106 return binding->groupType();
1111 return ExpressionType{ name,
1117 : findDefiningScopeForProperty(referrerScope, name),
1123 Q_UNREACHABLE_RETURN({});
1127
1128
1129
1131 const QQmlJSScope::ConstPtr &scope,
const DomItem &item)
1136 const QSet<QString> specialItems = {u"QQmlConnections"_s,
1137 u"QQuickPropertyChanges"_s,
1139 u"QQuickAnchorChanges"_s};
1141 const auto special = QQmlJSUtils::searchBaseAndExtensionTypes(
1142 scope, [&specialItems](
const QQmlJSScope::ConstPtr &visitedScope) {
1143 const auto typeName = visitedScope->internalName();
1144 if (specialItems.contains(typeName))
1154 if (scope->hasOwnPropertyBindings(u"target"_s)) {
1158 DomItem current = item.qmlObject();
1159 auto target = current.bindings().key(u"target"_s).index(0);
1161 targetName = target.field(Fields::value)
1162 .field(Fields::scriptElement)
1163 .field(Fields::identifier)
1169 if (!targetName.isEmpty()) {
1171 auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
1176 return resolver->scopedType(scope, targetName);
1179 if (item.internalKind() == DomType::Binding &&
1180 item.field(Fields::bindingType).value().toInteger() ==
int(BindingType::OnBinding)) {
1185 return scope->parentScope();
1189
1190
1191
1196 const auto scope = resolver->typeForName(name);
1200 if (scope->isSingleton())
1205 if (!isFieldMemberBase(item))
1209 const DomItem rightHandSide = [&item]() {
1210 const DomItem candidate = item.directParent().field(Fields::right);
1212 if (candidate != item)
1215 return item.directParent().directParent().field(Fields::right);
1218 if (rightHandSide.internalKind() != DomType::ScriptIdentifierExpression)
1221 const QString fieldMemberAccessName = rightHandSide.value().toString();
1222 if (fieldMemberAccessName.isEmpty() || !fieldMemberAccessName.front().isLower())
1225 if (scope->attachedType()) {
1235 const QString name = item.field(Fields::identifier).value().toString();
1236 DomItem parent = item.directParent();
1237 auto owner = resolveExpressionType(parent.field(Fields::left),
1238 ResolveOptions::ResolveActualTypeForFieldMemberExpression);
1242 if (!owner->semanticScope) {
1249 const QmlFile *qmlFile = item.fileObject().as<QmlFile>();
1252 if (
auto scope = resolveTypeName(
1253 qmlFile->typeResolver(), u"%1.%2"_s.arg(*owner->name, name), item,
1263 if (
auto scope = methodFromReferrerScope(owner->semanticScope, name, options))
1266 if (
auto scope = propertyBindingFromReferrerScope(owner->semanticScope, name, options,
nullptr))
1269 if (
auto scope = propertyFromReferrerScope(owner->semanticScope, name, options))
1277 if (
const auto scope = owner->semanticScope->isComposite()
1278 ? item.goToFile(owner->semanticScope->filePath())
1279 .rootQmlObject(GoTo::MostLikely)
1281 : owner->semanticScope) {
1282 if (scope->hasEnumerationKey(name)) {
1286 else if (scope->hasEnumeration(name)) {
1292 for (
auto it = owner->semanticScope->childScopesBegin(),
1293 end = owner->semanticScope->childScopesEnd();
1295 if ((*it)->inlineComponentName() == name) {
1302 qCDebug(QQmlLSUtilsLog) <<
"Could not find identifier expression for" << item.internalKindStr();
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1319 if (functionExpression.internalKind() != DomType::ScriptFunctionExpression)
1322 const DomItem parent = functionExpression.directParent();
1323 if (parent.internalKind() != DomType::ScriptExpression)
1326 const DomItem grandParent = parent.directParent();
1327 if (grandParent.internalKind() != DomType::Binding)
1330 auto bindingType = resolveExpressionType(grandParent, ResolveOwnerType);
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1351 const std::optional<QQmlJSScope::JavaScriptIdentifier> jsIdentifier =
1352 parameterDefinition.semanticScope()->jsIdentifier(name);
1353 if (!jsIdentifier || jsIdentifier->kind != QQmlJSScope::JavaScriptIdentifier::Parameter)
1356 const DomItem handlerFunctionExpression =
1357 parameterDefinition.internalKind() == DomType::ScriptBlockStatement
1358 ? parameterDefinition.directParent()
1359 : parameterDefinition;
1362 resolveBindingIfSignalHandler(handlerFunctionExpression);
1367 return ExpressionType{};
1372 const DomItem parameters = handlerFunctionExpression[Fields::parameters];
1373 const int indexOfParameter = [¶meters, &name]() {
1374 for (
int i = 0; i < parameters.indexes(); ++i) {
1375 if (parameters[i][Fields::identifier].value().toString() == name)
1378 Q_ASSERT_X(
false,
"resolveSignalHandlerParameter",
1379 "can't find JS identifier with Parameter kind in the parameters");
1380 Q_UNREACHABLE_RETURN(-1);
1383 const std::optional<QString> signalName =
1384 QQmlSignalNames::handlerNameToSignalName(*bindingType->name);
1385 Q_ASSERT_X(signalName.has_value(),
"resolveSignalHandlerParameterType",
1386 "handlerNameToSignalName failed on a SignalHandler");
1388 const QQmlJSMetaMethod signalDefinition =
1389 bindingType->semanticScope->methods(*signalName).front();
1390 const QList<QQmlJSMetaParameter> parameterList = signalDefinition.parameters();
1393 if (parameterList.size() <= indexOfParameter)
1401 const QQmlJSScope::ConstPtr parameterType = parameterList[indexOfParameter].type();
1407
1408
1409
1410
1411
1412
1413
1414
1418 if (isFieldMemberAccess(item) || isFieldMemberExpression(item)) {
1419 return resolveFieldMemberExpressionType(item, options);
1422 const QString name = item.field(Fields::identifier).value().toString();
1424 if (
const DomItem definitionOfItem = findJSIdentifierDefinition(item, name)) {
1425 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
1426 && definitionOfItem.semanticScope()->ownJSIdentifier(name),
1427 "QQmlLSUtils::findDefinitionOf",
1428 "JS definition does not actually define the JS identifer. "
1429 "It should be empty.");
1430 if (
auto parameter = resolveSignalHandlerParameterType(definitionOfItem, name, options))
1433 const auto scope = definitionOfItem.semanticScope();
1434 return ExpressionType{ name,
1435 options == ResolveOwnerType
1437 : QQmlJSScope::ConstPtr(
1438 scope->ownJSIdentifier(name)->scope.toStrongRef()),
1439 IdentifierType::JavaScriptIdentifier };
1442 const auto referrerScope = item.nearestSemanticScope();
1446 const auto qmlFile = item.containingFile().ownerAs<QmlFile>();
1450 const auto resolver = qmlFile->typeResolver();
1455 if (
const QQmlJSScope::ConstPtr fromId =
1456 resolver->typeForId(referrerScope, name, QQmlJSScopesByIdOption::AssumeComponentsAreBound)) {
1461 if (
const auto scope = propertyBindingFromReferrerScope(
1462 referrerScope, name, options, resolver.get())) {
1467 if (
const auto scope = propertyFromReferrerScope(referrerScope, name, options))
1471 if (
auto scope = methodFromReferrerScope(referrerScope, name, options))
1474 if (resolver->seenModuleQualifiers().contains(name))
1477 if (
const auto scope = resolveTypeName(resolver, name, item, options))
1482 const QQmlJSScope::ConstPtr jsGlobal = resolver->jsGlobalObject();
1484 if (
auto scope = methodFromReferrerScope(jsGlobal, name, options)) {
1490 if (
auto scope = propertyFromReferrerScope(jsGlobal, name, options)) {
1502 const auto signalOrProperty = resolveNameInQmlScope(name, scope);
1503 if (!signalOrProperty)
1506 switch (signalOrProperty->type) {
1509 case ResolveOwnerType:
1510 return ExpressionType{ name, findDefiningScopeForProperty(scope, name),
1511 signalOrProperty->type };
1513 return ExpressionType{ name, scope->property(name).type(), signalOrProperty->type };
1515 Q_UNREACHABLE_RETURN({});
1518 case ResolveOwnerType:
1519 return ExpressionType{ name,
1520 findDefiningScopeForProperty(scope, signalOrProperty->name),
1521 signalOrProperty->type };
1524 Q_UNREACHABLE_RETURN({});
1526 Q_UNREACHABLE_RETURN({});
1533 return ExpressionType{ name, findDefiningScopeForMethod(scope, signalOrProperty->name),
1534 signalOrProperty->type };
1538 Q_UNREACHABLE_RETURN({});
1540 Q_UNREACHABLE_RETURN({});
1542 Q_UNREACHABLE_RETURN({});
1547
1548
1549
1550
1551
1552
1553
1554
1555
1559 switch (item.internalKind()) {
1560 case DomType::ScriptIdentifierExpression: {
1561 return resolveIdentifierExpressionType(item, options);
1563 case DomType::PropertyDefinition: {
1564 auto propertyDefinition = item.as<PropertyDefinition>();
1565 if (propertyDefinition && propertyDefinition->semanticScope()) {
1566 const auto &scope = propertyDefinition->semanticScope();
1572 Q_UNREACHABLE_RETURN({});
1574 Q_UNREACHABLE_RETURN({});
1578 case DomType::Binding: {
1579 auto binding = item.as<Binding>();
1581 std::optional<QQmlJSScope::ConstPtr> owner = item.qmlObject().semanticScope();
1584 const QString name = binding->name();
1589 if (
const QQmlJSScope::ConstPtr targetScope
1590 = findScopeOfSpecialItems(owner.value(), item)) {
1591 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1592 if (!signalOrProperty)
1595 case ResolveOwnerType:
1596 return ExpressionType{
1597 name, findDefiningScopeForBinding(targetScope, signalOrProperty->name),
1598 signalOrProperty->type
1602 Q_UNREACHABLE_RETURN({});
1605 if (
auto result = resolveSignalOrPropertyExpressionType(name, owner.value(), options)) {
1608 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1609 "type of a Binding.";
1614 case DomType::QmlObject: {
1615 auto object = item.as<QmlObject>();
1618 if (
auto scope = object->semanticScope()) {
1619 const auto name = item.name();
1620 const bool isComponent = name.front().isUpper();
1622 scope = scope->baseType();
1634 case DomType::QmlComponent: {
1635 auto component = item.as<QmlComponent>();
1638 const auto scope = component->semanticScope();
1642 QString name = item.name();
1643 if (
auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1644 name = name.sliced(dotIndex + 1);
1651 Q_UNREACHABLE_RETURN({});
1653 case DomType::ScriptFunctionExpression: {
1656 case DomType::MethodInfo: {
1657 const auto object = item.as<MethodInfo>();
1658 if (object && object->semanticScope()) {
1659 std::optional<QQmlJSScope::ConstPtr> scope = object->semanticScope();
1663 if (
const QQmlJSScope::ConstPtr targetScope =
1664 findScopeOfSpecialItems(scope.value()->parentScope(), item)) {
1665 const auto signalOrProperty = resolveNameInQmlScope(object->name, targetScope);
1666 if (!signalOrProperty)
1670 case ResolveOwnerType:
1671 return ExpressionType{ object->name,
1672 findDefiningScopeForMethod(targetScope,
1673 signalOrProperty->name),
1674 signalOrProperty->type };
1683 if (QQmlSA::isFunctionScope(scope.value()->scopeType()))
1684 scope = scope.value()->parentScope();
1686 if (
const auto result = resolveSignalOrPropertyExpressionType(
1687 object->name, scope.value(), options)) {
1690 qDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::resolveExpressionType() could not resolve the"
1691 "type of a MethodInfo.";
1696 case DomType::ScriptBinaryExpression: {
1697 if (isFieldMemberExpression(item)) {
1698 return resolveExpressionType(item.field(Fields::right), options);
1702 case DomType::ScriptLiteral: {
1704
1705
1706 const auto scope = item.qmlObject().semanticScope();
1707 const auto name = item.field(Fields::value).value().toString();
1708 if (
const QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(scope, item)) {
1709 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1710 if (!signalOrProperty)
1713 case ResolveOwnerType:
1714 return ExpressionType{
1715 name, findDefiningScopeForProperty(targetScope, signalOrProperty->name),
1716 signalOrProperty->type
1721 Q_UNREACHABLE_RETURN({});
1726 case DomType::EnumItem: {
1727 const QString enumValue = item.field(Fields::name).value().toString();
1728 const QQmlJSScope::ConstPtr referrerScope
1729 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1730 if (!referrerScope->hasEnumerationKey(enumValue))
1735 case ResolveActualTypeForFieldMemberExpression:
1736 case ResolveOwnerType:
1737 return ExpressionType {
1739 findDefiningScopeForEnumerationKey(referrerScope, enumValue),
1740 EnumeratorValueIdentifier
1743 Q_UNREACHABLE_RETURN({});
1745 case DomType::EnumDecl: {
1746 const QString enumName = item.field(Fields::name).value().toString();
1747 const QQmlJSScope::ConstPtr referrerScope
1748 = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1749 if (!referrerScope->hasEnumeration(enumName))
1753 case ResolveActualTypeForFieldMemberExpression:
1754 case ResolveOwnerType:
1755 return ExpressionType {
1757 findDefiningScopeForEnumeration(referrerScope, enumName),
1758 EnumeratorIdentifier
1762 Q_UNREACHABLE_RETURN({});
1764 case DomType::Import: {
1766 if (!item[Fields::importId])
1768 return ExpressionType{ item[Fields::importId].value().toString(),
1770 QualifiedModuleIdentifier };
1772 case DomType::ScriptNewMemberExpression: {
1773 const auto name = item.field(Fields::base).value().toString();
1776 case DomType::ScriptCallExpression: {
1777 const DomItem callee = item.field(Fields::callee);
1778 const auto calleeExpressionType = resolveExpressionType(callee, ResolveOwnerType);
1780 if (!calleeExpressionType || !calleeExpressionType->semanticScope
1781 || !calleeExpressionType->name || calleeExpressionType->type !=
MethodIdentifier) {
1785 const auto methods =
1786 calleeExpressionType->semanticScope->methods(*calleeExpressionType->name);
1787 if (methods.isEmpty())
1790 const auto returnType = methods.front().returnType();
1794 qCDebug(QQmlLSUtilsLog) <<
"Type" << item.internalKindStr()
1795 <<
"is unimplemented in QQmlLSUtils::resolveExpressionType";
1805 auto items = QQmlLSUtils::itemsFromTextLocation(file, location.startLine - 1,
1806 location.startColumn - 1);
1807 switch (items.size()) {
1811 return items.front().domItem;
1817 auto &first = items.front();
1818 auto &second = items.back();
1819 Q_ASSERT_X(first.fileLocation->info().fullRegion.startLine
1820 == second.fileLocation->info().fullRegion.startLine,
1821 "QQmlLSUtils::findTypeDefinitionOf(DomItem)",
1822 "QQmlLSUtils::itemsFromTextLocation returned non-adjacent items.");
1823 if (first.fileLocation->info().fullRegion.startColumn
1824 > second.fileLocation->info().fullRegion.startColumn)
1825 return first.domItem;
1827 return second.domItem;
1831 qDebug() <<
"Found multiple candidates for type of scriptidentifierexpression";
1840 const DomItem enumeration = [&file, &location, &name]() -> DomItem {
1841 const DomItem enumerations = QQmlLSUtils::sourceLocationToDomItem(file, location)
1844 .field(Fields::enumerations);
1845 const QSet<QString> enumerationNames = enumerations.keys();
1846 for (
const QString &enumName : enumerationNames) {
1847 const DomItem currentKey = enumerations.key(enumName).index(0);
1848 if (enumName == name)
1850 const DomItem values = currentKey.field(Fields::values);
1851 for (
int i = 0, end = values.size(); i < end; ++i) {
1852 const DomItem currentValue = values.index(i);
1853 if (currentValue.field(Fields::name).value().toStringView() == name)
1854 return currentValue;
1860 auto fileLocation = FileLocations::treeOf(enumeration);
1865 auto regions = fileLocation->info().regions;
1867 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1868 return Location::tryFrom(enumeration.canonicalFilePath(), *it, file);
1877 DomItem owner = QQmlLSUtils::sourceLocationToDomItem(file, location).qmlObject();
1878 DomItem method = owner.field(Fields::methods).key(name).index(0);
1879 auto fileLocation = FileLocations::treeOf(method);
1883 auto regions = fileLocation->info().regions;
1885 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1886 return Location::tryFrom(method.canonicalFilePath(), *it, file);
1894 const QString &name)
1896 DomItem propertyOwner =
1897 QQmlLSUtils::sourceLocationToDomItem(file, propertyDefinitionLocation).qmlObject();
1898 DomItem propertyDefinition = propertyOwner.field(Fields::propertyDefs).key(name).index(0);
1899 auto fileLocation = FileLocations::treeOf(propertyDefinition);
1903 auto regions = fileLocation->info().regions;
1905 if (
auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1906 return Location::tryFrom(propertyDefinition.canonicalFilePath(), *it, file);
1914 return location.startLine == 0 ? QQmlJS::s_documentOrigin : location;
1918 const QStringList &headerLocations,
1919 const QQmlJS::SourceLocation &location)
1921 const TextPosition endPosition{
static_cast<
int>(location.startLine) + 1, 1 };
1922 const QFileInfo filePathInfo(fileName);
1923 if (filePathInfo.isAbsolute() && filePathInfo.exists())
1924 return Location{ fileName, location, endPosition };
1926 const QString filePath = findFilePathFromFileName(headerLocations, fileName, { });
1927 if (filePath.isEmpty()) {
1928 qCWarning(QQmlLSUtilsLog) <<
"Couldn't find file '%1'."_L1.arg(fileName);
1932 return Location{ filePath, location, endPosition };
1936
1937
1938
1939
1941 const QStringList &headerDirectories)
1945 if (scope->isComposite()) {
1946 if (
const auto result =
1947 Location::tryFrom(scope->filePath(), scope->sourceLocation(), item)) {
1951 QList<Location> result;
1952 auto locationOfTypeOrForeign = createCppTypeLocation(
1953 scope->filePath(), headerDirectories, sourceLocationOrDefault(scope->sourceLocation()));
1954 if (locationOfTypeOrForeign)
1955 result << *locationOfTypeOrForeign;
1957 auto resolvedTypeLocation = createCppTypeLocation(
1958 scope->resolvedFilePath(), headerDirectories,
1959 QQmlJS::SourceLocation{ 0u, 0u, scope->lineNumberInResolvedFile(), 1u });
1960 if (resolvedTypeLocation)
1961 result << *resolvedTypeLocation;
1966template <
typename T>
1970 return { *optional };
1976 auto resolvedExpression = resolveExpressionType(item, ResolveOptions::ResolveOwnerType);
1978 if (!resolvedExpression || !resolvedExpression->name
1979 || (!resolvedExpression->semanticScope
1981 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf: Type could not be resolved.";
1985 switch (resolvedExpression->type) {
1987 const auto jsIdentifier =
1988 resolvedExpression->semanticScope->ownJSIdentifier(*resolvedExpression->name);
1992 return optionalToList(Location::tryFrom(resolvedExpression->semanticScope->filePath(),
1993 jsIdentifier->location, item));
1998 if (!resolvedExpression->semanticScope->isComposite()) {
1999 return optionalToList(createCppTypeLocation(
2000 resolvedExpression->semanticScope->filePath(), headerDirectories,
2001 resolvedExpression->semanticScope->property(*resolvedExpression->name)
2002 .sourceLocation()));
2004 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2005 const QQmlJS::SourceLocation ownerLocation =
2006 resolvedExpression->semanticScope->sourceLocation();
2007 return optionalToList(
2008 findPropertyDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2015 if (!resolvedExpression->semanticScope->isComposite()) {
2016 const auto methods =
2017 resolvedExpression->semanticScope->methods(*resolvedExpression->name);
2018 if (methods.isEmpty())
2020 return optionalToList(
2021 createCppTypeLocation(resolvedExpression->semanticScope->filePath(),
2022 headerDirectories, methods.front().sourceLocation()));
2024 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2025 const QQmlJS::SourceLocation ownerLocation =
2026 resolvedExpression->semanticScope->sourceLocation();
2027 return optionalToList(
2028 findMethodDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2031 DomItem qmlObject = QQmlLSUtils::sourceLocationToDomItem(
2032 item.containingFile(), resolvedExpression->semanticScope->sourceLocation());
2034 const DomItem domId = qmlObject.component()
2036 .key(*resolvedExpression->name)
2038 .field(Fields::value);
2040 qCDebug(QQmlLSUtilsLog)
2041 <<
"QmlComponent in Dom structure has no id, was it misconstructed?";
2045 return optionalToList(Location::tryFrom(
2046 domId.canonicalFilePath(), FileLocations::treeOf(domId)->info().fullRegion, domId));
2048 case AttachedTypeIdentifier:
2049 return findDefinitionOfType(resolvedExpression->semanticScope->attachedType(), item,
2051 case AttachedTypeIdentifierInBindingTarget:
2052 return findDefinitionOfType(resolvedExpression->semanticScope->baseType(), item,
2054 case QmlComponentIdentifier:
2055 case SingletonIdentifier:
2056 return findDefinitionOfType(resolvedExpression->semanticScope, item, headerDirectories);
2058 const DomItem imports = item.fileObject().field(Fields::imports);
2059 for (
int i = 0; i < imports.indexes(); ++i) {
2060 if (imports[i][Fields::importId].value().toString() == resolvedExpression->name) {
2061 const auto fileLocations = FileLocations::treeOf(imports[i]);
2065 return optionalToList(Location::tryFrom(item.canonicalFilePath(),
2066 fileLocations->info().regions[IdNameRegion],
2074 if (!resolvedExpression->semanticScope->isComposite()) {
2075 const auto enumerations = resolvedExpression->semanticScope->enumerations();
2076 for (
const auto &enumeration : enumerations) {
2077 if (enumeration.hasKey(*resolvedExpression->name)
2078 || enumeration.name() == *resolvedExpression->name) {
2079 return optionalToList(createCppTypeLocation(
2080 resolvedExpression->semanticScope->filePath(), headerDirectories,
2081 sourceLocationOrDefault(QQmlJS::SourceLocation::fromQSizeType(
2082 0, 0, enumeration.lineNumber(), 1))));
2087 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
2088 const QQmlJS::SourceLocation ownerLocation =
2089 resolvedExpression->semanticScope->sourceLocation();
2090 return optionalToList(
2091 findEnumDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name));
2096 qCDebug(QQmlLSUtilsLog) <<
"QQmlLSUtils::findDefinitionOf was not implemented for type"
2097 << resolvedExpression->type;
2100 Q_UNREACHABLE_RETURN({});
2104 const QString &name)
2106 Q_ASSERT(!name.isEmpty());
2109 QQmlJSScope::ConstPtr typeWithDefinition = type;
2110 while (typeWithDefinition && !typeWithDefinition->hasOwnProperty(name))
2111 typeWithDefinition = typeWithDefinition->baseType();
2113 if (!typeWithDefinition) {
2114 qCDebug(QQmlLSUtilsLog)
2115 <<
"QQmlLSUtils::checkNameForRename cannot find property definition,"
2119 return typeWithDefinition;
2123 const QString &name)
2125 Q_ASSERT(!name.isEmpty());
2128 QQmlJSScope::ConstPtr typeWithDefinition = type;
2129 while (typeWithDefinition && !typeWithDefinition->hasOwnMethod(name))
2130 typeWithDefinition = typeWithDefinition->baseType();
2132 if (!typeWithDefinition) {
2133 qCDebug(QQmlLSUtilsLog)
2134 <<
"QQmlLSUtils::checkNameForRename cannot find method definition,"
2138 return typeWithDefinition;
2143 switch (ownerType
.type) {
2144 case PropertyIdentifier:
2145 return propertyOwnerFrom(ownerType.semanticScope, *ownerType.name);
2147 const auto propertyName =
2148 QQmlSignalNames::changedHandlerNameToPropertyName(*ownerType.name);
2149 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2153 const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(*ownerType.name);
2154 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
2156 case MethodIdentifier:
2157 case SignalIdentifier:
2158 return methodOwnerFrom(ownerType.semanticScope, *ownerType.name);
2160 const auto signalName = QQmlSignalNames::handlerNameToSignalName(*ownerType.name);
2161 return methodOwnerFrom(ownerType.semanticScope, *signalName);
2175 return ownerType.semanticScope;
2184 if (
const auto resolved = resolveExpressionType(item, ResolveOwnerType))
2185 return checkNameForRename(item, dirtyNewName, resolved);
2189 if (!isValidEcmaScriptIdentifier(dirtyNewName))
2190 return ErrorMessage{ 0, u"Invalid EcmaScript identifier!"_s };
2192 const auto userSemanticScope = item.nearestSemanticScope();
2194 if (!ownerType || !userSemanticScope) {
2195 return ErrorMessage{ 0, u"Requested item cannot be renamed."_s };
2199 switch (ownerType->type) {
2201 if (!QQmlSignalNames::isChangedSignalName(dirtyNewName)) {
2202 return ErrorMessage{ 0, u"Invalid name for a property changed signal."_s };
2207 if (!QQmlSignalNames::isChangedHandlerName(dirtyNewName)) {
2208 return ErrorMessage{ 0, u"Invalid name for a property changed handler identifier."_s };
2213 if (!QQmlSignalNames::isHandlerName(dirtyNewName)) {
2214 return ErrorMessage{ 0, u"Invalid name for a signal handler identifier."_s };
2220 if (dirtyNewName.front().isLetter() && !dirtyNewName.front().isLower()) {
2221 return ErrorMessage{ 0, u"Object id names cannot start with an upper case letter."_s };
2232 auto typeWithDefinition = expressionTypeWithDefinition(*ownerType);
2234 if (!typeWithDefinition) {
2235 return ErrorMessage{
2237 u"Renaming has not been implemented for the requested item."_s,
2242 if (!typeWithDefinition->isComposite()) {
2243 return ErrorMessage{ 0, u"Cannot rename items defined in non-QML files."_s };
2247 const QString moduleOfDefinition = ownerType->semanticScope->moduleName();
2248 const QString moduleOfCurrentItem = userSemanticScope->moduleName();
2249 if (moduleOfDefinition != moduleOfCurrentItem) {
2250 return ErrorMessage{
2252 u"Cannot rename items defined in the \"%1\" module from a usage in the \"%2\" module."_s
2253 .arg(moduleOfDefinition, moduleOfCurrentItem),
2262 switch (item.internalKind()) {
2263 case DomType::ScriptIdentifierExpression: {
2264 return item.field(Fields::identifier).value().toString();
2266 case DomType::ScriptVariableDeclarationEntry: {
2267 return item.field(Fields::identifier).value().toString();
2269 case DomType::PropertyDefinition:
2270 case DomType::Binding:
2271 case DomType::MethodInfo: {
2272 return item.field(Fields::name).value().toString();
2275 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
2276 <<
"was not implemented for QQmlLSUtils::renameUsagesOf";
2277 return std::nullopt;
2279 Q_UNREACHABLE_RETURN(
std::nullopt);
2287 switch (alternative) {
2289 return QQmlSignalNames::handlerNameToSignalName(dirtyNewName);
2292 return QQmlSignalNames::changedHandlerNameToPropertyName(dirtyNewName);
2295 return QQmlSignalNames::changedSignalNameToPropertyName(dirtyNewName);
2300 return std::nullopt;
2302 Q_UNREACHABLE_RETURN(
std::nullopt);
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2327 const Usages locations = findUsagesOf(item);
2331 auto oldName = oldNameFrom(item);
2335 QQmlJSScope::ConstPtr semanticScope;
2337 semanticScope = targetType->semanticScope;
2338 }
else if (
const auto resolved =
2339 QQmlLSUtils::resolveExpressionType(item, ResolveOptions::ResolveOwnerType)) {
2340 semanticScope = resolved->semanticScope;
2346 if (
const auto resolved = resolveNameInQmlScope(*oldName, semanticScope)) {
2347 newName = newNameFrom(dirtyNewName, resolved->type).value_or(dirtyNewName);
2348 oldName = resolved->name;
2350 newName = dirtyNewName;
2353 const qsizetype oldNameLength = oldName->length();
2354 const qsizetype oldHandlerNameLength =
2355 QQmlSignalNames::signalNameToHandlerName(*oldName).length();
2356 const qsizetype oldChangedSignalNameLength =
2357 QQmlSignalNames::propertyNameToChangedSignalName(*oldName).length();
2358 const qsizetype oldChangedHandlerNameLength =
2359 QQmlSignalNames::propertyNameToChangedHandlerName(*oldName).length();
2361 const QString newHandlerName = QQmlSignalNames::signalNameToHandlerName(newName);
2362 const QString newChangedSignalName = QQmlSignalNames::propertyNameToChangedSignalName(newName);
2363 const QString newChangedHandlerName =
2364 QQmlSignalNames::propertyNameToChangedHandlerName(newName);
2367 for (
const auto &location : locations.usagesInFile()) {
2368 const qsizetype currentLength = location.sourceLocation().length;
2370 edit.location = location;
2371 if (oldNameLength == currentLength) {
2373 edit.replacement = newName;
2375 }
else if (oldHandlerNameLength == currentLength) {
2377 edit.replacement = newHandlerName;
2379 }
else if (oldChangedSignalNameLength == currentLength) {
2381 edit.replacement = newChangedSignalName;
2383 }
else if (oldChangedHandlerNameLength == currentLength) {
2385 edit.replacement = newChangedHandlerName;
2388 qCDebug(QQmlLSUtilsLog) <<
"Found usage with wrong identifier length, ignoring...";
2391 result.appendRename(edit);
2394 for (
const auto &filename : locations.usagesInFilename()) {
2397 if (filename.endsWith(u".ui.qml"_s))
2398 extension = u".ui.qml"_s;
2399 else if (filename.endsWith(u".qml"_s))
2400 extension = u".qml"_s;
2404 QFileInfo info(filename);
2406 if (!info.isFile() || info.baseName() != oldName)
2409 const QString newFilename =
2410 QDir::cleanPath(filename +
"/.."_L1) +
'/'_L1 + newName + extension;
2411 result.appendRename({ filename, newFilename });
2418 const QQmlJS::SourceLocation &sourceLocation,
2419 const QQmlJS::Dom::DomItem &someItem)
2421 auto qmlFile = someItem.goToFile(fileName).ownerAs<QQmlJS::Dom::QmlFile>();
2423 qDebug() <<
"Could not find file" << fileName <<
"in the dom!";
2426 return Location{ fileName, sourceLocation,
2427 textRowAndColumnFrom(qmlFile->code(), sourceLocation.end()) };
2430Location Location::from(
const QString &fileName,
const QQmlJS::SourceLocation &sourceLocation,
const QString &code)
2432 return Location{ fileName, sourceLocation, textRowAndColumnFrom(code, sourceLocation.end()) };
2436 qsizetype startCharacter, qsizetype length)
2438 const auto offset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
2439 return from(fileName,
2440 QQmlJS::SourceLocation::fromQSizeType(offset, length, startLine, startCharacter),
2444Edit Edit::from(
const QString &fileName,
const QString &code, qsizetype startLine,
2445 qsizetype startCharacter, qsizetype length,
const QString &newName)
2448 rename.location = Location::from(fileName, code, startLine, startCharacter, length);
2449 rename.replacement = newName;
2455 QQmlJS::Lexer lexer(
nullptr);
2456 lexer.setCode(identifier.toString(), 0);
2457 const int token = lexer.lex();
2458 if (token !=
static_cast<
int>(QQmlJS::Lexer::T_IDENTIFIER))
2461 const int eofToken = lexer.lex();
2462 return eofToken ==
static_cast<
int>(QQmlJS::Lexer::EOF_SYMBOL);
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2481 std::pair<QString, QStringList> result{
2482 u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
2485 result.second <<
"-j"_L1 << QString::number(cmakeJobs);
2491 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2492 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2497 return m_usagesInFilename.isEmpty() && m_usagesInFile.isEmpty();
2500Usages::
Usages(
const QList<Location> &usageInFile,
const QList<QString> &usageInFilename)
2503 std::sort(m_usagesInFile.begin(), m_usagesInFile.end());
2504 std::sort(m_usagesInFilename.begin(), m_usagesInFilename.end());
2508 const QList<FileRename> &renamesInFilename)
2511 std::sort(m_renamesInFile.begin(), m_renamesInFile.end());
2512 std::sort(m_renamesInFilename.begin(), m_renamesInFilename.end());
2517 const QStringList &fileNamesToSearch,
2519 const QSet<QString> &ignoredFilePaths)
2521 if (fileNamesToSearch.isEmpty() || rootDirs.isEmpty())
2524 const qint64 maxFilesToSearch =
2525 qEnvironmentVariableIntegerValue(
"QMLLS_MAX_FILES_TO_SEARCH")
2527 qint64 visitedItems = 0;
2530 std::queue<QString> toVisit;
2531 for (
const QString &rootDir : rootDirs)
2532 toVisit.push(rootDir);
2534 while (!toVisit.empty()) {
2535 const QString current = toVisit.front();
2538 for (
const auto &entry : QDirListing{ current, QDirListing::IteratorFlag::ExcludeOther }) {
2541 if (++visitedItems > maxFilesToSearch) {
2542 qInfo(QQmlLSUtilsLog).noquote().nospace()
2543 <<
"Aborting search for \"" << fileNamesToSearch.join(
"\", \""_L1)
2544 <<
"\" inside \"" << rootDirs.join(
"\", \""_L1)
2545 <<
"\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to "
2547 <<
"). Set the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a "
2548 "higher value to spend more time on searching.";
2552 if (entry.isDir()) {
2553 toVisit.push(entry.filePath());
2556 Q_ASSERT(entry.isFile());
2557 if (!fileNamesToSearch.contains(entry.fileName()))
2560 if (ignoredFilePaths.contains(entry.absoluteFilePath()))
2563 if (option == FindFirst)
2564 return { entry.absoluteFilePath() };
2566 result << entry.absoluteFilePath();
2574 const QSet<QString> &ignoredFilePaths)
2576 return findFilePathsFromFileNamesImpl({ rootDir }, fileNamesToSearch, FindAll,
2581 const QSet<QString> &ignoredFilePaths)
2583 const QStringList result = findFilePathsFromFileNamesImpl(rootDirs, { fileNameToSearch },
2584 FindFirst, ignoredFilePaths);
2585 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< ExpressionType > propertyBindingFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name, ResolveOptions options, QQmlJSTypeResolver *resolverForIds)
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")