Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmllsutils.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmllsutils_p.h"
5
6#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
7#include <QtCore/qthreadpool.h>
8#include <QtCore/private/qduplicatetracker_p.h>
9#include <QtCore/QRegularExpression>
10#include <QtQmlDom/private/qqmldomexternalitems_p.h>
11#include <QtQmlDom/private/qqmldomtop_p.h>
12#include <QtQmlDom/private/qqmldomscriptelements_p.h>
13#include <QtQmlDom/private/qqmldom_utils_p.h>
14#include <QtQml/private/qqmlsignalnames_p.h>
15#include <QtQml/private/qqmljslexer_p.h>
16#include <QtQmlCompiler/private/qqmljsutils_p.h>
17
18#include <algorithm>
19#include <iterator>
20#include <memory>
21#include <optional>
22#include <set>
23#include <stack>
24#include <type_traits>
25#include <utility>
26#include <variant>
27
28using namespace QLspSpecification;
29using namespace QQmlJS::Dom;
30using namespace Qt::StringLiterals;
31
33
34Q_LOGGING_CATEGORY(QQmlLSUtilsLog, "qt.languageserver.utils")
35
36QString QQmlLSUtils::qualifiersFrom(const DomItem &el)
37{
38 const bool isAccess = QQmlLSUtils::isFieldMemberAccess(el);
40 return {};
41
42 const DomItem fieldMemberExpressionBeginning = el.filterUp(
44 FilterUpOptions::ReturnOuter);
45 QStringList qualifiers =
46 QQmlLSUtils::fieldMemberExpressionBits(fieldMemberExpressionBeginning, el);
47
49 for (const QString &qualifier : qualifiers)
50 result.append(qualifier).append(QChar(u'.'));
51 return result;
52}
53
59{
60 return item.internalKind() == DomType::ScriptBinaryExpression
61 && item.field(Fields::operation).value().toInteger()
63}
64
71{
72 auto parent = item.directParent();
73 if (!isFieldMemberExpression(parent))
74 return false;
75
76 DomItem rightHandSide = parent.field(Fields::right);
77 return item == rightHandSide;
78}
79
90{
91 const bool isAccess = isFieldMemberAccess(item);
92 const bool isExpression = isFieldMemberExpression(item);
93
94 // assume it is a non-qualified name
95 if (!isAccess && !isExpression)
96 return { item.value().toString() };
97
98 const DomItem stopMarker =
99 isFieldMemberExpression(stopAtChild) ? stopAtChild : stopAtChild.directParent();
100
102 DomItem current =
103 isAccess ? item.directParent() : (isFieldMemberExpression(item) ? item : DomItem{});
104
105 for (; isFieldMemberExpression(current); current = current.field(Fields::right)) {
106 result << current.field(Fields::left).value().toString();
107
108 if (current == stopMarker)
109 return result;
110 }
111 result << current.value().toString();
112
113 return result;
114}
115
131{
132 return uri;
133}
134
139
149QLspSpecification::Range QQmlLSUtils::qmlLocationToLspLocation(const QString &code,
150 QQmlJS::SourceLocation qmlLocation)
151{
152 Range range;
153
154 range.start.line = qmlLocation.startLine - 1;
155 range.start.character = qmlLocation.startColumn - 1;
156
157 auto end = QQmlLSUtils::textRowAndColumnFrom(code, qmlLocation.end());
158 range.end.line = end.line;
159 range.end.character = end.character;
160 return range;
161}
162
173{
174 int targetLine = row;
175 qsizetype i = 0;
176 while (i != text.size() && targetLine != 0) {
177 QChar c = text.at(i++);
178 if (c == u'\n') {
179 --targetLine;
180 }
181 if (c == u'\r') {
182 if (i != text.size() && text.at(i) == u'\n')
183 ++i;
184 --targetLine;
185 }
186 }
187 qsizetype leftChars = column;
188 while (i != text.size() && leftChars) {
189 QChar c = text.at(i);
190 if (c == u'\n' || c == u'\r')
191 break;
192 ++i;
193 if (!c.isLowSurrogate())
194 --leftChars;
195 }
196 return i;
197}
198
210{
211 int row = 0;
212 int column = 0;
213 qsizetype currentLineOffset = 0;
214 for (qsizetype i = 0; i < offset; i++) {
215 QChar c = text[i];
216 if (c == u'\n') {
217 row++;
218 currentLineOffset = i + 1;
219 } else if (c == u'\r') {
220 if (i > 0 && text[i - 1] == u'\n')
221 currentLineOffset++;
222 }
223 }
224 column = offset - currentLineOffset;
225
226 return { row, column };
227}
228
230handlePropertyDefinitionAndBindingOverlap(const QList<QQmlLSUtilsItemLocation> &items,
231 qsizetype offsetInFile)
232{
233 auto smallest = std::min_element(
234 items.begin(), items.end(),
236 return a.fileLocation->info().fullRegion.length
237 < b.fileLocation->info().fullRegion.length;
238 });
239
240 if (smallest->domItem.internalKind() == DomType::Binding) {
241 // weird edge case: the filelocations of property definitions and property bindings are
242 // actually overlapping, which means that qmlls cannot distinguish between bindings and
243 // bindings in property definitions. Those need to be treated differently for
244 // autocompletion, for example.
245 // Therefore: when inside a binding and a propertydefinition, choose the property definition
246 // if offsetInFile is before the colon, like for example:
247 // property var helloProperty: Rectangle { /*...*/ }
248 // |----return propertydef---|-- return Binding ---|
249
250 // get the smallest property definition to avoid getting the property definition that the
251 // current QmlObject is getting bound to!
252 auto smallestPropertyDefinition = std::min_element(
253 items.begin(), items.end(),
255 // make property definition smaller to avoid getting smaller items that are not
256 // property definitions
257 const bool aIsPropertyDefinition =
258 a.domItem.internalKind() == DomType::PropertyDefinition;
259 const bool bIsPropertyDefinition =
260 b.domItem.internalKind() == DomType::PropertyDefinition;
261 return aIsPropertyDefinition > bIsPropertyDefinition
262 && a.fileLocation->info().fullRegion.length
263 < b.fileLocation->info().fullRegion.length;
264 });
265
266 if (smallestPropertyDefinition->domItem.internalKind() != DomType::PropertyDefinition)
267 return smallest;
268
269 const auto propertyDefinitionColon =
270 smallestPropertyDefinition->fileLocation->info().regions[ColonTokenRegion];
271 const auto smallestColon = smallest->fileLocation->info().regions[ColonTokenRegion];
272 // sanity check: is it the definition of the current binding? check if they both have their
273 // ':' at the same location
274 if (propertyDefinitionColon.isValid() && propertyDefinitionColon == smallestColon
275 && offsetInFile < smallestColon.offset) {
276 return smallestPropertyDefinition;
277 }
278 }
279 return smallest;
280}
281
282static QList<QQmlLSUtilsItemLocation>
283filterItemsFromTextLocation(const QList<QQmlLSUtilsItemLocation> &items, qsizetype offsetInFile)
284{
285 if (items.size() < 2)
286 return items;
287
288 // if there are multiple items, take the smallest one + its neighbors
289 // this allows to prefer inline components over main components, when both contain the
290 // current textposition, and to disregard internal structures like property maps, which
291 // "contain" everything from their first-appearing to last-appearing property (e.g. also
292 // other stuff in between those two properties).
293
294 QList<QQmlLSUtilsItemLocation> filteredItems;
295
296 auto smallest = handlePropertyDefinitionAndBindingOverlap(items, offsetInFile);
297
298 filteredItems.append(*smallest);
299
300 const QQmlJS::SourceLocation smallestLoc = smallest->fileLocation->info().fullRegion;
301 const quint32 smallestBegin = smallestLoc.begin();
302 const quint32 smallestEnd = smallestLoc.end();
303
304 for (auto it = items.begin(); it != items.end(); it++) {
305 if (it == smallest)
306 continue;
307
308 const QQmlJS::SourceLocation itLoc = it->fileLocation->info().fullRegion;
309 const quint32 itBegin = itLoc.begin();
310 const quint32 itEnd = itLoc.end();
311 if (itBegin == smallestEnd || smallestBegin == itEnd) {
312 filteredItems.append(*it);
313 }
314 }
315 return filteredItems;
316}
317
326QList<QQmlLSUtilsItemLocation> QQmlLSUtils::itemsFromTextLocation(const DomItem &file, int line,
327 int character)
328{
329 QList<QQmlLSUtilsItemLocation> itemsFound;
330 std::shared_ptr<QmlFile> filePtr = file.ownerAs<QmlFile>();
331 if (!filePtr)
332 return itemsFound;
334 Q_ASSERT(t);
335 QString code = filePtr->code(); // do something more advanced wrt to changes wrt to this->code?
336 QList<QQmlLSUtilsItemLocation> toDo;
337 qsizetype targetPos = textOffsetFrom(code, line, character);
338 Q_ASSERT(targetPos >= 0);
339 auto containsTarget = [targetPos](QQmlJS::SourceLocation l) {
340 if constexpr (sizeof(qsizetype) <= sizeof(quint32)) {
341 return l.begin() <= quint32(targetPos) && quint32(targetPos) <= l.end();
342 } else {
343 return l.begin() <= targetPos && targetPos <= l.end();
344 }
345 };
346 if (containsTarget(t->info().fullRegion)) {
348 loc.domItem = file;
349 loc.fileLocation = t;
350 toDo.append(loc);
351 }
352 while (!toDo.isEmpty()) {
353 QQmlLSUtilsItemLocation iLoc = toDo.last();
354 toDo.removeLast();
355
356 bool inParentButOutsideChildren = true;
357
358 auto subEls = iLoc.fileLocation->subItems();
359 for (auto it = subEls.begin(); it != subEls.end(); ++it) {
360 auto subLoc = std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value());
361 Q_ASSERT(subLoc);
362
363 if (containsTarget(subLoc->info().fullRegion)) {
365 subItem.domItem = iLoc.domItem.path(it.key());
366 if (!subItem.domItem) {
367 qCDebug(QQmlLSUtilsLog)
368 << "A DomItem child is missing or the FileLocationsTree structure does "
369 "not follow the DomItem Structure.";
370 continue;
371 }
372 // the parser inserts empty Script Expressions for bindings that are not completely
373 // written out yet. Ignore them here.
374 if (subItem.domItem.internalKind() == DomType::ScriptExpression
375 && subLoc->info().fullRegion.length == 0) {
376 continue;
377 }
378 subItem.fileLocation = subLoc;
379 toDo.append(subItem);
380 inParentButOutsideChildren = false;
381 }
382 }
383 if (inParentButOutsideChildren) {
384 itemsFound.append(iLoc);
385 }
386 }
387
388 // filtering step:
389 auto filtered = filterItemsFromTextLocation(itemsFound, targetPos);
390 return filtered;
391}
392
394{
395 DomItem prototypes;
396 DomItem qmlObject = object.qmlObject();
397 // object is (or is inside) an inline component definition
398 if (object.internalKind() == DomType::QmlComponent || !qmlObject) {
399 prototypes = object.component()
400 .field(Fields::objects)
401 .index(0)
402 .field(QQmlJS::Dom::Fields::prototypes);
403 } else {
404 // object is (or is inside) a QmlObject
405 prototypes = qmlObject.field(QQmlJS::Dom::Fields::prototypes);
406 }
407 switch (prototypes.indexes()) {
408 case 0:
409 return {};
410 case 1:
411 break;
412 default:
413 qDebug() << "Multiple prototypes found for " << object.name() << ", taking the first one.";
414 break;
415 }
416 QQmlJS::Dom::DomItem base = prototypes.index(0).proceedToScope();
417 return base;
418}
419
420static std::optional<QQmlLSUtilsLocation> locationFromDomItem(const DomItem &item,
421 FileLocationRegion region)
422{
424 location.filename = item.canonicalFilePath();
425
426 auto tree = FileLocations::treeOf(item);
427 // tree is null for C++ defined types, for example
428 if (!tree)
429 return {};
430
431 location.sourceLocation = FileLocations::region(tree, region);
432 if (!location.sourceLocation.isValid() && region != QQmlJS::Dom::MainRegion)
433 location.sourceLocation = FileLocations::region(tree, MainRegion);
434 return location;
435}
436
449std::optional<QQmlLSUtilsLocation> QQmlLSUtils::findTypeDefinitionOf(const DomItem &object)
450{
451 DomItem typeDefinition;
452
453 switch (object.internalKind()) {
455 typeDefinition = object.field(Fields::objects).index(0);
456 break;
458 typeDefinition = baseObject(object);
459 break;
461 auto binding = object.as<Binding>();
462 Q_ASSERT(binding);
463
464 // try to grab the type from the bound object
465 if (binding->valueKind() == BindingValueKind::Object) {
466 typeDefinition = baseObject(object.field(Fields::value));
467 break;
468 } else {
469 // use the type of the property it is bound on for scriptexpression etc.
470 DomItem propertyDefinition;
471 const QString bindingName = binding->name();
472 object.containingObject().visitLookup(
473 bindingName,
474 [&propertyDefinition](const DomItem &item) {
475 if (item.internalKind() == QQmlJS::Dom::DomType::PropertyDefinition) {
476 propertyDefinition = item;
477 return false;
478 }
479 return true;
480 },
481 LookupType::PropertyDef);
482 typeDefinition = propertyDefinition.field(Fields::type).proceedToScope();
483 break;
484 }
485 Q_UNREACHABLE();
486 }
488 typeDefinition = object.field(Fields::referredObject).proceedToScope();
489 break;
493 typeDefinition = object.field(Fields::type).proceedToScope();
494 break;
496 if (DomItem type = object.filterUp(
497 [](DomType k, const DomItem &) { return k == DomType::ScriptType; },
498 FilterUpOptions::ReturnOuter)) {
499
500 const QString name = fieldMemberExpressionBits(type.field(Fields::typeName)).join(u'.');
501 switch (type.directParent().internalKind()) {
502 case DomType::QmlObject:
503 // is the type name of a QmlObject, like Item in `Item {...}`
504 typeDefinition = baseObject(type.directParent());
505 break;
506 case DomType::QmlComponent:
507 typeDefinition = type.directParent();
508 return locationFromDomItem(typeDefinition, FileLocationRegion::IdentifierRegion);
509 break;
510 default:
511 // is a type annotation, like Item in `function f(x: Item) { ... }`
512 typeDefinition = object.path(Paths::lookupTypePath(name));
513 if (typeDefinition.internalKind() == DomType::Export) {
514 typeDefinition = typeDefinition.field(Fields::type).get();
515 }
516 }
517 break;
518 }
519 if (DomItem id = object.filterUp(
520 [](DomType k, const DomItem &) { return k == DomType::Id; },
521 FilterUpOptions::ReturnOuter)) {
522
523 typeDefinition = id.field(Fields::referredObject).proceedToScope();
524 break;
525 }
526
529 if (!scope)
530 return {};
531
532 if (scope->type == QmlObjectIdIdentifier) {
533 return QQmlLSUtilsLocation{ scope->semanticScope->filePath(),
534 scope->semanticScope->sourceLocation() };
535 }
536
538 object.containingFile(), scope->semanticScope->sourceLocation());
539 return locationFromDomItem(typeDefinition.component(),
540 FileLocationRegion::IdentifierRegion);
541 }
542 default:
543 qDebug() << "QQmlLSUtils::findTypeDefinitionOf: Found unimplemented Type"
544 << object.internalKindStr();
545 return {};
546 }
547
548 return locationFromDomItem(typeDefinition, FileLocationRegion::MainRegion);
549}
550
551static bool findDefinitionFromItem(const DomItem &item, const QString &name)
552{
553 if (const QQmlJSScope::ConstPtr &scope = item.semanticScope()) {
554 qCDebug(QQmlLSUtilsLog) << "Searching for definition in" << item.internalKindStr();
555 if (auto jsIdentifier = scope->ownJSIdentifier(name)) {
556 qCDebug(QQmlLSUtilsLog) << "Found scope" << scope->baseTypeName();
557 return true;
558 }
559 }
560 return false;
561}
562
564{
565 DomItem definitionOfItem;
566 item.visitUp([&name, &definitionOfItem](const DomItem &i) {
568 definitionOfItem = i;
569 return false;
570 }
571 // early exit: no JS definitions/usages outside the ScriptExpression DOM element.
572 if (i.internalKind() == DomType::ScriptExpression)
573 return false;
574 return true;
575 });
576
577 if (definitionOfItem)
578 return definitionOfItem;
579
580 // special case: somebody asks for usages of a function parameter from its definition
581 // function parameters are defined in the method's scope
582 if (DomItem res = item.filterUp([](DomType k, const DomItem &) { return k == DomType::MethodInfo; },
583 FilterUpOptions::ReturnOuter)) {
584 DomItem candidate = res.field(Fields::body).field(Fields::scriptElement);
585 if (findDefinitionFromItem(candidate, name)) {
586 return candidate;
587 }
588 }
589
590 return definitionOfItem;
591}
592
607
622static std::optional<SignalOrProperty> resolveNameInQmlScope(const QString &name,
623 const QQmlJSScope::ConstPtr &owner)
624{
625 if (owner->hasProperty(name)) {
627 }
628
629 if (const auto propertyName = QQmlSignalNames::changedHandlerNameToPropertyName(name)) {
630 if (owner->hasProperty(*propertyName)) {
631 return SignalOrProperty{ *propertyName, PropertyChangedHandlerIdentifier };
632 }
633 }
634
635 if (const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
636 if (auto methods = owner->methods(*signalName); !methods.isEmpty()) {
637 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
638 return SignalOrProperty{ *signalName, SignalHandlerIdentifier };
639 }
640 }
641 }
642
643 if (const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
644 if (owner->hasProperty(*propertyName)) {
645 return SignalOrProperty{ *propertyName, PropertyChangedSignalIdentifier };
646 }
647 }
648
649 if (auto methods = owner->methods(name); !methods.isEmpty()) {
650 if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
652 }
654 }
655 return std::nullopt;
656}
657
664 const DomItem &item,
665 const QQmlJSScope::ConstPtr &targetType)
666{
667 QStringList namesToCheck = { name };
668 if (item.internalKind() == DomType::EnumItem || item.internalKind() == DomType::EnumDecl)
669 return namesToCheck;
670
671 auto namings = resolveNameInQmlScope(name, targetType);
672 if (!namings)
673 return namesToCheck;
674 switch (namings->type) {
675 case PropertyIdentifier: {
676 // for a property, also find bindings to its onPropertyChanged handler + propertyChanged
677 // signal
678 const QString propertyChangedHandler =
680 namesToCheck.append(propertyChangedHandler);
681
682 const QString propertyChangedSignal =
684 namesToCheck.append(propertyChangedSignal);
685 break;
686 }
688 // for a property changed handler, also find the usages of its property + propertyChanged
689 // signal
690 namesToCheck.append(namings->name);
691 namesToCheck.append(QQmlSignalNames::propertyNameToChangedSignalName(namings->name));
692 break;
693 }
695 // for a property changed signal, also find the usages of its property + onPropertyChanged
696 // handlers
697 namesToCheck.append(namings->name);
698 namesToCheck.append(QQmlSignalNames::propertyNameToChangedHandlerName(namings->name));
699 break;
700 }
701 case SignalIdentifier: {
702 // for a signal, also find bindings to its onSignalHandler.
703 namesToCheck.append(QQmlSignalNames::signalNameToHandlerName(namings->name));
704 break;
705 }
707 // for a signal handler, also find the usages of the signal it handles
708 namesToCheck.append(namings->name);
709 break;
710 }
711 default: {
712 break;
713 }
714 }
715 return namesToCheck;
716}
717
718template<typename Predicate>
720{
723 if (check(scope)) {
724 result = scope;
725 return true;
726 }
727 return false;
728 });
729
730 return result;
731}
732
741 const QString &nameToCheck)
742{
743 return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
744 return scope->hasOwnProperty(nameToCheck);
745 });
746}
747
755 const QString &nameToCheck)
756{
757 return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
758 return scope->hasOwnProperty(nameToCheck) || scope->hasOwnMethod(nameToCheck);
759 });
760}
761
767 const QString &nameToCheck)
768{
769 return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
770 return scope->hasOwnMethod(nameToCheck);
771 });
772}
773
779 const QString &nameToCheck)
780{
781 return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
782 return scope->hasOwnEnumeration(nameToCheck);
783 });
784}
785
791 const QString &nameToCheck)
792{
793 return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
794 return scope->hasOwnEnumerationKey(nameToCheck);
795 });
796}
797
810{
812 {
813 { QString(), QString::fromUtf16(Fields::propertyInfos) },
814 { QString(), QString::fromUtf16(Fields::defaultPropertyName) },
815 { QString(), QString::fromUtf16(Fields::get) },
816 } };
817 return filter;
818};
819
821 QList<QQmlLSUtilsLocation> &result)
822{
823 const auto expressionType = QQmlLSUtils::resolveExpressionType(item, ResolveOwnerType);
824 if (!expressionType)
825 return;
826
827 const QStringList namesToCheck = namesOfPossibleUsages(name, item, expressionType->semanticScope);
828
829 const auto addLocationIfTypeMatchesTarget = [&result,
830 &expressionType](const DomItem &toBeResolved,
831 FileLocationRegion subRegion) {
832 const auto currentType = QQmlLSUtils::resolveExpressionType(
834 if (!currentType)
835 return;
836
837 const QQmlJSScope::ConstPtr target = expressionType->semanticScope;
838 const QQmlJSScope::ConstPtr current = currentType->semanticScope;
839 if (target == current) {
840 auto tree = FileLocations::treeOf(toBeResolved);
841 QQmlJS::SourceLocation sourceLocation;
842
843 sourceLocation = FileLocations::region(tree, subRegion);
844 if (!sourceLocation.isValid())
845 return;
846
847 QQmlLSUtilsLocation location{ toBeResolved.canonicalFilePath(), sourceLocation };
848 if (!result.contains(location))
849 result.append(location);
850 }
851 };
852
853 auto findUsages = [&addLocationIfTypeMatchesTarget, &name,
854 &namesToCheck](Path, const DomItem &current, bool) -> bool {
855 bool continueForChildren = true;
856 if (auto scope = current.semanticScope()) {
857 // is the current property shadowed by some JS identifier? ignore current + its children
858 if (scope->ownJSIdentifier(name)) {
859 return false;
860 }
861 }
862 switch (current.internalKind()) {
863 case DomType::QmlObject:
864 case DomType::Binding:
865 case DomType::MethodInfo:
866 case DomType::PropertyDefinition: {
867 const QString propertyName = current.field(Fields::name).value().toString();
868 if (namesToCheck.contains(propertyName))
869 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
870 return continueForChildren;
871 }
872 case DomType::ScriptIdentifierExpression: {
873 const QString identifierName = current.field(Fields::identifier).value().toString();
874 if (namesToCheck.contains(identifierName))
875 addLocationIfTypeMatchesTarget(current, MainRegion);
876 return continueForChildren;
877 }
878 case DomType::ScriptLiteral: {
879 const QString literal = current.field(Fields::value).value().toString();
880 if (namesToCheck.contains(literal))
881 addLocationIfTypeMatchesTarget(current, MainRegion);
882 return continueForChildren;
883 }
884 case DomType::EnumItem: {
885 // Only look for the first enum defined. The inner enums
886 // have no way to be accessed.
887 const auto parentPath = current.containingObject().pathFromOwner();
888 const auto index = parentPath.last().headIndex();
889 if (index != 0)
890 return continueForChildren;
891 const QString enumValue = current.field(Fields::name).value().toString();
892 if (namesToCheck.contains(enumValue))
893 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
894 return continueForChildren;
895 }
896 case DomType::EnumDecl: {
897 // Only look for the first enum defined. The inner enums
898 // have no way to be accessed.
899 const auto parentPath = current.pathFromOwner();
900 const auto index = parentPath.last().headIndex();
901 if (index != 0)
902 return continueForChildren;
903 const QString enumValue = current.field(Fields::name).value().toString();
904 if (namesToCheck.contains(enumValue))
905 addLocationIfTypeMatchesTarget(current, IdentifierRegion);
906 return continueForChildren;
907 }
908 default:
909 return continueForChildren;
910 };
911
912 Q_UNREACHABLE_RETURN(continueForChildren);
913 };
914
915 const DomItem qmlFiles = item.top().field(Fields::qmlFileWithPath);
916 const auto filter = filterForFindUsages();
917 for (const QString &file : qmlFiles.keys()) {
918 const DomItem currentFileComponents =
919 qmlFiles.key(file).field(Fields::currentItem).field(Fields::components);
920 currentFileComponents.visitTree(Path(), emptyChildrenVisitor,
921 VisitOption::Recurse | VisitOption::VisitSelf, findUsages,
923 }
924}
925
927 const QString &name)
928{
929 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
930 && definitionOfItem.semanticScope()->ownJSIdentifier(name).has_value(),
931 "QQmlLSUtils::locationFromJSIdentifierDefinition",
932 "JS definition does not actually define the JS identifier. "
933 "Did you obtain definitionOfItem from findJSIdentifierDefinition() ?");
935 definitionOfItem.semanticScope()->ownJSIdentifier(name).value().location;
936
937 QQmlLSUtilsLocation result = { definitionOfItem.canonicalFilePath(), location };
938 return result;
939}
940
942 const DomItem &item, const QString &name, QList<QQmlLSUtilsLocation> &result)
943{
944 qCDebug(QQmlLSUtilsLog) << "Looking for JS identifier with name" << name;
945 DomItem definitionOfItem = findJSIdentifierDefinition(item, name);
946
947 // if there is no definition found: check if name was a property or an id instead
948 if (!definitionOfItem) {
949 qCDebug(QQmlLSUtilsLog) << "No defining JS-Scope found!";
951 return;
952 }
953
954 definitionOfItem.visitTree(
955 Path(), emptyChildrenVisitor, VisitOption::VisitAdopted | VisitOption::Recurse,
956 [&name, &result](Path, const DomItem &item, bool) -> bool {
957 qCDebug(QQmlLSUtilsLog) << "Visiting a " << item.internalKindStr();
958 if (item.internalKind() == DomType::ScriptIdentifierExpression
959 && item.field(Fields::identifier).value().toString() == name) {
960 // add this usage
961 auto fileLocation = FileLocations::treeOf(item);
962 if (!fileLocation) {
963 qCWarning(QQmlLSUtilsLog) << "Failed finding filelocation of found usage";
964 return true;
965 }
966 const QQmlJS::SourceLocation location = fileLocation->info().fullRegion;
967 const QString fileName = item.canonicalFilePath();
968 result.append({ fileName, location });
969 return true;
970 } else if (QQmlJSScope::ConstPtr scope = item.semanticScope();
971 scope && scope->ownJSIdentifier(name)) {
972 // current JS identifier has been redefined, do not visit children
973 return false;
974 }
975 return true;
976 },
978
979 const QQmlLSUtilsLocation definition =
980 locationFromJSIdentifierDefinition(definitionOfItem, name);
981 if (!result.contains(definition))
982 result.append(definition);
983}
984
985QList<QQmlLSUtilsLocation> QQmlLSUtils::findUsagesOf(const DomItem &item)
986{
987 QList<QQmlLSUtilsLocation> result;
988
989 switch (item.internalKind()) {
990 case DomType::ScriptIdentifierExpression: {
991 const QString name = item.field(Fields::identifier).value().toString();
993 break;
994 }
995 case DomType::ScriptVariableDeclarationEntry: {
996 const QString name = item.field(Fields::identifier).value().toString();
998 break;
999 }
1000 case DomType::EnumDecl:
1001 case DomType::EnumItem:
1002 case DomType::QmlObject:
1003 case DomType::PropertyDefinition:
1004 case DomType::Binding:
1005 case DomType::MethodInfo: {
1006 const QString name = item.field(Fields::name).value().toString();
1008 break;
1009 }
1010 case DomType::QmlComponent: {
1011 QString name = item.field(Fields::name).value().toString();
1012
1013 // get rid of extra qualifiers
1014 if (const auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1015 name = name.sliced(dotIndex + 1);
1017 break;
1018 }
1019 default:
1020 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
1021 << "was not implemented for QQmlLSUtils::findUsagesOf";
1022 return result;
1023 }
1024
1025 std::sort(result.begin(), result.end());
1026
1027 if (QQmlLSUtilsLog().isDebugEnabled()) {
1028 qCDebug(QQmlLSUtilsLog) << "Found following usages:";
1029 for (auto r : result) {
1030 qCDebug(QQmlLSUtilsLog)
1031 << r.filename << " @ " << r.sourceLocation.startLine << ":"
1032 << r.sourceLocation.startColumn << " with length " << r.sourceLocation.length;
1033 }
1034 }
1035
1036 return result;
1037}
1038
1039static std::optional<QQmlLSUtilsIdentifierType>
1041{
1042 auto methods = scope->methods(name);
1043 if (methods.isEmpty())
1044 return {};
1045
1046 const bool isSignal = methods.front().methodType() == QQmlJSMetaMethodType::Signal;
1049 return type;
1050}
1051
1062static std::optional<QQmlLSUtilsExpressionType>
1065{
1066 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1067 if (auto type = hasMethodOrSignal(current, name)) {
1068 switch (options) {
1069 case ResolveOwnerType:
1072 *type };
1074 // QQmlJSScopes were not implemented for methods yet, but JS functions have methods
1075 // and properties see
1076 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
1077 // for the list of properties/methods of functions. Therefore return a null scope.
1078 // see also code below for non-qualified method access
1079 return QQmlLSUtilsExpressionType{ name, {}, *type };
1080 }
1081 }
1082
1083 if (const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
1084 if (auto type = hasMethodOrSignal(current, *signalName)) {
1085 switch (options) {
1086 case ResolveOwnerType:
1088 name, findDefiningScopeForMethod(current, *signalName),
1090 };
1092 // Properties and methods of JS methods are not supported yet
1094 }
1095 }
1096 }
1097 }
1098 return {};
1099}
1100
1101
1106static std::optional<QQmlLSUtilsExpressionType>
1107propertyFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &propertyName,
1109{
1110 for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
1111 const auto resolved = resolveNameInQmlScope(propertyName, current);
1112 if (!resolved)
1113 continue;
1114
1115 if (auto property = current->property(resolved->name); property.isValid()) {
1116 switch (options) {
1117 case ResolveOwnerType:
1119 propertyName, findDefiningScopeForProperty(current, propertyName),
1120 resolved->type
1121 };
1123 return QQmlLSUtilsExpressionType{ propertyName, property.type(),
1124 resolved->type };
1125 }
1126 }
1127 }
1128 return {};
1129}
1130
1138static std::optional<QQmlLSUtilsExpressionType>
1141 QQmlJSTypeResolver *resolverForIds)
1142{
1143 auto bindings = referrerScope->propertyBindings(name);
1144 if (bindings.isEmpty())
1145 return {};
1146
1147 const auto binding = bindings.front();
1148
1149 if ((binding.bindingType() != QQmlSA::BindingType::AttachedProperty)
1150 && (binding.bindingType() != QQmlSA::BindingType::GroupProperty))
1151 return {};
1152
1153 const bool bindingIsAttached = binding.bindingType() == QQmlSA::BindingType::AttachedProperty;
1154
1155 // Generalized grouped properties, like Bindings or PropertyChanges, for example, have bindings
1156 // starting in an id (like `someId.someProperty: ...`).
1157 // If `someid` is not a property and is a deferred name, then it should be an id.
1158 if (!bindingIsAttached && !referrerScope->hasProperty(name)
1159 && referrerScope->isNameDeferred(name)) {
1160 if (!resolverForIds)
1161 return {};
1162
1163 QQmlJSRegisterContent fromId = resolverForIds->scopedType(
1168
1170 }
1171
1172 const auto typeIdentifier =
1174
1175 const auto getScope = [&bindingIsAttached, &binding]() -> QQmlJSScope::ConstPtr {
1176 if (bindingIsAttached)
1177 return binding.attachingType();
1178
1179 return binding.groupType();
1180 };
1181
1182 switch (options) {
1183 case ResolveOwnerType: {
1185 name,
1186 // note: always return the type of the attached type as the owner.
1187 // Find usages on "Keys.", for example, should yield all usages of the "Keys"
1188 // attached property.
1189 bindingIsAttached ? getScope() : findDefiningScopeForProperty(referrerScope, name),
1190 typeIdentifier
1191 };
1192 }
1194 return QQmlLSUtilsExpressionType{ name, getScope(), typeIdentifier };
1195 }
1196 Q_UNREACHABLE_RETURN({});
1197}
1198
1204{
1205 if (!scope)
1206 return {};
1207
1208 const QSet<QString> specialItems = {u"QQmlConnections"_s,
1209 u"QQuickPropertyChanges"_s,
1210 u"QQmlBind"_s,
1211 u"QQuickAnchorChanges"_s};
1212
1213 const auto special = QQmlJSUtils::searchBaseAndExtensionTypes(
1214 scope, [&specialItems](QQmlJSScope::ConstPtr visitedScope) {
1215 const auto typeName = visitedScope->internalName();
1216 if (specialItems.contains(typeName))
1217 return true;
1218 return false;
1219 });
1220
1221 if (!special)
1222 return {};
1223
1224 // Perform target name search if there is binding to property "target"
1225 QString targetName;
1226 if (scope->hasOwnPropertyBindings(u"target"_s)) {
1227 // TODO: propagate the whole binding.
1228 // We can figure out the meaning of target in more cases.
1229
1230 DomItem current = item.qmlObject();
1231 auto target = current.bindings().key(u"target"_s).index(0);
1232 if (target) {
1233 targetName = target.field(Fields::value)
1234 .field(Fields::scriptElement)
1235 .field(Fields::identifier)
1236 .value()
1237 .toString();
1238 }
1239 }
1240
1241 if (!targetName.isEmpty()) {
1242 // target: someId
1243 auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
1244 if (!resolver)
1245 return {};
1246
1247 // Note: It does not have to be an ID. It can be a property.
1248 return resolver->containedType(resolver->scopedType(scope, targetName));
1249 } else {
1250 if (item.internalKind() == DomType::Binding &&
1251 item.field(Fields::bindingType).value().toInteger() == int(BindingType::OnBinding)) {
1252 // Binding on sth : {} syntax
1253 // Target scope is the current scope
1254 return scope;
1255 }
1256 return scope->parentScope();
1257 }
1258
1259 return {};
1260}
1261
1262static std::optional<QQmlLSUtilsExpressionType>
1264{
1265 const QString name = item.field(Fields::identifier).value().toString();
1266 DomItem parent = item.directParent();
1268 parent.field(Fields::left),
1270 if (!owner)
1271 return {};
1272
1273 if (auto scope = methodFromReferrerScope(owner->semanticScope, name, options))
1274 return *scope;
1275
1276 if (auto scope = propertyBindingFromReferrerScope(owner->semanticScope, name, options, nullptr))
1277 return *scope;
1278
1279 if (auto scope = propertyFromReferrerScope(owner->semanticScope, name, options))
1280 return *scope;
1281
1282 // Ignore enum usages from other files for now.
1283 if (owner->type == QmlComponentIdentifier) {
1284 // Check if name is a enum value <TypeName>.<EnumValue>
1285 // Enumerations should live under the root element scope of the file that defines the enum,
1286 // therefore use the DomItem to find the root element of the qml file instead of directly
1287 // using owner->semanticScope.
1288 const auto scope = item.goToFile(owner->semanticScope->filePath())
1289 .rootQmlObject(GoTo::MostLikely)
1290 .semanticScope();
1291 if (scope->hasEnumerationKey(name)) {
1293 }
1294 // Or it is a enum name <TypeName>.<EnumName>.<EnumValue>
1295 else if (scope->hasEnumeration(name)) {
1297 }
1298
1299 // check inline components <TypeName>.<InlineComponentName>
1300 for (auto it = owner->semanticScope->childScopesBegin(),
1301 end = owner->semanticScope->childScopesEnd();
1302 it != end; ++it) {
1303 if ((*it)->inlineComponentName() == name) {
1305 }
1306 }
1307 return {};
1308 }
1309
1310 qCDebug(QQmlLSUtilsLog) << "Could not find identifier expression for" << item.internalKindStr();
1311 return owner;
1312}
1313
1314static std::optional<QQmlLSUtilsExpressionType>
1316{
1318 return resolveFieldMemberExpressionType(item, options);
1319 }
1320
1321 const QString name = item.field(Fields::identifier).value().toString();
1322
1323 if (DomItem definitionOfItem = findJSIdentifierDefinition(item, name)) {
1324 Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
1325 && definitionOfItem.semanticScope()->ownJSIdentifier(name),
1326 "QQmlLSUtils::findDefinitionOf",
1327 "JS definition does not actually define the JS identifer. "
1328 "It should be empty.");
1329 auto scope = definitionOfItem.semanticScope();
1330 auto jsIdentifier = scope->ownJSIdentifier(name);
1331 if (jsIdentifier->scope) {
1332 return QQmlLSUtilsExpressionType{ name, jsIdentifier->scope.toStrongRef(),
1334 } else {
1335 return QQmlLSUtilsExpressionType{ name, scope,
1337 }
1338 }
1339
1340 const auto referrerScope = item.nearestSemanticScope();
1341 if (!referrerScope)
1342 return {};
1343
1344 // check if its a method
1345 if (auto scope = methodFromReferrerScope(referrerScope, name, options))
1346 return scope;
1347
1348 const auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
1349 if (!resolver)
1350 return {};
1351
1352 // check if its found as a property binding
1353 if (auto scope = propertyBindingFromReferrerScope(referrerScope, name, options, resolver.get()))
1354 return *scope;
1355
1356 // check if its an (unqualified) property
1357 if (auto scope = propertyFromReferrerScope(referrerScope, name, options))
1358 return *scope;
1359
1360 // Returns the baseType, can't use it with options.
1361 if (auto scope = resolver->typeForName(name)) {
1362 if (scope->isSingleton())
1363 return QQmlLSUtilsExpressionType{ name, scope,
1365
1366 if (auto attachedScope = scope->attachedType()) {
1369 };
1370 }
1371
1372 // its a (inline) component!
1374 }
1375
1376 // check if its an id
1377 QQmlJSRegisterContent fromId =
1378 resolver->scopedType(referrerScope, name, QQmlJSRegisterContent::InvalidLookupIndex,
1382
1383 const QQmlJSScope::ConstPtr jsGlobal = resolver->jsGlobalObject();
1384 // check if its a JS global method
1385 if (auto scope = methodFromReferrerScope(jsGlobal, name, options))
1386 return scope;
1387
1388 // check if its an JS global property
1389 if (auto scope = propertyFromReferrerScope(jsGlobal, name, options))
1390 return *scope;
1391
1392 return {};
1393}
1394
1395static std::optional<QQmlLSUtilsExpressionType>
1398{
1399 auto signalOrProperty = resolveNameInQmlScope(name, scope);
1400 if (!signalOrProperty)
1401 return {};
1402
1403 switch (signalOrProperty->type) {
1404 case PropertyIdentifier:
1405 switch (options) {
1406 case ResolveOwnerType:
1408 signalOrProperty->type };
1410 return QQmlLSUtilsExpressionType{ name, scope->property(name).type(),
1411 signalOrProperty->type };
1412 }
1413 Q_UNREACHABLE_RETURN({});
1415 switch (options) {
1416 case ResolveOwnerType:
1418 name, findDefiningScopeForProperty(scope, signalOrProperty->name),
1419 signalOrProperty->type
1420 };
1422 // Properties and methods are not implemented on methods.
1423 Q_UNREACHABLE_RETURN({});
1424 }
1425 Q_UNREACHABLE_RETURN({});
1428 case SignalIdentifier:
1429 case MethodIdentifier:
1430 switch (options) {
1431 case ResolveOwnerType: {
1433 signalOrProperty->type };
1434 }
1436 // Properties and methods are not implemented on methods.
1437 Q_UNREACHABLE_RETURN({});
1438 }
1439 Q_UNREACHABLE_RETURN({});
1440 default:
1441 Q_UNREACHABLE_RETURN({});
1442 }
1443}
1444
1450std::optional<QQmlLSUtilsExpressionType>
1453{
1454 switch (item.internalKind()) {
1455 case DomType::ScriptIdentifierExpression: {
1456 return resolveIdentifierExpressionType(item, options);
1457 }
1458 case DomType::PropertyDefinition: {
1459 auto propertyDefinition = item.as<PropertyDefinition>();
1460 if (propertyDefinition && propertyDefinition->semanticScope()) {
1461 const auto &scope = propertyDefinition->semanticScope();
1462 switch (options) {
1463 case ResolveOwnerType:
1464 return QQmlLSUtilsExpressionType{ propertyDefinition->name, scope,
1467 // There should not be any PropertyDefinition inside a FieldMemberExpression.
1468 Q_UNREACHABLE_RETURN({});
1469 }
1470 Q_UNREACHABLE_RETURN({});
1471 }
1472 return {};
1473 }
1474 case DomType::Binding: {
1475 auto binding = item.as<Binding>();
1476 if (binding) {
1477 std::optional<QQmlJSScope::ConstPtr> owner = item.qmlObject().semanticScope();
1478 if (!owner)
1479 return {};
1480 const QString name = binding->name();
1481
1482 if (name == u"id")
1483 return QQmlLSUtilsExpressionType{ name, owner.value(), QmlObjectIdIdentifier };
1484
1485 if (QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(owner.value(), item)) {
1486 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1487 if (!signalOrProperty)
1488 return {};
1489 switch (options) {
1490 case ResolveOwnerType:
1492 name, findDefiningScopeForBinding(targetScope, signalOrProperty->name),
1493 signalOrProperty->type
1494 };
1496 // Bindings can't be inside of FieldMemberExpressions.
1497 Q_UNREACHABLE_RETURN({});
1498 }
1499 }
1500 if (auto result = resolveSignalOrPropertyExpressionType(name, owner.value(), options)) {
1501 return result;
1502 }
1503 qDebug(QQmlLSUtilsLog) << "QQmlLSUtils::resolveExpressionType() could not resolve the"
1504 "type of a Binding.";
1505 }
1506
1507 return {};
1508 }
1509 case DomType::QmlObject: {
1510 auto object = item.as<QmlObject>();
1511 if (!object)
1512 return {};
1513 if (auto scope = object->semanticScope()) {
1514 const auto name = item.name();
1515 const bool isComponent = name.front().isUpper();
1516 if (isComponent)
1517 scope = scope->baseType();
1520 switch (options) {
1521 case ResolveOwnerType:
1522 return QQmlLSUtilsExpressionType{ name, scope, type };
1524 return QQmlLSUtilsExpressionType{ name, scope, type};
1525 }
1526 }
1527 return {};
1528 }
1529 case DomType::QmlComponent: {
1530 auto component = item.as<QmlComponent>();
1531 if (!component)
1532 return {};
1533 const auto scope = component->semanticScope();
1534 if (!scope)
1535 return {};
1536
1537 QString name = item.name();
1538 if (auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
1539 name = name.sliced(dotIndex + 1);
1540 switch (options) {
1541 case ResolveOwnerType:
1545 }
1546 Q_UNREACHABLE_RETURN({});
1547 }
1548 case DomType::MethodInfo: {
1549 auto object = item.as<MethodInfo>();
1550 if (object && object->semanticScope()) {
1551 std::optional<QQmlJSScope::ConstPtr> scope = object->semanticScope();
1552 if (!scope)
1553 return {};
1554
1555 if (QQmlJSScope::ConstPtr targetScope =
1556 findScopeOfSpecialItems(scope.value()->parentScope(), item)) {
1557 const auto signalOrProperty = resolveNameInQmlScope(object->name, targetScope);
1558 if (!signalOrProperty)
1559 return {};
1560
1561 switch (options) {
1562 case ResolveOwnerType:
1563 return QQmlLSUtilsExpressionType{ object->name,
1565 targetScope, signalOrProperty->name),
1566 signalOrProperty->type };
1568 // not supported for methods
1569 return {};
1570 }
1571 }
1572
1573 // in case scope is the semantic scope for the function bodies: grab the owner's scope
1574 // this happens for all methods but not for signals (they do not have a body)
1575 if (scope.value()->scopeType() == QQmlJSScope::ScopeType::JSFunctionScope)
1576 scope = scope.value()->parentScope();
1577
1578 if (auto result = resolveSignalOrPropertyExpressionType(object->name, scope.value(),
1579 options)) {
1580 return result;
1581 }
1582 qDebug(QQmlLSUtilsLog) << "QQmlLSUtils::resolveExpressionType() could not resolve the"
1583 "type of a MethodInfo.";
1584 }
1585
1586 return {};
1587 }
1588 case DomType::ScriptBinaryExpression: {
1590 return resolveExpressionType(item.field(Fields::right), options);
1591 }
1592 return {};
1593 }
1594 case DomType::ScriptLiteral: {
1595 /* special case
1596 Binding { target: someId; property: "someProperty"}
1597 */
1598 const auto scope = item.qmlObject().semanticScope();
1599 const auto name = item.field(Fields::value).value().toString();
1600 if (QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(scope, item)) {
1601 const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
1602 if (!signalOrProperty)
1603 return {};
1604 switch (options) {
1605 case ResolveOwnerType:
1607 name, findDefiningScopeForProperty(targetScope, signalOrProperty->name),
1608 signalOrProperty->type
1609 };
1611 // ScriptLiteral's can't be inside of FieldMemberExpression's, especially when they
1612 // are inside a special item.
1613 Q_UNREACHABLE_RETURN({});
1614 }
1615 }
1616 return {};
1617 }
1618 case DomType::EnumItem: {
1619 const QString enumValue = item.field(Fields::name).value().toString();
1620 QQmlJSScope::ConstPtr referrerScope = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1621 if (!referrerScope->hasEnumerationKey(enumValue))
1622 return {};
1623 switch (options) {
1624 // special case: use the owner's scope here, as enums do not have their own
1625 // QQmlJSScope.
1627 case ResolveOwnerType:
1629 enumValue, findDefiningScopeForEnumerationKey(referrerScope, enumValue),
1631 };
1632 }
1633 Q_UNREACHABLE_RETURN({});
1634 }
1635 case DomType::EnumDecl: {
1636 const QString enumName = item.field(Fields::name).value().toString();
1637 QQmlJSScope::ConstPtr referrerScope = item.rootQmlObject(GoTo::MostLikely).semanticScope();
1638 if (!referrerScope->hasEnumeration(enumName))
1639 return {};
1640 switch (options) {
1641 // special case: use the owner's scope here, as enums do not have their own QQmlJSScope.
1643 case ResolveOwnerType:
1645 enumName, findDefiningScopeForEnumeration(referrerScope, enumName),
1647 };
1648 }
1649
1650 Q_UNREACHABLE_RETURN({});
1651 }
1652 default: {
1653 qCDebug(QQmlLSUtilsLog) << "Type" << item.internalKindStr()
1654 << "is unimplemented in QQmlLSUtils::resolveExpressionType";
1655 return {};
1656 }
1657 }
1658 Q_UNREACHABLE();
1659}
1660
1663{
1664 // QQmlJS::SourceLocation starts counting at 1 but the utils and the LSP start at 0.
1666 location.startColumn - 1);
1667 switch (items.size()) {
1668 case 0:
1669 return {};
1670 case 1:
1671 return items.front().domItem;
1672 case 2: {
1673 // special case: because location points to the beginning of the type definition,
1674 // itemsFromTextLocation might also return the type on its left, in case it is directly
1675 // adjacent to it. In this case always take the right (=with the higher column-number)
1676 // item.
1677 auto &first = items.front();
1678 auto &second = items.back();
1679 Q_ASSERT_X(first.fileLocation->info().fullRegion.startLine
1680 == second.fileLocation->info().fullRegion.startLine,
1681 "QQmlLSUtils::findTypeDefinitionOf(DomItem)",
1682 "QQmlLSUtils::itemsFromTextLocation returned non-adjacent items.");
1683 if (first.fileLocation->info().fullRegion.startColumn
1684 > second.fileLocation->info().fullRegion.startColumn)
1685 return first.domItem;
1686 else
1687 return second.domItem;
1688 break;
1689 }
1690 default:
1691 qDebug() << "Found multiple candidates for type of scriptidentifierexpression";
1692 break;
1693 }
1694 return {};
1695}
1696
1697static std::optional<QQmlLSUtilsLocation>
1699{
1701 DomItem method = owner.field(Fields::methods).key(name).index(0);
1702 auto fileLocation = FileLocations::treeOf(method);
1703 if (!fileLocation)
1704 return {};
1705
1706 auto regions = fileLocation->info().regions;
1707
1708 if (auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1710 result.sourceLocation = *it;
1711 result.filename = method.canonicalFilePath();
1712 return result;
1713 }
1714
1715 return {};
1716}
1717
1718static std::optional<QQmlLSUtilsLocation>
1720 const QString &name)
1721{
1722 DomItem propertyOwner =
1723 QQmlLSUtils::sourceLocationToDomItem(file, propertyDefinitionLocation).qmlObject();
1724 DomItem propertyDefinition = propertyOwner.field(Fields::propertyDefs).key(name).index(0);
1725 auto fileLocation = FileLocations::treeOf(propertyDefinition);
1726 if (!fileLocation)
1727 return {};
1728
1729 auto regions = fileLocation->info().regions;
1730
1731 if (auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
1733 result.sourceLocation = *it;
1734 result.filename = propertyDefinition.canonicalFilePath();
1735 return result;
1736 }
1737
1738 return {};
1739}
1740
1741std::optional<QQmlLSUtilsLocation> QQmlLSUtils::findDefinitionOf(const DomItem &item)
1742{
1743 auto resolvedExpression =
1745
1746 if (!resolvedExpression || !resolvedExpression->name || !resolvedExpression->semanticScope) {
1747 qCDebug(QQmlLSUtilsLog) << "QQmlLSUtils::findDefinitionOf: Type could not be resolved.";
1748 return {};
1749 }
1750
1751 switch (resolvedExpression->type) {
1752 case JavaScriptIdentifier: {
1754 resolvedExpression->semanticScope->ownJSIdentifier(*resolvedExpression->name)
1755 .value()
1756 .location;
1757
1758 return QQmlLSUtilsLocation{ resolvedExpression->semanticScope->filePath(), location };
1759 }
1760
1761 case PropertyIdentifier: {
1762 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
1763 const QQmlJS::SourceLocation ownerLocation =
1764 resolvedExpression->semanticScope->sourceLocation();
1765 return findPropertyDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
1766 }
1769 case SignalIdentifier:
1771 case MethodIdentifier: {
1772 const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
1773 const QQmlJS::SourceLocation ownerLocation =
1774 resolvedExpression->semanticScope->sourceLocation();
1775 return findMethodDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
1776 }
1777 case QmlObjectIdIdentifier: {
1779 item.containingFile(), resolvedExpression->semanticScope->sourceLocation());
1780 // in the Dom, the id is saved in a QMultiHash inside the Component of an QmlObject.
1781 const DomItem domId = qmlObject.component()
1782 .field(Fields::ids)
1783 .key(*resolvedExpression->name)
1784 .index(0)
1785 .field(Fields::value);
1786 if (!domId) {
1787 qCDebug(QQmlLSUtilsLog)
1788 << "QmlComponent in Dom structure has no id, was it misconstructed?";
1789 return {};
1790 }
1791
1793 result.sourceLocation = FileLocations::treeOf(domId)->info().fullRegion;
1794 result.filename = domId.canonicalFilePath();
1795 return result;
1796 }
1799 result.sourceLocation = resolvedExpression->semanticScope->sourceLocation();
1800 result.filename = resolvedExpression->semanticScope->filePath();
1801 return result;
1802 }
1808 qCDebug(QQmlLSUtilsLog) << "QQmlLSUtils::findDefinitionOf was not implemented for type"
1809 << resolvedExpression->type;
1810 return {};
1811 }
1812 Q_UNREACHABLE_RETURN({});
1813}
1814
1816 const QString &name)
1817{
1818 Q_ASSERT(!name.isEmpty());
1819 Q_ASSERT(type);
1820
1821 QQmlJSScope::ConstPtr typeWithDefinition = type;
1822 while (typeWithDefinition && !typeWithDefinition->hasOwnProperty(name))
1823 typeWithDefinition = typeWithDefinition->baseType();
1824
1825 if (!typeWithDefinition) {
1826 qCDebug(QQmlLSUtilsLog)
1827 << "QQmlLSUtils::checkNameForRename cannot find property definition,"
1828 " ignoring.";
1829 }
1830
1831 return typeWithDefinition;
1832}
1833
1835 const QString &name)
1836{
1837 Q_ASSERT(!name.isEmpty());
1838 Q_ASSERT(type);
1839
1840 QQmlJSScope::ConstPtr typeWithDefinition = type;
1841 while (typeWithDefinition && !typeWithDefinition->hasOwnMethod(name))
1842 typeWithDefinition = typeWithDefinition->baseType();
1843
1844 if (!typeWithDefinition) {
1845 qCDebug(QQmlLSUtilsLog)
1846 << "QQmlLSUtils::checkNameForRename cannot find method definition,"
1847 " ignoring.";
1848 }
1849
1850 return typeWithDefinition;
1851}
1852
1855{
1856 switch (ownerType.type) {
1857 case PropertyIdentifier:
1858 return propertyOwnerFrom(ownerType.semanticScope, *ownerType.name);
1860 const auto propertyName =
1862 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
1863 break;
1864 }
1866 const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(*ownerType.name);
1867 return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
1868 }
1869 case MethodIdentifier:
1870 case SignalIdentifier:
1871 return methodOwnerFrom(ownerType.semanticScope, *ownerType.name);
1873 const auto signalName = QQmlSignalNames::handlerNameToSignalName(*ownerType.name);
1874 return methodOwnerFrom(ownerType.semanticScope, *signalName);
1875 }
1884 return ownerType.semanticScope;
1885 }
1886 return {};
1887}
1888
1889std::optional<QQmlLSUtilsErrorMessage> QQmlLSUtils::checkNameForRename(
1890 const DomItem &item, const QString &dirtyNewName,
1891 const std::optional<QQmlLSUtilsExpressionType> &ownerType)
1892{
1893 if (!ownerType) {
1894 if (const auto resolved = QQmlLSUtils::resolveExpressionType(item, ResolveOwnerType))
1895 return checkNameForRename(item, dirtyNewName, resolved);
1896 }
1897
1898 // general checks for ECMAscript identifiers
1899 if (!isValidEcmaScriptIdentifier(dirtyNewName))
1900 return QQmlLSUtilsErrorMessage{ 0, u"Invalid EcmaScript identifier!"_s };
1901
1902 const auto userSemanticScope = item.nearestSemanticScope();
1903
1904 if (!ownerType || !userSemanticScope) {
1905 return QQmlLSUtilsErrorMessage{ 0, u"Requested item cannot be renamed"_s };
1906 }
1907
1908 // type specific checks
1909 switch (ownerType->type) {
1911 if (!QQmlSignalNames::isChangedSignalName(dirtyNewName)) {
1912 return QQmlLSUtilsErrorMessage{ 0, u"Invalid name for a property changed signal."_s };
1913 }
1914 break;
1915 }
1917 if (!QQmlSignalNames::isChangedHandlerName(dirtyNewName)) {
1919 0, u"Invalid name for a property changed handler identifier."_s
1920 };
1921 }
1922 break;
1923 }
1925 if (!QQmlSignalNames::isHandlerName(dirtyNewName)) {
1926 return QQmlLSUtilsErrorMessage{ 0, u"Invalid name for a signal handler identifier."_s };
1927 }
1928 break;
1929 }
1930 // TODO: any other specificities?
1932 if (dirtyNewName.front().isLetter() && !dirtyNewName.front().isLower()) {
1934 0, u"Object id names cannot start with an upper case letter."_s
1935 };
1936 }
1937 break;
1939 case PropertyIdentifier:
1940 case SignalIdentifier:
1941 case MethodIdentifier:
1942 default:
1943 break;
1944 };
1945
1946 auto typeWithDefinition = expressionTypeWithDefinition(*ownerType);
1947
1948 if (!typeWithDefinition) {
1950 0,
1951 u"Renaming has not been implemented for the requested item."_s,
1952 };
1953 }
1954
1955 // is it not defined in QML?
1956 if (!typeWithDefinition->isComposite()) {
1957 return QQmlLSUtilsErrorMessage{ 0, u"Cannot rename items defined in non-QML files."_s };
1958 }
1959
1960 // is it defined in the current module?
1961 const QString moduleOfDefinition = ownerType->semanticScope->moduleName();
1962 const QString moduleOfCurrentItem = userSemanticScope->moduleName();
1963 if (moduleOfDefinition != moduleOfCurrentItem) {
1965 0,
1966 u"Cannot rename items defined in the %1 module fromits usage in the %2 module."_s
1967 .arg(moduleOfDefinition, moduleOfCurrentItem),
1968 };
1969 }
1970
1971 return {};
1972}
1973
1974static std::optional<QString> oldNameFrom(const DomItem &item)
1975{
1976 switch (item.internalKind()) {
1977 case DomType::ScriptIdentifierExpression: {
1978 return item.field(Fields::identifier).value().toString();
1979 }
1980 case DomType::ScriptVariableDeclarationEntry: {
1981 return item.field(Fields::identifier).value().toString();
1982 }
1983 case DomType::PropertyDefinition:
1984 case DomType::Binding:
1985 case DomType::MethodInfo: {
1986 return item.field(Fields::name).value().toString();
1987 }
1988 default:
1989 qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
1990 << "was not implemented for QQmlLSUtils::renameUsagesOf";
1991 return std::nullopt;
1992 }
1993 Q_UNREACHABLE_RETURN(std::nullopt);
1994}
1995
1996static std::optional<QString> newNameFrom(const QString &dirtyNewName,
1997 QQmlLSUtilsIdentifierType alternative)
1998{
1999 // When renaming signal/property changed handlers and property changed signals:
2000 // Get the actual corresponding signal name (for signal handlers) or property name (for
2001 // property changed signal + handlers) that will be used for the renaming.
2002 switch (alternative) {
2004 return QQmlSignalNames::handlerNameToSignalName(dirtyNewName);
2005 }
2008 }
2011 }
2012 case SignalIdentifier:
2013 case PropertyIdentifier:
2014 default:
2015 return std::nullopt;
2016 }
2017 Q_UNREACHABLE_RETURN(std::nullopt);
2018}
2019
2038QList<QQmlLSUtilsEdit> QQmlLSUtils::renameUsagesOf(
2039 const DomItem &item, const QString &dirtyNewName,
2040 const std::optional<QQmlLSUtilsExpressionType> &targetType)
2041{
2042 QList<QQmlLSUtilsEdit> results;
2043 const QList<QQmlLSUtilsLocation> locations = findUsagesOf(item);
2044 if (locations.isEmpty())
2045 return results;
2046
2047 auto oldName = oldNameFrom(item);
2048 if (!oldName)
2049 return results;
2050
2051 QQmlJSScope::ConstPtr semanticScope;
2052 if (targetType) {
2053 semanticScope = targetType->semanticScope;
2054 } else if (const auto resolved = QQmlLSUtils::resolveExpressionType(
2056 semanticScope = resolved->semanticScope;
2057 } else {
2058 return results;
2059 }
2060
2061 QString newName;
2062 if (const auto resolved = resolveNameInQmlScope(*oldName, semanticScope)) {
2063 newName = newNameFrom(dirtyNewName, resolved->type).value_or(dirtyNewName);
2064 oldName = resolved->name;
2065 } else {
2066 newName = dirtyNewName;
2067 }
2068
2069 const qsizetype oldNameLength = oldName->length();
2070 const qsizetype oldHandlerNameLength =
2072 const qsizetype oldChangedSignalNameLength =
2074 const qsizetype oldChangedHandlerNameLength =
2076
2077 const QString newHandlerName = QQmlSignalNames::signalNameToHandlerName(newName);
2078 const QString newChangedSignalName = QQmlSignalNames::propertyNameToChangedSignalName(newName);
2079 const QString newChangedHandlerName =
2081
2082 // set the new name at the found usages, but add "on"-prefix and "Changed"-suffix if needed
2083 for (const auto &location : locations) {
2084 const qsizetype currentLength = location.sourceLocation.length;
2086 edit.location = location;
2087 if (oldNameLength == currentLength) {
2088 // normal case, nothing to do
2089 edit.replacement = newName;
2090
2091 } else if (oldHandlerNameLength == currentLength) {
2092 // signal handler location
2093 edit.replacement = newHandlerName;
2094
2095 } else if (oldChangedSignalNameLength == currentLength) {
2096 // property changed signal location
2097 edit.replacement = newChangedSignalName;
2098
2099 } else if (oldChangedHandlerNameLength == currentLength) {
2100 // property changed handler location
2101 edit.replacement = newChangedHandlerName;
2102
2103 } else {
2104 qCDebug(QQmlLSUtilsLog) << "Found usage with wrong identifier length, ignoring...";
2105 continue;
2106 }
2108 }
2109
2110 return results;
2111}
2112
2114 quint32 startLine, quint32 startCharacter,
2116{
2117 quint32 offset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
2118
2120 fileName, QQmlJS::SourceLocation{ offset, length, startLine, startCharacter }
2121 };
2122 return location;
2123}
2124
2126 quint32 startLine, quint32 startCharacter, quint32 length,
2127 const QString &newName)
2128{
2129 QQmlLSUtilsEdit rename;
2130 rename.location = QQmlLSUtilsLocation::from(fileName, code, startLine, startCharacter, length);
2131 rename.replacement = newName;
2132 return rename;
2133}
2134
2136{
2137 QQmlJS::Lexer lexer(nullptr);
2138 lexer.setCode(identifier.toString(), 0);
2139 const int token = lexer.lex();
2140 if (token != static_cast<int>(QQmlJS::Lexer::T_IDENTIFIER))
2141 return false;
2142 // make sure there is nothing following the lexed identifier
2143 const int eofToken = lexer.lex();
2144 return eofToken == static_cast<int>(QQmlJS::Lexer::EOF_SYMBOL);
2145}
2146
2147
2161QPair<QString, QStringList> QQmlLSUtils::cmakeBuildCommand(const QString &path)
2162{
2163 const QPair<QString, QStringList> result{
2164 u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
2165 };
2166 return result;
2167}
2168
2169
2171{
2173 const auto [line, character] = position;
2174 const auto itemLocation = itemsFromTextLocation(file, line, character);
2175
2176 // TODO:
2177 // Process found item's internalKind and fetch its documentation.
2178 Q_UNUSED(itemLocation);
2179
2180 return result;
2181}
2182
static JNINativeMethod methods[]
\inmodule QtCore
Definition qbytearray.h:57
QString toString(const QString &defaultValue={}) const
Returns the string value stored in this QCborValue, if it is of the string type.
\inmodule QtCore
qsizetype size() const noexcept
Definition qlist.h:397
reference back()
Definition qlist.h:689
iterator end()
Definition qlist.h:626
reference front()
Definition qlist.h:687
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
QSharedPointer< const QQmlJSScope > type() const
ContentVariant variant() const
QQmlJSScope::ConstPtr type() const
bool hasOwnPropertyBindings(const QString &name) const
QHash< QString, QQmlJSMetaMethod > methods() const
Returns all methods visible from this scope including those of base types and extensions.
QQmlJSScope::Ptr parentScope()
QQmlJSMetaProperty property(const QString &name) const
bool hasOwnEnumeration(const QString &name) const
bool hasOwnEnumerationKey(const QString &name) const
bool hasOwnProperty(const QString &name) const
QQmlJSScope::ConstPtr baseType() const
bool hasProperty(const QString &name) const
bool hasOwnMethod(const QString &name) const
QQmlJSScope::ConstPtr semanticScope() const
DomItem field(QStringView name) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
QCborValue value() const
DomItem index(index_type) const
DomItem component(GoTo option=GoTo::Strict) const
DomItem qmlObject(GoTo option=GoTo::Strict, FilterUpOptions options=FilterUpOptions::ReturnOuter) const
DomItem key(const QString &name) const
static QQmlJS::SourceLocation region(const Tree &fLoc, FileLocationRegion region)
std::shared_ptr< AttachedInfoT< FileLocations > > Tree
static FileLocations::Tree treeOf(const DomItem &)
A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,...
FileLocations::Tree fileLocationsTree() const
void setCode(const QString &code, int lineno, bool qmlMode=true, CodeContinuation codeContinuation=CodeContinuation::Reset)
static QList< QQmlLSUtilsLocation > findUsagesOf(const DomItem &item)
static bool isValidEcmaScriptIdentifier(QStringView view)
static QPair< QString, QStringList > cmakeBuildCommand(const QString &path)
static QList< QQmlLSUtilsItemLocation > 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< QQmlLSUtilsErrorMessage > checkNameForRename(const DomItem &item, const QString &newName, const std::optional< QQmlLSUtilsExpressionType > &targetType=std::nullopt)
static DomItem baseObject(const DomItem &qmlObject)
static QByteArray lspUriToQmlUrl(const QByteArray &uri)
static DomItem sourceLocationToDomItem(const DomItem &file, const QQmlJS::SourceLocation &location)
static std::optional< QQmlLSUtilsExpressionType > resolveExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions)
static QStringList fieldMemberExpressionBits(const DomItem &item, const DomItem &stopAtChild={})
static QByteArray qmlUrlToLspUri(const QByteArray &url)
static bool isFieldMemberExpression(const DomItem &item)
static bool isFieldMemberAccess(const DomItem &item)
static QLspSpecification::Range qmlLocationToLspLocation(const QString &code, QQmlJS::SourceLocation qmlLocation)
Converts a QQmlJS::SourceLocation to a LSP Range.
static std::optional< QQmlLSUtilsLocation > findTypeDefinitionOf(const DomItem &item)
Returns the location of the type definition pointed by object.
static std::optional< QQmlLSUtilsLocation > findDefinitionOf(const DomItem &item)
static qsizetype textOffsetFrom(const QString &code, int row, int character)
Convert a text position from (line, column) into an offset.
static QList< QQmlLSUtilsEdit > renameUsagesOf(const DomItem &item, const QString &newName, const std::optional< QQmlLSUtilsExpressionType > &targetType=std::nullopt)
Rename the appearance of item to newName.
static QByteArray getDocumentationFromLocation(const DomItem &file, const QQmlLSUtilsTextPosition &position)
static QQmlLSUtilsTextPosition textRowAndColumnFrom(const QString &code, qsizetype offset)
Convert a text position from an offset into (line, column).
static std::optional< QString > changedSignalNameToPropertyName(QStringView changeSignal)
static QString signalNameToHandlerName(QAnyStringView signal)
static bool isChangedSignalName(QStringView signalName)
static std::optional< QString > changedHandlerNameToPropertyName(QStringView handler)
static QString propertyNameToChangedHandlerName(QStringView property)
static bool isHandlerName(QStringView signalName)
static std::optional< QString > handlerNameToSignalName(QStringView handler)
static QString propertyNameToChangedSignalName(QStringView property)
static bool isChangedHandlerName(QStringView signalName)
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1121
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6045
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
iterator end()
Returns an \l{STL-style iterators}{STL-style iterator} pointing just after the last character in the ...
Definition qstring.h:1357
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
QString text
QSet< QString >::iterator it
Token token
Definition keywords.cpp:444
Path lookupTypePath(const QString &name)
bool emptyChildrenVisitor(Path, const DomItem &, bool)
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
#define qDebug
[1]
Definition qlogging.h:164
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
const char * typeName
GLint location
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLuint object
[3]
GLsizei range
GLenum type
GLenum target
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLenum GLenum GLsizei void GLsizei void * column
GLuint res
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLuint const GLint * locations
static qreal component(const QPointF &point, unsigned int i)
@ AssumeComponentsAreBound
static std::optional< QString > newNameFrom(const QString &dirtyNewName, QQmlLSUtilsIdentifierType alternative)
static QQmlJSScope::ConstPtr methodOwnerFrom(const QQmlJSScope::ConstPtr &type, const QString &name)
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< QQmlLSUtilsExpressionType > resolveFieldMemberExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions options)
static QList< QQmlLSUtilsItemLocation >::const_iterator handlePropertyDefinitionAndBindingOverlap(const QList< QQmlLSUtilsItemLocation > &items, qsizetype offsetInFile)
static QQmlJSScope::ConstPtr findDefiningScopeForProperty(QQmlJSScope::ConstPtr referrerScope, const QString &nameToCheck)
Finds the scope where a property is first defined.
static QQmlJSScope::ConstPtr propertyOwnerFrom(const QQmlJSScope::ConstPtr &type, const QString &name)
static bool findDefinitionFromItem(const DomItem &item, const QString &name)
static std::optional< QQmlLSUtilsExpressionType > resolveIdentifierExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions options)
static QQmlJSScope::ConstPtr findDefiningScopeForEnumerationKey(QQmlJSScope::ConstPtr referrerScope, const QString &nameToCheck)
static QList< QQmlLSUtilsItemLocation > filterItemsFromTextLocation(const QList< QQmlLSUtilsItemLocation > &items, qsizetype offsetInFile)
static QQmlJSScope::ConstPtr findDefiningScopeForMethod(QQmlJSScope::ConstPtr referrerScope, const QString &nameToCheck)
static std::optional< QQmlLSUtilsExpressionType > methodFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name, QQmlLSUtilsResolveOptions options)
static void findUsagesHelper(const DomItem &item, const QString &name, QList< QQmlLSUtilsLocation > &result)
static QQmlJSScope::ConstPtr findDefiningScopeForBinding(QQmlJSScope::ConstPtr referrerScope, const QString &nameToCheck)
static QQmlJSScope::ConstPtr findScopeOfSpecialItems(QQmlJSScope::ConstPtr scope, const DomItem &item)
static QQmlLSUtilsLocation locationFromJSIdentifierDefinition(const DomItem &definitionOfItem, const QString &name)
static QQmlJSScope::ConstPtr findDefiningScopeForEnumeration(QQmlJSScope::ConstPtr referrerScope, const QString &nameToCheck)
static std::optional< QQmlLSUtilsIdentifierType > hasMethodOrSignal(const QQmlJSScope::ConstPtr &scope, const QString &name)
static std::optional< QQmlLSUtilsExpressionType > propertyFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &propertyName, QQmlLSUtilsResolveOptions options)
static std::optional< QQmlLSUtilsExpressionType > propertyBindingFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name, QQmlLSUtilsResolveOptions options, QQmlJSTypeResolver *resolverForIds)
static std::optional< QQmlLSUtilsLocation > locationFromDomItem(const DomItem &item, FileLocationRegion region)
static void findUsagesOfNonJSIdentifiers(const DomItem &item, const QString &name, QList< QQmlLSUtilsLocation > &result)
static FieldFilter filterForFindUsages()
Filter away the parts of the Dom not needed for find usages, by following the profiler's information.
QQmlJSScope::ConstPtr findDefiningScopeIf(QQmlJSScope::ConstPtr referrerScope, Predicate &&check)
static QStringList namesOfPossibleUsages(const QString &name, const DomItem &item, const QQmlJSScope::ConstPtr &targetType)
static DomItem findJSIdentifierDefinition(const DomItem &item, const QString &name)
static QQmlJSScope::ConstPtr expressionTypeWithDefinition(const QQmlLSUtilsExpressionType &ownerType)
static std::optional< QString > oldNameFrom(const DomItem &item)
static std::optional< QQmlLSUtilsLocation > findMethodDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
static std::optional< QQmlLSUtilsLocation > findPropertyDefinitionOf(const DomItem &file, QQmlJS::SourceLocation propertyDefinitionLocation, const QString &name)
static std::optional< QQmlLSUtilsExpressionType > resolveSignalOrPropertyExpressionType(const QString &name, const QQmlJSScope::ConstPtr &scope, QQmlLSUtilsResolveOptions options)
QQmlLSUtilsIdentifierType
@ PropertyChangedSignalIdentifier
@ JavaScriptIdentifier
@ EnumeratorIdentifier
@ QmlObjectIdIdentifier
@ AttachedTypeIdentifier
@ SignalHandlerIdentifier
@ SingletonIdentifier
@ PropertyChangedHandlerIdentifier
@ SignalIdentifier
@ QmlComponentIdentifier
@ PropertyIdentifier
@ MethodIdentifier
@ GroupedPropertyIdentifier
@ EnumeratorValueIdentifier
QQmlLSUtilsResolveOptions
@ ResolveOwnerType
@ ResolveActualTypeForFieldMemberExpression
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
static const uint base
Definition qurlidna.cpp:20
static Heap::CallContext * getScope(QV4::Value *stack, int level)
const char property[13]
Definition qwizard.cpp:101
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
QStringView el
static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
QQmlLSUtilsLocation location
static QQmlLSUtilsEdit from(const QString &fileName, const QString &code, quint32 startLine, quint32 startCharacter, quint32 length, const QString &newName)
std::optional< QString > name
QQmlJS::Dom::DomItem domItem
QQmlJS::Dom::FileLocations::Tree fileLocation
QQmlJS::SourceLocation sourceLocation
static QQmlLSUtilsLocation from(const QString &fileName, const QString &code, quint32 startLine, quint32 startCharacter, quint32 length)
QQmlLSUtilsIdentifierType type