1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmlsa.h"
5#include "qqmlsa_p.h"
6#include "qqmlsasourcelocation.h"
7
8#include "qqmljsscope_p.h"
9#include "qqmljslogger_p.h"
10#include "qqmljstyperesolver_p.h"
11#include "qqmljsimportvisitor_p.h"
12#include "qqmljsutils_p.h"
13#include "qdeferredpointer_p.h"
14
15#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
16
17#include <memory>
18#include <new>
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24namespace QQmlSA {
25
26static_assert(QQmlJSScope::sizeofQQmlSAElement() == sizeof(Element));
27
28/*!
29 \namespace QQmlSA
30 \inmodule QtQmlCompiler
31
32 \brief Provides tools for static analysis on QML programs.
33 */
34
35/*!
36 \class QQmlSA::Binding::Bindings
37 \inmodule QtQmlCompiler
38
39 \brief Holds multiple property name to property binding associations.
40 */
41
42Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { }
43
44BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { }
45
46Binding::Bindings::Bindings(const Bindings &other)
47 : d_ptr{ new BindingsPrivate{ this, *other.d_func() } }
48{
49}
50
51Binding::Bindings::~Bindings() = default;
52
53BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, const BindingsPrivate &other)
54 : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ interface }
55{
56}
57
58BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, BindingsPrivate &&other)
59 : m_bindings{ std::move(other.m_bindings) }, q_ptr{ interface }
60{
61}
62
63/*!
64 Returns an iterator to the beginning of the bindings.
65 */
66QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const
67{
68 Q_D(const Bindings);
69 return d->constBegin();
70}
71
72QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constBegin() const
73{
74 return m_bindings.constBegin();
75}
76
77/*!
78 Returns an iterator to the end of the bindings.
79 */
80QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const
81{
82 Q_D(const Bindings);
83 return d->constEnd();
84}
85
86QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constEnd() const
87{
88 return m_bindings.constEnd();
89}
90
91/*!
92 \class QQmlSA::Binding
93 \inmodule QtQmlCompiler
94
95 \brief Represents a single QML property binding for a specific type.
96 */
97
98Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { }
99
100BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { }
101
102Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
103
104Binding::Binding(Binding &&other) noexcept
105 : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
106
107Binding &Binding::operator=(const Binding &other)
108{
109 if (*this == other)
110 return *this;
111
112 d_func()->m_binding = other.d_func()->m_binding;
113 d_func()->q_ptr = this;
114 return *this;
115}
116
117Binding &Binding::operator=(Binding &&other) noexcept
118{
119 if (*this == other)
120 return *this;
121
122 d_func()->m_binding = std::move(other.d_func()->m_binding);
123 d_func()->q_ptr = this;
124 return *this;
125}
126
127Binding::~Binding() = default;
128
129bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs)
130{
131 return lhs.d_func()->m_binding == rhs.d_func()->m_binding;
132}
133
134BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other)
135 : m_binding{ other.m_binding }, q_ptr{ interface }
136{
137}
138
139QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding)
140{
141 QQmlSA::Binding saBinding;
142 saBinding.d_func()->m_binding = binding;
143 return saBinding;
144}
145
146QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding)
147{
148 return binding.d_func()->m_binding;
149}
150
151const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding)
152{
153 return binding.d_func()->m_binding;
154}
155
156/*!
157 Returns the type of the property if this element is a group property,
158 otherwise returns an invalid Element.
159 */
160Element Binding::groupType() const
161{
162 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).groupType());
163}
164
165QQmlSA::BindingType Binding::bindingType() const
166{
167 return BindingPrivate::binding(binding: *this).bindingType();
168}
169
170/*!
171 Returns the associated string literal if the content type of this binding is
172 StringLiteral, otherwise returns an empty string.
173 */
174QString Binding::stringValue() const
175{
176 return BindingPrivate::binding(binding: *this).stringValue();
177}
178
179/*!
180 Returns the name of the property using this binding.
181 */
182QString Binding::propertyName() const
183{
184 return BindingPrivate::binding(binding: *this).propertyName();
185}
186
187/*!
188 Returns the attached type if the content type of this binding is
189 AttachedProperty, otherwise returns an invalid Element.
190 */
191Element Binding::attachingType() const
192{
193 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).attachingType());
194}
195
196/*!
197 Returns the location in the QML code where this binding is defined.
198 */
199QQmlSA::SourceLocation Binding::sourceLocation() const
200{
201 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
202 jsLocation: BindingPrivate::binding(binding: *this).sourceLocation());
203}
204
205/*!
206 Returns the associated number if the content type of this binding is
207 NumberLiteral, otherwise returns 0.
208 */
209double Binding::numberValue() const
210{
211 return BindingPrivate::binding(binding: *this).numberValue();
212}
213
214/*!
215 Returns the kind of associated associated script if the content type of
216 this binding is Script, otherwise returns Script_Invalid.
217 */
218QQmlSA::ScriptBindingKind Binding::scriptKind() const
219{
220 return BindingPrivate::binding(binding: *this).scriptKind();
221}
222
223/*!
224 Returns \c true if this binding has an objects, otherwise returns \c false.
225 */
226bool Binding::hasObject() const
227{
228 return BindingPrivate::binding(binding: *this).hasObject();
229}
230
231/*!
232 Returns the type of the associated object if the content type of this
233 binding is Object, otherwise returns an invlaid Element.
234 */
235QQmlSA::Element Binding::objectType() const
236{
237 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).objectType());
238}
239
240Element QQmlSA::Binding::literalType(const QQmlJSTypeResolver *resolver) const
241{
242 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).literalType(resolver));
243}
244
245bool Binding::hasUndefinedScriptValue() const
246{
247 const auto &jsBinding = BindingPrivate::binding(binding: *this);
248 return jsBinding.bindingType() == BindingType::Script
249 && jsBinding.scriptValueType() == ScriptValue_Undefined;
250}
251
252/*!
253 Returns \c true if \a bindingType is a literal type, and \c false
254 otherwise. Literal types include strings, booleans, numbers, regular
255 expressions among other.
256 */
257bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType)
258{
259 return QQmlJSMetaPropertyBinding::isLiteralBinding(type: bindingType);
260}
261
262QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { }
263
264QQmlSA::Method::Methods::Methods(const Methods &other)
265 : d_ptr{ new MethodsPrivate{ this, *other.d_func() } }
266{
267}
268
269QQmlSA::Method::Methods::~Methods() = default;
270
271/*!
272 Returns an iterator to the beginning of the methods.
273 */
274QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const
275{
276 Q_D(const Methods);
277 return d->constBegin();
278}
279
280QMultiHash<QString, Method>::const_iterator MethodsPrivate::constBegin() const
281{
282 return m_methods.constBegin();
283}
284
285/*!
286 Returns an iterator to the end of the methods.
287 */
288QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const
289{
290 Q_D(const Methods);
291 return d->constEnd();
292}
293QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const
294{
295 return m_methods.constEnd();
296}
297
298MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { }
299
300MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other)
301 : m_methods{ other.m_methods }, q_ptr{ interface }
302{
303}
304
305MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other)
306 : m_methods{ std::move(other.m_methods) }, q_ptr{ interface }
307{
308}
309
310MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { }
311
312MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other)
313 : m_method{ other.m_method }, q_ptr{ interface }
314{
315}
316
317QString MethodPrivate::methodName() const
318{
319 return m_method.methodName();
320}
321
322MethodType MethodPrivate::methodType() const
323{
324 return m_method.methodType();
325}
326
327/*!
328 \class QQmlSA::Method
329 \inmodule QtQmlCompiler
330
331 \brief Represents a QML method.
332 */
333
334Method::Method() : d_ptr{ new MethodPrivate{ this } } { }
335
336Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { }
337
338Method::Method(Method &&other) noexcept
339 : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } }
340{
341}
342
343Method &Method::operator=(const Method &other)
344{
345 if (*this == other)
346 return *this;
347
348 d_func()->m_method = other.d_func()->m_method;
349 d_func()->q_ptr = this;
350 return *this;
351}
352
353Method &Method::operator=(Method &&other) noexcept
354{
355 if (*this == other)
356 return *this;
357
358 d_func()->m_method = std::move(other.d_func()->m_method);
359 d_func()->q_ptr = this;
360 return *this;
361}
362
363Method::~Method() = default;
364
365/*!
366 Returns the name of the this method.
367 */
368QString Method::methodName() const
369{
370 Q_D(const Method);
371 return d->methodName();
372}
373
374/*!
375 Returns the type of this method. For example, Signal, Slot, Method or
376 StaticMethod.
377 */
378MethodType Method::methodType() const
379{
380 Q_D(const Method);
381 return d->methodType();
382}
383
384bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs)
385{
386 return lhs.d_func()->m_method == rhs.d_func()->m_method;
387}
388
389QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod)
390{
391 QQmlSA::Method saMethod;
392 auto &wrappedMethod = saMethod.d_func()->m_method;
393 wrappedMethod = jsMethod;
394 return saMethod;
395}
396
397QQmlSA::Method::Methods
398MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash)
399{
400 QMultiHash<QString, QQmlSA::Method> saMethods;
401 for (const auto &[key, value] : hash.asKeyValueRange()) {
402 saMethods.insert(key, value: MethodPrivate::createMethod(jsMethod: value));
403 }
404
405 QQmlSA::Method::Methods methods;
406 methods.d_func()->m_methods = std::move(saMethods);
407 return methods;
408}
409
410QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method)
411{
412 return method.d_func()->m_method;
413}
414
415PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { }
416
417PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other)
418 : m_property{ other.m_property }, q_ptr{ interface }
419{
420}
421
422PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other)
423 : m_property{ std::move(other.m_property) }, q_ptr{ interface }
424{
425}
426
427QString PropertyPrivate::typeName() const
428{
429 return m_property.typeName();
430}
431
432bool PropertyPrivate::isValid() const
433{
434 return m_property.isValid();
435}
436
437QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property)
438{
439 return property.d_func()->m_property;
440}
441
442QQmlSA::Property PropertyPrivate::createProperty(const QQmlJSMetaProperty &property)
443{
444 QQmlSA::Property saProperty;
445 auto &wrappedProperty = saProperty.d_func()->m_property;
446 wrappedProperty = property;
447 return saProperty;
448}
449
450/*!
451 \class QQmlSA::Property
452 \inmodule QtQmlCompiler
453
454 \brief Represents a QML property.
455 */
456
457Property::Property() : d_ptr{ new PropertyPrivate{ this } } { }
458
459Property::Property(const Property &other)
460 : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { }
461
462Property::Property(Property &&other) noexcept
463 : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } }
464{
465}
466
467Property &Property::operator=(const Property &other)
468{
469 if (*this == other)
470 return *this;
471
472 d_func()->m_property = other.d_func()->m_property;
473 d_func()->q_ptr = this;
474 return *this;
475}
476
477Property &Property::operator=(Property &&other) noexcept
478{
479 if (*this == other)
480 return *this;
481
482 d_func()->m_property = std::move(other.d_func()->m_property);
483 d_func()->q_ptr = this;
484 return *this;
485}
486
487Property::~Property() = default;
488
489/*!
490 Returns the name of the type of this property.
491 */
492QString Property::typeName() const
493{
494 Q_D(const Property);
495 return d->typeName();
496}
497
498bool Property::isValid() const
499{
500 Q_D(const Property);
501 return d->isValid();
502}
503
504bool Property::operatorEqualsImpl(const Property &lhs, const Property &rhs)
505{
506 return lhs.d_func()->m_property == rhs.d_func()->m_property;
507}
508
509/*!
510 \class QQmlSA::Element
511 \inmodule QtQmlCompiler
512
513 \brief Represents a QML type.
514 */
515
516Element::Element()
517{
518 new (m_data) QQmlJSScope::ConstPtr();
519}
520
521Element::Element(const Element &other)
522{
523 new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other));
524}
525
526Element &Element::operator=(const Element &other)
527{
528 if (this == &other)
529 return *this;
530
531 *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other);
532 return *this;
533}
534
535Element::~Element()
536{
537 (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr();
538}
539
540/*!
541 Returns the type of Element's scope.
542 */
543QQmlJSScope::ScopeType Element::scopeType() const
544{
545 return QQmlJSScope::scope(*this)->scopeType();
546}
547
548/*!
549 Returns the Element this Element derives from.
550 */
551Element Element::baseType() const
552{
553 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->baseType());
554}
555
556/*!
557 Returns the name of the Element this Element derives from.
558 */
559QString Element::baseTypeName() const
560{
561 return QQmlJSScope::prettyName(name: QQmlJSScope::scope(*this)->baseTypeName());
562}
563
564/*!
565 Returns the Element that encloses this Element.
566 */
567Element Element::parentScope() const
568{
569 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->parentScope());
570}
571
572/*!
573 Returns whether this Element inherits from \a element.
574 */
575bool Element::inherits(const Element &element) const
576{
577 return QQmlJSScope::scope(*this)->inherits(base: QQmlJSScope::scope(element));
578}
579
580bool Element::isNull() const
581{
582 return QQmlJSScope::scope(*this).isNull();
583}
584
585/*!
586 \internal
587 */
588QString Element::internalId() const
589{
590 return QQmlJSScope::scope(*this)->internalName();
591}
592
593/*!
594 Returns the access semantics of this Element. For example, Reference,
595 Value or Sequence.
596 */
597AccessSemantics Element::accessSemantics() const
598{
599 return QQmlJSScope::scope(*this)->accessSemantics();
600}
601
602/*!
603 Returns true for objects defined from Qml, and false for objects declared from C++.
604 */
605bool QQmlSA::Element::isComposite() const
606{
607 return QQmlJSScope::scope(*this)->isComposite();
608}
609
610/*!
611 Returns whether this Element has a property with the name \a propertyName.
612 */
613bool Element::hasProperty(const QString &propertyName) const
614{
615 return QQmlJSScope::scope(*this)->hasProperty(name: propertyName);
616}
617
618/*!
619 Returns the property with the name \a propertyName if it is found in this
620 Element or its base and extension objects, otherwise returns an invalid property.
621 */
622QQmlSA::Property Element::property(const QString &propertyName) const
623{
624 return PropertyPrivate::createProperty(property: QQmlJSScope::scope(*this)->property(name: propertyName));
625}
626
627/*!
628 Returns whether the property with the name \a propertyName resolved on this
629 Element is required. Returns false if the the property couldn't be found.
630 */
631bool Element::isPropertyRequired(const QString &propertyName) const
632{
633 return QQmlJSScope::scope(*this)->isPropertyRequired(name: propertyName);
634}
635
636/*!
637 Returns the name of the default property of this Element. If it doesn't
638 have one, returns an empty string.
639 */
640QString Element::defaultPropertyName() const
641{
642 return QQmlJSScope::scope(*this)->defaultPropertyName();
643}
644
645/*!
646 Returns whether this Element has a method with the name \a methodName.
647 */
648bool Element::hasMethod(const QString &methodName) const
649{
650 return QQmlJSScope::scope(*this)->hasMethod(name: methodName);
651}
652
653/*!
654 \class QQmlSA::Method::Methods
655 \inmodule QtQmlCompiler
656
657 \brief Holds multiple method name to method associations.
658 */
659
660/*!
661 Returns this Elements's method which are not defined on its base or
662 extension objects.
663 */
664Method::Methods Element::ownMethods() const
665{
666 return MethodsPrivate::createMethods(hash: QQmlJSScope::scope(*this)->ownMethods());
667}
668
669/*!
670 Returns the location in the QML code where this method is defined.
671 */
672QQmlSA::SourceLocation Element::sourceLocation() const
673{
674 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
675 jsLocation: QQmlJSScope::scope(*this)->sourceLocation());
676}
677
678/*!
679 Returns the file path of the QML code that defines this method.
680 */
681QString Element::filePath() const
682{
683 return QQmlJSScope::scope(*this)->filePath();
684}
685
686/*!
687 Returns whethe this Element has a property binding with the name \a name.
688 */
689bool Element::hasPropertyBindings(const QString &name) const
690{
691 return QQmlJSScope::scope(*this)->hasPropertyBindings(name);
692}
693
694/*!
695 Returns whether this Element has property bindings which are not defined in
696 its base or extension objects and that have name \a propertyName.
697 */
698bool Element::hasOwnPropertyBindings(const QString &propertyName) const
699{
700 return QQmlJSScope::scope(*this)->hasOwnPropertyBindings(name: propertyName);
701}
702
703/*!
704 Returns this Element's property bindings which are not defined on its base
705 or extension objects.
706 */
707Binding::Bindings Element::ownPropertyBindings() const
708{
709 return BindingsPrivate::createBindings(QQmlJSScope::scope(*this)->ownPropertyBindings());
710}
711
712/*!
713 Returns this Element's property bindings which are not defined on its base
714 or extension objects and that have the name \a propertyName.
715 */
716Binding::Bindings Element::ownPropertyBindings(const QString &propertyName) const
717{
718 return BindingsPrivate::createBindings(
719 QQmlJSScope::scope(*this)->ownPropertyBindings(name: propertyName));
720}
721
722/*!
723 Returns this Element's property bindings that have the name \a propertyName.
724 */
725QList<Binding> Element::propertyBindings(const QString &propertyName) const
726{
727 const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(name: propertyName);
728
729 QList<Binding> saBindings;
730 for (const auto &jsBinding : bindings) {
731 saBindings.push_back(t: BindingPrivate::createBinding(binding: jsBinding));
732 }
733 return saBindings;
734}
735
736QQmlSA::Binding::Bindings
737BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash)
738{
739 QMultiHash<QString, QQmlSA::Binding> saBindings;
740 for (const auto &[key, value] : hash.asKeyValueRange()) {
741 saBindings.insert(key, value: BindingPrivate::createBinding(binding: value));
742 }
743
744 QQmlSA::Binding::Bindings bindings;
745 bindings.d_func()->m_bindings = std::move(saBindings);
746 return bindings;
747}
748
749QQmlSA::Binding::Bindings BindingsPrivate::createBindings(
750 QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
751 QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> iterators)
752{
753 QMultiHash<QString, QQmlSA::Binding> saBindings;
754 for (auto it = iterators.first; it != iterators.second; ++it) {
755 saBindings.insert(key: it.key(), value: BindingPrivate::createBinding(binding: it.value()));
756 }
757
758 QQmlSA::Binding::Bindings bindings;
759 bindings.d_func()->m_bindings = std::move(saBindings);
760 return bindings;
761}
762
763/*!
764 Returns an iterator to the beginning of this Element's children.
765 */
766QQmlJS::ConstPtrWrapperIterator Element::childScopesBegin() const
767{
768 return QQmlJSScope::scope(*this)->childScopesBegin();
769}
770
771/*!
772 Returns an iterator to the end of this Element's children.
773 */
774QQmlJS::ConstPtrWrapperIterator Element::childScopesEnd() const
775{
776 return QQmlJSScope::scope(*this)->childScopesEnd();
777}
778
779Element::operator bool() const
780{
781 return bool(QQmlJSScope::scope(*this));
782}
783
784bool Element::operator!() const
785{
786 return !QQmlJSScope::scope(*this);
787}
788
789/*!
790 Returns the name of this Element.
791 */
792QString Element::name() const
793{
794 if (isNull())
795 return {};
796 return QQmlJSScope::prettyName(name: QQmlJSScope::scope(*this)->internalName());
797}
798
799bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs)
800{
801 return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs);
802}
803
804qsizetype Element::qHashImpl(const Element &key, qsizetype seed) noexcept
805{
806 return qHash(ptr: QQmlJSScope::scope(key), seed);
807}
808
809/*!
810 \class QQmlSA::GenericPass
811 \inmodule QtQmlCompiler
812
813 \brief The base class for static analysis passes.
814
815 This class contains common functionality used by more specific passses.
816 Custom passes should not directly derive from it, but rather from one of
817 its subclasses.
818 \sa ElementPass, PropertyPass
819 */
820
821class GenericPassPrivate {
822 Q_DECLARE_PUBLIC(GenericPass);
823
824public:
825 GenericPassPrivate(GenericPass *interface, PassManager *manager)
826 : m_manager{ manager }, q_ptr{ interface }
827 {
828 Q_ASSERT(manager);
829 }
830
831private:
832 PassManager *m_manager;
833
834 GenericPass *q_ptr;
835};
836
837GenericPass::~GenericPass() = default;
838
839/*!
840 Creates a generic pass.
841 */
842GenericPass::GenericPass(PassManager *manager)
843 : d_ptr{ new GenericPassPrivate{ this, manager } } { }
844
845/*!
846 Emits a warning message \a diagnostic about an issue of type \a id.
847 */
848void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id)
849{
850 emitWarning(diagnostic, id, srcLocation: QQmlSA::SourceLocation{});
851}
852
853/*!
854 Emits warning message \a diagnostic about an issue of type \a id located at
855 \a srcLocation.
856 */
857void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
858 QQmlSA::SourceLocation srcLocation)
859{
860 Q_D(const GenericPass);
861 PassManagerPrivate::visitor(*d->m_manager)
862 ->logger()
863 ->log(message: diagnostic.toString(), id,
864 srcLocation: QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: srcLocation));
865}
866
867/*!
868 Emits a warning message \a diagnostic about an issue of type \a id located at
869 \a srcLocation and with suggested fix \a fix.
870 */
871void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
872 QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix)
873{
874 Q_D(const GenericPass);
875 PassManagerPrivate::visitor(*d->m_manager)
876 ->logger()
877 ->log(message: diagnostic.toString(), id,
878 srcLocation: QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: srcLocation), showContext: true, showFileName: true,
879 suggestion: FixSuggestionPrivate::fixSuggestion(fix));
880}
881
882/*!
883 Returns the type corresponding to \a typeName inside the
884 currently analysed file.
885 */
886Element GenericPass::resolveTypeInFileScope(QAnyStringView typeName)
887{
888 Q_D(const GenericPass);
889 const auto scope =
890 PassManagerPrivate::visitor(*d->m_manager)->imports().type(name: typeName.toString()).scope;
891 return QQmlJSScope::createQQmlSAElement(scope);
892}
893
894/*!
895 Returns the attached type corresponding to \a typeName used inside
896 the currently analysed file.
897 */
898Element GenericPass::resolveAttachedInFileScope(QAnyStringView typeName)
899{
900 const auto type = resolveTypeInFileScope(typeName);
901 const auto scope = QQmlJSScope::scope(type);
902
903 if (scope.isNull())
904 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::ConstPtr(nullptr));
905
906 return QQmlJSScope::createQQmlSAElement(scope->attachedType());
907}
908
909/*!
910 Returns the type of \a typeName defined in module \a moduleName.
911 If an attached type and and a non-attached type share the same
912 name (e.g. \c ListView), the \l Element corresponding to the
913 non-attached type is returned.
914 To obtain the attached type, use \l resolveAttached.
915 */
916Element GenericPass::resolveType(QAnyStringView moduleName, QAnyStringView typeName)
917{
918 Q_D(const GenericPass);
919 QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
920 const auto module = typeImporter->importModule(module: moduleName.toString());
921 const auto scope = module.type(name: typeName.toString()).scope;
922 return QQmlJSScope::createQQmlSAElement(scope);
923}
924
925/*!
926 Returns the type of the built-in type identified by \a typeName.
927 Built-in types encompasses \c{C++} types which the QML engine can handle
928 without any imports (e.g. \l QDateTime and \l QString), global EcmaScript
929 objects like \c Number, as well as the \l{global Qt object}
930 {QML Global Object}.
931 */
932Element GenericPass::resolveBuiltinType(QAnyStringView typeName) const
933{
934 Q_D(const GenericPass);
935 QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
936 auto typeNameString = typeName.toString();
937 // we have to check both cpp names
938 auto scope = typeImporter->builtinInternalNames().type(name: typeNameString).scope;
939 if (!scope) {
940 // and qml names (e.g. for bool) - builtinImportHelper is private, so we can't do it in one call
941 auto builtins = typeImporter->importBuiltins();
942 scope = builtins.type(name: typeNameString).scope;
943 }
944 return QQmlJSScope::createQQmlSAElement(scope);
945}
946
947/*!
948 Returns the attached type of \a typeName defined in module \a moduleName.
949 */
950Element GenericPass::resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
951{
952 const auto &resolvedType = resolveType(moduleName, typeName);
953 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(resolvedType)->attachedType());
954}
955
956/*!
957 Returns the element representing the type of literal in \a binding. If the
958 binding does not contain a literal value, a null Element is returned.
959 */
960Element GenericPass::resolveLiteralType(const QQmlSA::Binding &binding)
961{
962 Q_D(const GenericPass);
963 return binding.literalType(resolver: PassManagerPrivate::resolver(*d->m_manager));
964}
965
966/*!
967 Returns the element in \a context that has id \a id.
968 */
969Element GenericPass::resolveIdToElement(QAnyStringView id, const Element &context)
970{
971 Q_D(const GenericPass);
972 const auto scope = PassManagerPrivate::visitor(*d->m_manager)
973 ->addressableScopes()
974 .scope(id: id.toString(), referrer: QQmlJSScope::scope(context));
975 return QQmlJSScope::createQQmlSAElement(scope);
976}
977
978/*!
979 Returns the id of \a element in a given \a context.
980 */
981QString GenericPass::resolveElementToId(const Element &element, const Element &context)
982{
983 Q_D(const GenericPass);
984 return PassManagerPrivate::visitor(*d->m_manager)
985 ->addressableScopes()
986 .id(scope: QQmlJSScope::scope(element), referrer: QQmlJSScope::scope(context));
987}
988
989/*!
990 Returns the source code located within \a location.
991 */
992QString GenericPass::sourceCode(QQmlSA::SourceLocation location)
993{
994 Q_D(const GenericPass);
995 return PassManagerPrivate::visitor(*d->m_manager)
996 ->logger()
997 ->code()
998 .mid(position: location.offset(), n: location.length());
999}
1000
1001/*!
1002 \class QQmlSA::PassManager
1003 \inmodule QtQmlCompiler
1004
1005 \brief Can analyze an element and its children with static analysis passes.
1006 */
1007
1008/*!
1009 Constructs a pass manager given an import \a visitor and a type \a resolver.
1010 */
1011QQmlSA::PassManager::PassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver)
1012 : d_ptr{ new PassManagerPrivate{ this, visitor, resolver } }
1013{
1014}
1015
1016PassManager::~PassManager() = default; // explicitly defaulted out-of-line for PIMPL
1017
1018/*!
1019 Registers a static analysis \a pass to be run on all elements.
1020 */
1021void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass)
1022{
1023 Q_D(PassManager);
1024 d->registerElementPass(pass: std::move(pass));
1025}
1026
1027/*!
1028 \internal
1029 \brief PassManager::registerElementPass registers ElementPass
1030 with the pass manager.
1031 \param pass The registered pass. Ownership is transferred to the pass manager.
1032 */
1033void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass)
1034{
1035 m_elementPasses.push_back(x: std::move(pass));
1036}
1037
1038enum LookupMode { Register, Lookup };
1039static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup)
1040{
1041 QString name;
1042 if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) {
1043 // Bail out with an invalid name, this type is so screwed up we can't do anything reasonable
1044 // with it We should have warned about it in another plac
1045 if (element.isNull() || element.baseType().isNull())
1046 return u"$INVALID$"_s;
1047 name = QQmlJSScope::scope(element.baseType())->internalName();
1048 } else {
1049 name = QQmlJSScope::scope(element)->internalName();
1050 }
1051
1052 const QString filePath =
1053 (mode == Register || !element.baseType() ? element : element.baseType()).filePath();
1054
1055 if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(s: u".h"))
1056 name += u'@' + filePath;
1057 return name;
1058}
1059
1060/*!
1061 Registers a static analysis pass for properties. The \a pass will be run on
1062 every property matching the \a moduleName, \a typeName and \a propertyName.
1063
1064 Omitting the \a propertyName will register this pass for all properties
1065 matching the \a typeName and \a moduleName.
1066
1067 Setting \a allowInheritance to \c true means that the filtering on the type
1068 also accepts types deriving from \a typeName.
1069
1070 \a pass is passed as a \c{std::shared_ptr} to allow reusing the same pass
1071 on multiple elements:
1072 \code
1073 auto titleValiadorPass = std::make_shared<TitleValidatorPass>(manager);
1074 manager->registerPropertyPass(titleValidatorPass,
1075 "QtQuick", "Window", "title");
1076 manager->registerPropertyPass(titleValidatorPass,
1077 "QtQuick.Controls", "Dialog", "title");
1078 \endcode
1079
1080 \note Running analysis passes on too many items can be expensive. This is
1081 why it is generally good to filter down the set of properties of a pass
1082 using the \a moduleName, \a typeName and \a propertyName.
1083
1084 Returns \c true if the pass was successfully added, \c false otherwise.
1085 Adding a pass fails when the \l{QQmlSA::Element}{Element} specified by
1086 \a moduleName and \a typeName does not exist.
1087
1088 \sa PropertyPass
1089*/
1090bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
1091 QAnyStringView moduleName, QAnyStringView typeName,
1092 QAnyStringView propertyName, bool allowInheritance)
1093{
1094 Q_D(PassManager);
1095 return d->registerPropertyPass(pass, moduleName, typeName, propertyName, allowInheritance);
1096}
1097
1098bool PassManagerPrivate::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
1099 QAnyStringView moduleName, QAnyStringView typeName,
1100 QAnyStringView propertyName, bool allowInheritance)
1101{
1102 if (moduleName.isEmpty() != typeName.isEmpty()) {
1103 qWarning() << "Both the moduleName and the typeName must be specified "
1104 "for the pass to be registered for a specific element.";
1105 }
1106
1107 QString name;
1108 if (!moduleName.isEmpty() && !typeName.isEmpty()) {
1109 auto typeImporter = m_visitor->importer();
1110 auto module = typeImporter->importModule(module: moduleName.toString());
1111 auto element = QQmlJSScope::createQQmlSAElement(module.type(name: typeName.toString()).scope);
1112
1113 if (element.isNull())
1114 return false;
1115
1116 name = lookupName(element, mode: Register);
1117 }
1118 const QQmlSA::PropertyPassInfo passInfo{ .properties: propertyName.isEmpty()
1119 ? QStringList{}
1120 : QStringList{ propertyName.toString() },
1121 .pass: std::move(pass), .allowInheritance: allowInheritance };
1122 m_propertyPasses.insert(x: { name, passInfo });
1123
1124 return true;
1125}
1126
1127void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope,
1128 const QString prefix, bool isAttached)
1129{
1130 const Element &currentScope = scope.isNull() ? element : scope;
1131 const auto ownBindings = currentScope.ownPropertyBindings();
1132 for (const auto &binding : ownBindings) {
1133 switch (binding.bindingType()) {
1134 case QQmlSA::BindingType::GroupProperty:
1135 addBindingSourceLocations(element, scope: Element{ binding.groupType() },
1136 prefix: prefix + binding.propertyName() + u'.');
1137 break;
1138 case QQmlSA::BindingType::AttachedProperty:
1139 addBindingSourceLocations(element, scope: Element{ binding.attachingType() },
1140 prefix: prefix + binding.propertyName() + u'.', isAttached: true);
1141 break;
1142 default:
1143 m_bindingsByLocation.insert(x: { binding.sourceLocation().offset(),
1144 BindingInfo{ .fullPropertyName: prefix + binding.propertyName(), .binding: binding,
1145 .bindingScope: currentScope, .isAttached: isAttached } });
1146
1147 if (binding.bindingType() != QQmlSA::BindingType::Script)
1148 analyzeBinding(element, value: QQmlSA::Element(), location: binding.sourceLocation());
1149 }
1150 }
1151}
1152
1153/*!
1154 Runs the element passes over \a root and all its children.
1155 */
1156void PassManager::analyze(const Element &root)
1157{
1158 Q_D(PassManager);
1159 d->analyze(root);
1160}
1161
1162void PassManagerPrivate::analyze(const Element &root)
1163{
1164 QList<Element> runStack;
1165 runStack.push_back(t: root);
1166 while (!runStack.isEmpty()) {
1167 auto element = runStack.takeLast();
1168 addBindingSourceLocations(element);
1169 for (auto &elementPass : m_elementPasses)
1170 if (elementPass->shouldRun(element))
1171 elementPass->run(element);
1172
1173 for (auto it = element.childScopesBegin(); it != element.childScopesEnd(); ++it) {
1174 if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope)
1175 runStack.push_back(t: QQmlJSScope::createQQmlSAElement(*it));
1176 }
1177 }
1178}
1179
1180void PassManagerPrivate::analyzeWrite(const Element &element, QString propertyName,
1181 const Element &value, const Element &writeScope,
1182 QQmlSA::SourceLocation location)
1183{
1184 for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
1185 pass->onWrite(element, propertyName, value, writeScope, location);
1186}
1187
1188void PassManagerPrivate::analyzeRead(const Element &element, QString propertyName,
1189 const Element &readScope, QQmlSA::SourceLocation location)
1190{
1191 for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
1192 pass->onRead(element, propertyName, readScope, location);
1193}
1194
1195void PassManagerPrivate::analyzeBinding(const Element &element, const QQmlSA::Element &value,
1196 QQmlSA::SourceLocation location)
1197{
1198 const auto info = m_bindingsByLocation.find(x: location.offset());
1199
1200 // If there's no matching binding that means we're in a nested Ret somewhere inside an
1201 // expression
1202 if (info == m_bindingsByLocation.end())
1203 return;
1204
1205 const QQmlSA::Element &bindingScope = info->second.bindingScope;
1206 const QQmlSA::Binding &binding = info->second.binding;
1207 const QString &propertyName = info->second.fullPropertyName;
1208
1209 for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
1210 pass->onBinding(element, propertyName, binding, bindingScope, value);
1211
1212 if (!info->second.isAttached || bindingScope.baseType().isNull())
1213 return;
1214
1215 for (PropertyPass *pass : findPropertyUsePasses(element: bindingScope.baseType(), propertyName))
1216 pass->onBinding(element, propertyName, binding, bindingScope, value);
1217}
1218
1219/*!
1220 Returns \c true if the module named \a module has been imported by the
1221 QML to be analyzed, \c false otherwise.
1222
1223 This can be used to skip registering a pass which is specific to a specific
1224 module.
1225
1226 \code
1227 if (passManager->hasImportedModule("QtPositioning"))
1228 passManager->registerElementPass(
1229 std::make_unique<PositioningPass>(passManager)
1230 );
1231 \endcode
1232
1233 \sa registerPropertyPass(), registerElementPass()
1234 */
1235bool PassManager::hasImportedModule(QAnyStringView module) const
1236{
1237 return PassManagerPrivate::visitor(*this)->imports().hasType(name: u"$module$." + module.toString());
1238}
1239
1240/*!
1241 Returns \c true if warnings of \a category are enabled, \c false otherwise.
1242 */
1243bool PassManager::isCategoryEnabled(LoggerWarningId category) const
1244{
1245 return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(id: category);
1246}
1247
1248QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager)
1249{
1250 return manager.d_func()->m_visitor;
1251}
1252
1253QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager)
1254{
1255 return manager.d_func()->m_typeResolver;
1256}
1257
1258QSet<PropertyPass *> PassManagerPrivate::findPropertyUsePasses(const QQmlSA::Element &element,
1259 const QString &propertyName)
1260{
1261 QStringList typeNames { lookupName(element) };
1262
1263 QQmlJSUtils::searchBaseAndExtensionTypes(
1264 type: QQmlJSScope::scope(element),
1265 check: [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1266 Q_UNUSED(mode);
1267 typeNames.append(t: lookupName(element: QQmlJSScope::createQQmlSAElement(scope)));
1268 return false;
1269 });
1270
1271 QSet<PropertyPass *> passes;
1272
1273 for (const QString &typeName : typeNames) {
1274 for (auto &pass :
1275 { m_propertyPasses.equal_range(x: u""_s), m_propertyPasses.equal_range(x: typeName) }) {
1276 if (pass.first == pass.second)
1277 continue;
1278
1279 for (auto it = pass.first; it != pass.second; it++) {
1280 if (typeName != typeNames.constFirst() && !it->second.allowInheritance)
1281 continue;
1282 if (it->second.properties.isEmpty()
1283 || it->second.properties.contains(str: propertyName)) {
1284 passes.insert(value: it->second.pass.get());
1285 }
1286 }
1287 }
1288 }
1289 return passes;
1290}
1291
1292void DebugElementPass::run(const Element &element) {
1293 emitWarning(diagnostic: u"Type: " + element.baseTypeName(), id: qmlPlugin);
1294 if (auto bindings = element.propertyBindings(propertyName: u"objectName"_s); !bindings.isEmpty()) {
1295 emitWarning(diagnostic: u"is named: " + bindings.first().stringValue(), id: qmlPlugin);
1296 }
1297 if (auto defPropName = element.defaultPropertyName(); !defPropName.isEmpty()) {
1298 emitWarning(diagnostic: u"binding " + QString::number(element.propertyBindings(propertyName: defPropName).size())
1299 + u" elements to property "_s + defPropName,
1300 id: qmlPlugin);
1301 }
1302}
1303
1304/*!
1305 \class QQmlSA::LintPlugin
1306 \inmodule QtQmlCompiler
1307
1308 \brief Base class for all static analysis plugins.
1309 */
1310
1311/*!
1312 \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement)
1313
1314 Adds a pass \a manager that will be executed on \a rootElement.
1315 */
1316
1317/*!
1318 \class QQmlSA::ElementPass
1319 \inmodule QtQmlCompiler
1320
1321 \brief Base class for all static analysis passes on elements.
1322
1323 ElementPass is the simpler of the two analysis passes. It will consider every element in
1324 a file. The \l shouldRun() method can be used to filter out irrelevant elements, and the
1325 \l run() method is doing the initial work.
1326
1327 Common tasks suitable for an ElementPass are
1328 \list
1329 \li checking that properties of an Element are not combined in a nonsensical way
1330 \li validating property values (e.g. that a property takes only certain enum values)
1331 \li checking behavior dependent on an Element's parent (e.g. not using \l {Item::width}
1332 when the parent element is a \c Layout).
1333 \endlist
1334
1335 As shown in the snippet below, it is recommended to do necessary type resolution in the
1336 constructor of the ElementPass and cache it in local members, and to implement some
1337 filtering via \l shouldRun() to keep the static analysis performant.
1338
1339 \code
1340 using namespace QQmlSA;
1341 class MyElementPass : public ElementPass
1342 {
1343 Element myType;
1344 public:
1345 MyElementPass(QQmlSA::PassManager *manager)
1346 : myType(resolveType("MyModule", "MyType")) {}
1347
1348 bool shouldRun(const Element &element) override
1349 {
1350 return element.inherits(myType);
1351 }
1352 void run(const Element &element) override
1353 {
1354 // actual pass logic
1355 }
1356 }
1357 \endcode
1358
1359 ElementPasses have limited insight into how an element's properties are used. If you need
1360 that information, consider using a \l PropertyPass instead.
1361
1362 \note ElementPass will only ever consider instantiable types. Therefore, it is unsuitable
1363 to analyze attached types and singletons. Those need to be handled via a PropertyPass.
1364 */
1365
1366/*!
1367 \fn void QQmlSA::ElementPass::run(const Element &element)
1368
1369 Executes if \c shouldRun() returns \c true. Performs the real computation
1370 of the pass on \a element.
1371 This method is meant to be overridden. Calling the base method is not
1372 necessary.
1373 */
1374
1375/*!
1376 Controls whether the \c run() function should be executed on the given \a element.
1377 Subclasses can override this method to improve performance of the analysis by
1378 filtering out elements which are not relevant.
1379
1380 The default implementation unconditionally returns \c true.
1381 */
1382bool ElementPass::shouldRun(const Element &element)
1383{
1384 (void)element;
1385 return true;
1386}
1387
1388/*!
1389 \class QQmlSA::PropertyPass
1390 \inmodule QtQmlCompiler
1391
1392 \brief Base class for all static analysis passes on properties.
1393 */
1394
1395
1396PropertyPass::PropertyPass(PassManager *manager) : GenericPass(manager) { }
1397/*!
1398 Executes whenever a property gets bound to a value.
1399
1400 The property \a propertyName of \a element is bound to the \a value within
1401 \a bindingScope with \a binding.
1402 */
1403void PropertyPass::onBinding(const Element &element, const QString &propertyName,
1404 const QQmlSA::Binding &binding, const Element &bindingScope,
1405 const Element &value)
1406{
1407 Q_UNUSED(element);
1408 Q_UNUSED(propertyName);
1409 Q_UNUSED(binding);
1410 Q_UNUSED(bindingScope);
1411 Q_UNUSED(value);
1412}
1413
1414/*!
1415 Executes whenever a property is read.
1416
1417 The property \a propertyName of \a element is read by an instruction within
1418 \a readScope defined at \a location.
1419 */
1420void PropertyPass::onRead(const Element &element, const QString &propertyName,
1421 const Element &readScope, QQmlSA::SourceLocation location)
1422{
1423 Q_UNUSED(element);
1424 Q_UNUSED(propertyName);
1425 Q_UNUSED(readScope);
1426 Q_UNUSED(location);
1427}
1428
1429/*!
1430 Executes whenever a property is written to.
1431
1432 The property \a propertyName of \a element is written to by an instruction
1433 within \a writeScope defined at \a location. The type of the expression
1434 written to \a propertyName is \a expressionType.
1435 */
1436void PropertyPass::onWrite(const Element &element, const QString &propertyName,
1437 const Element &expressionType, const Element &writeScope,
1438 QQmlSA::SourceLocation location)
1439{
1440 Q_UNUSED(element);
1441 Q_UNUSED(propertyName);
1442 Q_UNUSED(writeScope);
1443 Q_UNUSED(expressionType);
1444 Q_UNUSED(location);
1445}
1446
1447DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager)
1448{
1449}
1450
1451void DebugPropertyPass::onRead(const QQmlSA::Element &element, const QString &propertyName,
1452 const QQmlSA::Element &readScope, QQmlSA::SourceLocation location)
1453{
1454 emitWarning(diagnostic: u"onRead "_s
1455 + (QQmlJSScope::scope(element)->internalName().isEmpty()
1456 ? element.baseTypeName()
1457 : QQmlJSScope::scope(element)->internalName())
1458 + u' ' + propertyName + u' ' + QQmlJSScope::scope(readScope)->internalName()
1459 + u' ' + QString::number(location.startLine()) + u':'
1460 + QString::number(location.startColumn()),
1461 id: qmlPlugin, srcLocation: location);
1462}
1463
1464void DebugPropertyPass::onBinding(const QQmlSA::Element &element, const QString &propertyName,
1465 const QQmlSA::Binding &binding,
1466 const QQmlSA::Element &bindingScope, const QQmlSA::Element &value)
1467{
1468 const auto location = QQmlSA::SourceLocation{ binding.sourceLocation() };
1469 emitWarning(diagnostic: u"onBinding element: '"_s
1470 + (QQmlJSScope::scope(element)->internalName().isEmpty()
1471 ? element.baseTypeName()
1472 : QQmlJSScope::scope(element)->internalName())
1473 + u"' property: '"_s + propertyName + u"' value: '"_s
1474 + (value.isNull() ? u"NULL"_s
1475 : (QQmlJSScope::scope(value)->internalName().isNull()
1476 ? value.baseTypeName()
1477 : QQmlJSScope::scope(value)->internalName()))
1478 + u"' binding_scope: '"_s
1479 + (QQmlJSScope::scope(bindingScope)->internalName().isEmpty()
1480 ? bindingScope.baseTypeName()
1481 : QQmlJSScope::scope(bindingScope)->internalName())
1482 + u"' "_s + QString::number(location.startLine()) + u':'
1483 + QString::number(location.startColumn()),
1484 id: qmlPlugin, srcLocation: location);
1485}
1486
1487void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &propertyName,
1488 const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
1489 QQmlSA::SourceLocation location)
1490{
1491 emitWarning(diagnostic: u"onWrite "_s + element.baseTypeName() + u' ' + propertyName + u' '
1492 + QQmlJSScope::scope(value)->internalName() + u' '
1493 + QQmlJSScope::scope(writeScope)->internalName() + u' '
1494 + QString::number(location.startLine()) + u':'
1495 + QString::number(location.startColumn()),
1496 id: qmlPlugin, srcLocation: location);
1497}
1498
1499/*!
1500 Returns the list of element passes.
1501 */
1502std::vector<std::shared_ptr<ElementPass>> PassManager::elementPasses() const
1503{
1504 Q_D(const PassManager);
1505 return d->m_elementPasses;
1506}
1507
1508/*!
1509 Returns the list of property passes.
1510 */
1511std::multimap<QString, PropertyPassInfo> PassManager::propertyPasses() const
1512{
1513 Q_D(const PassManager);
1514 return d->m_propertyPasses;
1515}
1516
1517/*!
1518 Returns bindings by their source location.
1519 */
1520std::unordered_map<quint32, BindingInfo> PassManager::bindingsByLocation() const
1521{
1522 Q_D(const PassManager);
1523 return d->m_bindingsByLocation;
1524}
1525
1526FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { }
1527
1528FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, const QString &fixDescription,
1529 const QQmlSA::SourceLocation &location,
1530 const QString &replacement)
1531 : m_fixSuggestion{ fixDescription, QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: location),
1532 replacement },
1533 q_ptr{ interface }
1534{
1535}
1536
1537FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface,
1538 const FixSuggestionPrivate &other)
1539 : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface }
1540{
1541}
1542
1543FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other)
1544 : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface }
1545{
1546}
1547
1548QString FixSuggestionPrivate::fixDescription() const
1549{
1550 return m_fixSuggestion.fixDescription();
1551}
1552
1553QQmlSA::SourceLocation FixSuggestionPrivate::location() const
1554{
1555 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: m_fixSuggestion.location());
1556}
1557
1558QString FixSuggestionPrivate::replacement() const
1559{
1560 return m_fixSuggestion.replacement();
1561}
1562
1563void FixSuggestionPrivate::setFileName(const QString &fileName)
1564{
1565 m_fixSuggestion.setFilename(fileName);
1566}
1567
1568QString FixSuggestionPrivate::fileName() const
1569{
1570 return m_fixSuggestion.filename();
1571}
1572
1573void FixSuggestionPrivate::setHint(const QString &hint)
1574{
1575 m_fixSuggestion.setHint(hint);
1576}
1577
1578QString FixSuggestionPrivate::hint() const
1579{
1580 return m_fixSuggestion.hint();
1581}
1582
1583void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable)
1584{
1585 m_fixSuggestion.setAutoApplicable(autoApplicable);
1586}
1587
1588bool FixSuggestionPrivate::isAutoApplicable() const
1589{
1590 return m_fixSuggestion.isAutoApplicable();
1591}
1592
1593QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion)
1594{
1595 return saFixSuggestion.d_func()->m_fixSuggestion;
1596}
1597
1598const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion)
1599{
1600 return saFixSuggestion.d_func()->m_fixSuggestion;
1601}
1602
1603/*!
1604 \class QQmlSA::FixSuggestion
1605 \inmodule QtQmlCompiler
1606
1607 \brief Represents a suggested fix for an issue in the source code.
1608 */
1609
1610
1611FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location,
1612 const QString &replacement)
1613 : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } }
1614{
1615}
1616
1617FixSuggestion::FixSuggestion(const FixSuggestion &other)
1618 : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } }
1619{
1620}
1621
1622FixSuggestion::FixSuggestion(FixSuggestion &&other) noexcept
1623 : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } }
1624{
1625}
1626
1627FixSuggestion &FixSuggestion::operator=(const FixSuggestion &other)
1628{
1629 if (*this == other)
1630 return *this;
1631
1632 d_func()->m_fixSuggestion = other.d_func()->m_fixSuggestion;
1633 return *this;
1634}
1635
1636FixSuggestion &FixSuggestion::operator=(FixSuggestion &&other) noexcept
1637{
1638 if (*this == other)
1639 return *this;
1640
1641 d_func()->m_fixSuggestion = std::move(other.d_func()->m_fixSuggestion);
1642 return *this;
1643}
1644
1645FixSuggestion::~FixSuggestion() = default;
1646
1647/*!
1648 Returns the description of the fix.
1649 */
1650QString QQmlSA::FixSuggestion::fixDescription() const
1651{
1652 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).fixDescription();
1653}
1654
1655/*!
1656 Returns the location where the fix would be applied.
1657 */
1658QQmlSA::SourceLocation FixSuggestion::location() const
1659{
1660 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1661 jsLocation: FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).location());
1662}
1663
1664/*!
1665 Returns the fix that will replace the problematic source code.
1666 */
1667QString FixSuggestion::replacement() const
1668{
1669 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).replacement();
1670}
1671
1672/*!
1673 Sets \a fileName as the name of the file where this fix suggestion applies.
1674 */
1675void FixSuggestion::setFileName(const QString &fileName)
1676{
1677 FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setFilename(fileName);
1678}
1679
1680/*!
1681 Returns the name of the file where this fix suggestion applies.
1682 */
1683QString FixSuggestion::fileName() const
1684{
1685 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).filename();
1686}
1687
1688/*!
1689 Sets \a hint as the hint for this fix suggestion.
1690 */
1691void FixSuggestion::setHint(const QString &hint)
1692{
1693 FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setHint(hint);
1694}
1695
1696/*!
1697 Returns the hint for this fix suggestion.
1698 */
1699QString FixSuggestion::hint() const
1700{
1701 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).hint();
1702}
1703
1704/*!
1705 Sets uses \a autoApplicable to set whtether this suggested fix can be applied automatically.
1706 */
1707void FixSuggestion::setAutoApplicable(bool autoApplicable)
1708{
1709 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setAutoApplicable(autoApplicable);
1710}
1711
1712/*!
1713 Returns whether this suggested fix can be applied automatically.
1714 */
1715bool QQmlSA::FixSuggestion::isAutoApplicable() const
1716{
1717 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).isAutoApplicable();
1718}
1719
1720bool FixSuggestion::operatorEqualsImpl(const FixSuggestion &lhs, const FixSuggestion &rhs)
1721{
1722 return lhs.d_func()->m_fixSuggestion == rhs.d_func()->m_fixSuggestion;
1723}
1724
1725} // namespace QQmlSA
1726
1727QT_END_NAMESPACE
1728

source code of qtdeclarative/src/qmlcompiler/qqmlsa.cpp