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
19QT_BEGIN_NAMESPACE
20
21using namespace Qt::StringLiterals;
22
23namespace QQmlSA {
24
25static_assert(QQmlJSScope::sizeofQQmlSAElement() == sizeof(Element));
26
27/*!
28 \namespace QQmlSA
29 \inmodule QtQmlCompiler
30
31 \brief Provides tools for static analysis on QML programs.
32 */
33
34/*!
35 \enum QQmlSA::MethodType
36 \inmodule QtQmlCompiler
37
38 \brief Describes the type of a \l{QQmlSA::Method}.
39 \value Signal The method is a signal
40 \value Slot The method is a slot
41 \value Method The method is a \l{Q_INVOKABLE} method
42 \value StaticMethod The method is a \l{Q_INVOKABLE} static method
43*/
44
45/*!
46 \enum QQmlSA::AccessSemantics
47 \inmodule QtQmlCompiler
48
49 \brief Describes how a type is accessed and shared.
50 \value Reference The type behaves like an \l{QML Object Types}{Object type}
51 \value Value The type behaves like a \l{QML Value Types}{Value type}
52 \value None The type is a \l{QML Namespaces}{namespace}, or is invalid
53 \value Sequence The type behaves like a \l{QML Sequence Types}{Sequence type}
54
55 \sa {The QML Type System}
56*/
57
58/*!
59 \enum QQmlSA::BindingType
60 \inmodule QtQmlCompiler
61
62 \brief Describes the type of a \l{QQmlSA::Binding}.
63 \value Invalid There is no binding
64 \value BoolLiteral The binding is a bool literal
65 \value NumberLiteral The binding is a number literal
66 \value StringLiteral The binding is a string literal
67 \value RegExpLiteral The binding is a regular expression literal
68 \value Null The binding is a null literal
69 \value Translation The binding is a \l{Text ID based translations}{translation}
70 \value TranslationById The binding is a \l{Text ID based translations}{translation} by id
71 \value Script The binding is a regular script
72 \value Object The binging is an \l{QML Object Types}{Object}
73 \value Interceptor The binding is an interceptor that can intercept writes to properties such as \l[Quick]{Behavior}
74 \value ValueSource The binging is a \l{Defining QML Types from C++#Property Value Sources}{property value source}
75 \value AttachedProperty The binding is an \l{QML Object Attributes#Attached Properties and Attached Signal Handlers}{attached object}
76 \value GroupProperty The binding is a \l{QML Object Attributes#Grouped Properties}{grouped property}
77*/
78
79/*!
80 \enum QQmlSA::ScriptBindingKind
81 \inmodule QtQmlCompiler
82
83 \brief Describes the script type of a \l{QQmlSA::Binding} of type \l{Script}.
84 \value Invalid The binding has an invalid script
85 \value PropertyBinding The binding is bound to a property
86 \value SignalHandler The binding is a \l{Signal and Handler Event System#Receiving signals with signal handlers}{signal handler}
87 \value ChangeHandler The binding is a \l{Signal and Handler Event System#Property change signal handlers}{change handler}
88*/
89
90/*!
91 \enum QQmlSA::ScopeType
92 \brief Describes the type of QML scope.
93 \value JSFunctionScope The scope is a JavaScript function:
94 \badcode
95 Item {
96 function f() : int { <- begin
97 return 1
98 } <- end
99 }
100 \endcode
101 \value JSLexicalScope The scope is a JavaScript lexical scope:
102 \badcode
103 property int i: { <- begin
104 let a = 1
105 { <- begin
106 console.log("hello")
107 } <- end
108 return a
109 } <- end
110 \endcode
111 \value QMLScope The scope is a QML Object:
112 \badcode
113 Item { <- begin
114 x: 50
115 } <- end
116 \endcode
117 \value GroupedPropertyScope The scope is a \l{QML Object Attributes#Grouped Properties}{grouped property}:
118 \badcode
119 Text {
120 font { <- begin
121 pixelSize: 12
122 bold: true
123 } <- end
124 }
125 \endcode
126 \value AttachedPropertyScope The scope is an \l{QML Object Attributes#Attached Properties and Attached Signal Handlers}{attached property}:
127 \badcode
128 Item {
129 Component.onCompleted: console.log("Hello")
130 ^^^^^^^^^
131 \ Scope of attached property Component
132 }
133 \endcode
134 \value EnumScope The scope is a QML \l{QML Enumerations}{enum}:
135 \badcode
136 enum E { <- begin
137 A,
138 B,
139 C
140 } <- end
141 \endcode
142
143 Each entry is shown with an example scope of the matching type in QML code.
144*/
145
146/*!
147 \class QQmlSA::Binding::Bindings
148 \inmodule QtQmlCompiler
149
150 \brief Holds multiple property name to property binding associations.
151 */
152
153/*!
154 Constructs a new Bindings object.
155 */
156Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { }
157
158BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { }
159
160/*!
161 Creates a copy of \a other.
162 */
163Binding::Bindings::Bindings(const Bindings &other)
164 : d_ptr{ new BindingsPrivate{ this, *other.d_func() } }
165{
166}
167
168/*!
169 Destroys the Bindings object.
170 */
171Binding::Bindings::~Bindings() = default;
172
173BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, const BindingsPrivate &other)
174 : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ interface }
175{
176}
177
178BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, BindingsPrivate &&other)
179 : m_bindings{ std::move(other.m_bindings) }, q_ptr{ interface }
180{
181}
182
183/*!
184 Returns an iterator to the beginning of the bindings.
185 */
186QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const
187{
188 Q_D(const Bindings);
189 return d->constBegin();
190}
191
192/*!
193 \fn QMultiHash<QString, Binding>::const_iterator Binding::Bindings::begin() const
194 Same as constBegin().
195 */
196
197QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constBegin() const
198{
199 return m_bindings.constBegin();
200}
201
202/*!
203 Returns an iterator to the end of the bindings.
204 */
205QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const
206{
207 Q_D(const Bindings);
208 return d->constEnd();
209}
210
211/*!
212 \fn QMultiHash<QString, Binding>::const_iterator Binding::Bindings::end() const
213 Same as constEnd().
214 */
215
216QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constEnd() const
217{
218 return m_bindings.constEnd();
219}
220
221/*!
222 \class QQmlSA::Binding
223 \inmodule QtQmlCompiler
224
225 \brief Represents a single QML property binding for a specific type.
226 */
227
228/*!
229 Constructs a new Binding object.
230 */
231Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { }
232
233BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { }
234
235/*!
236 Creates a copy of \a other.
237*/
238Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
239
240/*!
241 Move-constructs a \c Binding instance.
242*/
243Binding::Binding(Binding &&other) noexcept
244 : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
245
246
247/*!
248 Assigns \a other to this Binding instance.
249*/
250Binding &Binding::operator=(const Binding &other)
251{
252 if (*this == other)
253 return *this;
254
255 Q_D(Binding);
256 d->m_binding = other.d_func()->m_binding;
257 d->m_bindingScope = other.d_func()->m_bindingScope;
258 d->q_ptr = this;
259 d->m_isAttached = other.d_func()->m_isAttached;
260 return *this;
261}
262
263/*!
264 Move-assigns \a other to this Binding instance.
265*/
266Binding &Binding::operator=(Binding &&other) noexcept
267{
268 if (*this == other)
269 return *this;
270
271 Q_D(Binding);
272 d->m_binding = std::move(other.d_func()->m_binding);
273 d->m_bindingScope = std::move(other.d_func()->m_bindingScope);
274 d->q_ptr = this;
275 d->m_isAttached = other.d_func()->m_isAttached;
276 return *this;
277}
278
279/*!
280 Destroys the binding.
281*/
282Binding::~Binding() = default;
283
284bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs)
285{
286 return lhs.d_func()->m_binding == rhs.d_func()->m_binding
287 && lhs.d_func()->m_bindingScope == rhs.d_func()->m_bindingScope
288 && lhs.d_func()->m_isAttached == rhs.d_func()->m_isAttached;
289}
290
291BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other)
292 : m_binding{ other.m_binding }, m_bindingScope{ other.m_bindingScope }, q_ptr{ interface },
293 m_isAttached{ other.m_isAttached }
294{
295}
296
297QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding)
298{
299 QQmlSA::Binding saBinding;
300 saBinding.d_func()->m_binding = binding;
301 return saBinding;
302}
303
304QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding)
305{
306 return binding.d_func()->m_binding;
307}
308
309const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding)
310{
311 return binding.d_func()->m_binding;
312}
313
314/*!
315 Returns the type of the property of this binding if it is a group property,
316 otherwise returns an invalid Element.
317 */
318Element Binding::groupType() const
319{
320 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).groupType());
321}
322
323/*!
324 Returns the Element scope in which the binding is defined.
325 */
326Element Binding::bindingScope() const
327{
328 return BindingPrivate::get(binding: this)->m_bindingScope;
329}
330
331/*!
332 Returns the type of this binding.
333 */
334QQmlSA::BindingType Binding::bindingType() const
335{
336 return BindingPrivate::binding(binding: *this).bindingType();
337}
338
339/*!
340 Returns the associated string literal if the content type of this binding is
341 StringLiteral, otherwise returns an empty string.
342 */
343QString Binding::stringValue() const
344{
345 return BindingPrivate::binding(binding: *this).stringValue();
346}
347
348/*!
349 Returns the name of the property bound with this binding.
350 */
351QString Binding::propertyName() const
352{
353 return BindingPrivate::binding(binding: *this).propertyName();
354}
355
356/*!
357 Returns \c true if this type is attached to another one, \c false otherwise.
358 */
359bool Binding::isAttached() const
360{
361 return BindingPrivate::get(binding: this)->m_isAttached;
362}
363
364/*!
365 Returns the attached type if the content type of this binding is
366 AttachedProperty, otherwise returns an invalid Element.
367 */
368Element Binding::attachedType() const
369{
370 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).attachedType());
371}
372
373#if QT_DEPRECATED_SINCE(6, 9)
374/*!
375 Returns the attached type if the content type of this binding is
376 AttachedProperty, otherwise returns an invalid Element.
377
378 \deprecated [6.9] Use the better named attachedType() method.
379 */
380Element Binding::attachingType() const
381{
382 return attachedType();
383}
384#endif
385
386/*!
387 Returns the location in the QML code where this binding is defined.
388 */
389QQmlSA::SourceLocation Binding::sourceLocation() const
390{
391 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
392 jsLocation: BindingPrivate::binding(binding: *this).sourceLocation());
393}
394
395/*!
396 Returns the associated number if the content type of this binding is
397 NumberLiteral, otherwise returns 0.
398 */
399double Binding::numberValue() const
400{
401 return BindingPrivate::binding(binding: *this).numberValue();
402}
403
404/*!
405 Returns the kind of the associated script if the content type of this
406 binding is Script, otherwise returns Invalid.
407 */
408QQmlSA::ScriptBindingKind Binding::scriptKind() const
409{
410 return BindingPrivate::binding(binding: *this).scriptKind();
411}
412
413/*!
414 Returns \c true if this binding has an objects, otherwise returns \c false.
415 */
416bool Binding::hasObject() const
417{
418 return BindingPrivate::binding(binding: *this).hasObject();
419}
420
421/*!
422 Returns the type of the associated object if the content type of this
423 binding is Object, otherwise returns an invalid Element.
424 */
425QQmlSA::Element Binding::objectType() const
426{
427 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).objectType());
428}
429
430/*!
431 Returns whether this binding has script value type undefined like when it
432 is assigned \c undefined. If the content type of this binding is not
433 \l{QQmlSA::BindingType::Script}, returns \c false.
434 */
435bool Binding::hasUndefinedScriptValue() const
436{
437 const auto &jsBinding = BindingPrivate::binding(binding: *this);
438 return jsBinding.bindingType() == BindingType::Script
439 && jsBinding.scriptValueType() == ScriptValue_Undefined;
440}
441
442/*!
443 Returns whether this binding has script value type function like when it
444 is assigned a (lambda) method, an arrow function or a statement block. If
445 the content type of this binding is not \l{QQmlSA::BindingType::Script},
446 returns \c false.
447 */
448bool Binding::hasFunctionScriptValue() const
449{
450 const auto &jsBinding = BindingPrivate::binding(binding: *this);
451 return jsBinding.bindingType() == BindingType::Script
452 && jsBinding.scriptValueType() == ScriptValue_Function;
453}
454
455/*!
456 Returns \c true if \a bindingType is a literal type, and \c false
457 otherwise.
458 */
459bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType)
460{
461 return QQmlJSMetaPropertyBinding::isLiteralBinding(type: bindingType);
462}
463
464/*!
465 \fn friend bool Binding::operator==(const Binding &lhs, const Binding &rhs)
466 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise. Two
467 \c Bindings are considered equal if their property name, content type, and
468 source location match.
469 */
470/*!
471 \fn friend bool Binding::operator!=(const Binding &lhs, const Binding &rhs)
472 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
473 Two \c Bindings are considered equal if their property name, content type,
474 and source location match.
475 */
476
477/*!
478 Constructs a new Methods object.
479*/
480QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { }
481
482/*!
483 Creates a copy of \a other.
484 */
485QQmlSA::Method::Methods::Methods(const Methods &other)
486 : d_ptr{ new MethodsPrivate{ this, *other.d_func() } }
487{
488}
489
490/*!
491 Destroys the Methods instance.
492 */
493QQmlSA::Method::Methods::~Methods() = default;
494
495/*!
496 Returns an iterator to the beginning of the methods.
497 */
498QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const
499{
500 Q_D(const Methods);
501 return d->constBegin();
502}
503
504/*!
505 \fn QMultiHash<QString, QQmlSA::Method>::const_iterator QQmlSA::Method::Methods::begin() const
506 Returns an iterator to the beginning of the methods.
507 */
508
509QMultiHash<QString, Method>::const_iterator MethodsPrivate::constBegin() const
510{
511 return m_methods.constBegin();
512}
513
514/*!
515 Returns an iterator to the end of the methods.
516 */
517QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const
518{
519 Q_D(const Methods);
520 return d->constEnd();
521}
522
523/*!
524 \fn QMultiHash<QString, QQmlSA::Method>::const_iterator QQmlSA::Method::Methods::end() const
525 Returns an iterator to the end of the methods.
526 */
527
528QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const
529{
530 return m_methods.constEnd();
531}
532
533MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { }
534
535MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other)
536 : m_methods{ other.m_methods }, q_ptr{ interface }
537{
538}
539
540MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other)
541 : m_methods{ std::move(other.m_methods) }, q_ptr{ interface }
542{
543}
544
545MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { }
546
547MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other)
548 : m_method{ other.m_method }, q_ptr{ interface }
549{
550}
551
552QString MethodPrivate::methodName() const
553{
554 return m_method.methodName();
555}
556
557QQmlSA::SourceLocation MethodPrivate::sourceLocation() const
558{
559 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: m_method.sourceLocation());
560}
561
562MethodType MethodPrivate::methodType() const
563{
564 return m_method.methodType();
565}
566
567/*!
568 \class QQmlSA::Method
569 \inmodule QtQmlCompiler
570
571 \brief Represents a QML method.
572 */
573
574/*!
575 Constructs a new Method object.
576 */
577Method::Method() : d_ptr{ new MethodPrivate{ this } } { }
578
579/*!
580 Creates a copy of \a other.
581 */
582Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { }
583
584/*!
585 Move-constructs a Method instance.
586 */
587Method::Method(Method &&other) noexcept
588 : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } }
589{
590}
591
592/*!
593 Assigns \a other to this Method instance.
594 */
595Method &Method::operator=(const Method &other)
596{
597 if (*this == other)
598 return *this;
599
600 d_func()->m_method = other.d_func()->m_method;
601 d_func()->q_ptr = this;
602 return *this;
603}
604
605/*!
606 Move-assigns \a other to this Method instance.
607 */
608Method &Method::operator=(Method &&other) noexcept
609{
610 if (*this == other)
611 return *this;
612
613 d_func()->m_method = std::move(other.d_func()->m_method);
614 d_func()->q_ptr = this;
615 return *this;
616}
617
618/*!
619 Destroys the Method.
620 */
621Method::~Method() = default;
622
623/*!
624 Returns the name of the this method.
625 */
626QString Method::methodName() const
627{
628 Q_D(const Method);
629 return d->methodName();
630}
631
632/*!
633 Returns the type of this method.
634 */
635MethodType Method::methodType() const
636{
637 Q_D(const Method);
638 return d->methodType();
639}
640
641/*!
642 \fn friend bool Method::operator==(const Method &lhs, const Method &rhs)
643 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
644 */
645/*!
646 \fn friend bool Method::operator!=(const Method &lhs, const Method &rhs)
647 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
648 */
649
650/*!
651 Returns the location in the QML code where this method is defined.
652 */
653QQmlSA::SourceLocation Method::sourceLocation() const
654{
655 Q_D(const Method);
656 return d->sourceLocation();
657}
658
659bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs)
660{
661 return lhs.d_func()->m_method == rhs.d_func()->m_method;
662}
663
664QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod)
665{
666 QQmlSA::Method saMethod;
667 auto &wrappedMethod = saMethod.d_func()->m_method;
668 wrappedMethod = jsMethod;
669 return saMethod;
670}
671
672QQmlSA::Method::Methods
673MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash)
674{
675 QMultiHash<QString, QQmlSA::Method> saMethods;
676 for (const auto &[key, value] : hash.asKeyValueRange()) {
677 saMethods.insert(key, value: MethodPrivate::createMethod(jsMethod: value));
678 }
679
680 QQmlSA::Method::Methods methods;
681 methods.d_func()->m_methods = std::move(saMethods);
682 return methods;
683}
684
685QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method)
686{
687 return method.d_func()->m_method;
688}
689
690PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { }
691
692PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other)
693 : m_property{ other.m_property }, q_ptr{ interface }
694{
695}
696
697PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other)
698 : m_property{ std::move(other.m_property) }, q_ptr{ interface }
699{
700}
701
702QString PropertyPrivate::typeName() const
703{
704 return m_property.typeName();
705}
706
707bool PropertyPrivate::isValid() const
708{
709 return m_property.isValid();
710}
711
712/*!
713 Returns whether this property is readonly. Properties defined in QML are readonly when their
714 definition has the 'readonly' keyword. Properties defined in C++ are readonly when they do not
715 have a WRITE accessor function.
716 */
717bool PropertyPrivate::isReadonly() const
718{
719 return !m_property.isWritable();
720}
721
722/*!
723 Returns the type that this property was defined with.
724 */
725QQmlSA::Element PropertyPrivate::type() const
726{
727 return QQmlJSScope::createQQmlSAElement(m_property.type());
728}
729
730QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property)
731{
732 return property.d_func()->m_property;
733}
734
735QQmlSA::Property PropertyPrivate::createProperty(const QQmlJSMetaProperty &property)
736{
737 QQmlSA::Property saProperty;
738 auto &wrappedProperty = saProperty.d_func()->m_property;
739 wrappedProperty = property;
740 return saProperty;
741}
742
743/*!
744 \class QQmlSA::Property
745 \inmodule QtQmlCompiler
746
747 \brief Represents a QML property.
748 */
749
750/*!
751 Constructs a new Property object.
752 */
753Property::Property() : d_ptr{ new PropertyPrivate{ this } } { }
754
755/*!
756 Creates a copy of \a other.
757 */
758Property::Property(const Property &other)
759 : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { }
760
761/*!
762 Move-constructs a Property instance.
763 */
764Property::Property(Property &&other) noexcept
765 : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } }
766{
767}
768
769/*!
770 Assigns \a other to this Property instance.
771 */
772Property &Property::operator=(const Property &other)
773{
774 if (*this == other)
775 return *this;
776
777 d_func()->m_property = other.d_func()->m_property;
778 d_func()->q_ptr = this;
779 return *this;
780}
781
782/*!
783 Move-assigns \a other to this Property instance.
784 */
785Property &Property::operator=(Property &&other) noexcept
786{
787 if (*this == other)
788 return *this;
789
790 d_func()->m_property = std::move(other.d_func()->m_property);
791 d_func()->q_ptr = this;
792 return *this;
793}
794
795/*!
796 Destroys this property.
797 */
798Property::~Property() = default;
799
800/*!
801 Returns the name of the type of this property.
802 */
803QString Property::typeName() const
804{
805 Q_D(const Property);
806 return d->typeName();
807}
808
809/*!
810 Returns \c true if this property is valid, \c false otherwise.
811 */
812bool Property::isValid() const
813{
814 Q_D(const Property);
815 return d->isValid();
816}
817
818/*!
819 Returns \c true if this property is read-only, \c false otherwise.
820 */
821bool Property::isReadonly() const
822{
823 Q_D(const Property);
824 return d->isReadonly();
825}
826
827/*!
828 Returns the type of this property.
829*/
830QQmlSA::Element Property::type() const
831{
832 Q_D(const Property);
833 return d->type();
834}
835
836/*!
837 \fn friend bool Property::operator==(const Property &lhs, const Property &rhs)
838 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
839 */
840/*!
841 \fn friend bool Property::operator!=(const Property &lhs, const Property &rhs)
842 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
843 */
844
845
846bool Property::operatorEqualsImpl(const Property &lhs, const Property &rhs)
847{
848 return lhs.d_func()->m_property == rhs.d_func()->m_property;
849}
850
851/*!
852 \class QQmlSA::Element
853 \inmodule QtQmlCompiler
854
855 \brief Represents a QML type.
856 */
857
858/*!
859 Constructs a new Element object.
860 */
861Element::Element()
862{
863 new (m_data) QQmlJSScope::ConstPtr();
864}
865
866/*!
867 Creates a copy of \a other.
868 */
869Element::Element(const Element &other)
870{
871 new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other));
872}
873
874/*!
875 \fn Element::Element(Element &&other) noexcept
876 Move-constructs an Element instance.
877 */
878
879/*!
880 Assigns \a other to this element instance.
881 */
882Element &Element::operator=(const Element &other)
883{
884 if (this == &other)
885 return *this;
886
887 *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other);
888 return *this;
889}
890
891/*!
892 \fn QQmlSA::Element &QQmlSA::Element::operator=(QQmlSA::Element &&other)
893 Move-assigns \a other to this Element instance.
894 */
895
896/*!
897 Destroys the element.
898 */
899Element::~Element()
900{
901 (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr();
902}
903
904/*!
905 Returns the type of Element's scope.
906 */
907QQmlJSScope::ScopeType Element::scopeType() const
908{
909 return QQmlJSScope::scope(*this)->scopeType();
910}
911
912/*!
913 Returns the Element this Element derives from.
914 */
915Element Element::baseType() const
916{
917 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->baseType());
918}
919
920/*!
921 Returns the name of the Element this Element derives from.
922 */
923QString Element::baseTypeName() const
924{
925 return QQmlJSScope::prettyName(name: QQmlJSScope::scope(*this)->baseTypeName());
926}
927
928/*!
929 Returns the Element that encloses this Element.
930 */
931Element Element::parentScope() const
932{
933 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->parentScope());
934}
935
936/*!
937 Returns whether this Element inherits from \a element.
938 */
939bool Element::inherits(const Element &element) const
940{
941 return QQmlJSScope::scope(*this)->inherits(base: QQmlJSScope::scope(element));
942}
943
944/*!
945 Returns whether this Element is the root component of its QML file.
946 */
947bool Element::isFileRootComponent() const
948{
949 return QQmlJSScope::scope(*this)->isFileRootComponent();
950}
951
952/*!
953 Returns \c true if this element is null, \c false otherwise.
954 */
955bool Element::isNull() const
956{
957 return QQmlJSScope::scope(*this).isNull();
958}
959
960/*!
961 \internal
962 */
963QString Element::internalId() const
964{
965 return QQmlJSScope::scope(*this)->internalName();
966}
967
968/*!
969 Returns the access semantics of this Element. For example, Reference,
970 Value or Sequence.
971 */
972AccessSemantics Element::accessSemantics() const
973{
974 return QQmlJSScope::scope(*this)->accessSemantics();
975}
976
977/*!
978 Returns \c true for objects defined from Qml, and \c false for objects declared from C++.
979 */
980bool QQmlSA::Element::isComposite() const
981{
982 return QQmlJSScope::scope(*this)->isComposite();
983}
984
985/*!
986 Returns whether this Element has a property with the name \a propertyName.
987 */
988bool Element::hasProperty(const QString &propertyName) const
989{
990 return QQmlJSScope::scope(*this)->hasProperty(name: propertyName);
991}
992
993/*!
994 Returns whether this Element defines a property with the name \a propertyName
995 which is not defined on its base or extension objects.
996 */
997bool Element::hasOwnProperty(const QString &propertyName) const
998{
999 return QQmlJSScope::scope(*this)->hasOwnProperty(name: propertyName);
1000}
1001
1002/*!
1003 Returns the property with the name \a propertyName if it is found in this
1004 Element or its base and extension objects, otherwise returns an invalid property.
1005 */
1006QQmlSA::Property Element::property(const QString &propertyName) const
1007{
1008 return PropertyPrivate::createProperty(property: QQmlJSScope::scope(*this)->property(name: propertyName));
1009}
1010
1011/*!
1012 Returns whether the property with the name \a propertyName resolved on this
1013 Element is required. Returns false if the the property couldn't be found.
1014 */
1015bool Element::isPropertyRequired(const QString &propertyName) const
1016{
1017 return QQmlJSScope::scope(*this)->isPropertyRequired(name: propertyName);
1018}
1019
1020/*!
1021 Returns the name of the default property of this Element. If it doesn't
1022 have one, returns an empty string.
1023 */
1024QString Element::defaultPropertyName() const
1025{
1026 return QQmlJSScope::scope(*this)->defaultPropertyName();
1027}
1028
1029/*!
1030 Returns whether this Element has a method with the name \a methodName.
1031 */
1032bool Element::hasMethod(const QString &methodName) const
1033{
1034 return QQmlJSScope::scope(*this)->hasMethod(name: methodName);
1035}
1036
1037/*!
1038 \class QQmlSA::Method::Methods
1039 \inmodule QtQmlCompiler
1040
1041 \brief Holds multiple method name to method associations.
1042 */
1043
1044/*!
1045 Returns this Elements's methods, which are not defined on its base or
1046 extension objects.
1047 */
1048Method::Methods Element::ownMethods() const
1049{
1050 return MethodsPrivate::createMethods(hash: QQmlJSScope::scope(*this)->ownMethods());
1051}
1052
1053/*!
1054 Returns the location in the QML code where this Element is defined.
1055 */
1056QQmlSA::SourceLocation Element::sourceLocation() const
1057{
1058 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1059 jsLocation: QQmlJSScope::scope(*this)->sourceLocation());
1060}
1061
1062/*!
1063 Returns the location in the QML code where this Element is assigned its id, if it has one.
1064 */
1065QQmlSA::SourceLocation Element::idSourceLocation() const
1066{
1067 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1068 jsLocation: QQmlJSScope::scope(*this)->idSourceLocation());
1069}
1070
1071/*!
1072 Returns the file path of the QML code that defines this Element.
1073 */
1074QString Element::filePath() const
1075{
1076 return QQmlJSScope::scope(*this)->filePath();
1077}
1078
1079/*!
1080 Returns whether this Element has a property binding with the name \a name.
1081 */
1082bool Element::hasPropertyBindings(const QString &name) const
1083{
1084 return QQmlJSScope::scope(*this)->hasPropertyBindings(name);
1085}
1086
1087/*!
1088 Returns whether this Element has property bindings which are not defined in
1089 its base or extension objects and that have name \a propertyName.
1090 */
1091bool Element::hasOwnPropertyBindings(const QString &propertyName) const
1092{
1093 return QQmlJSScope::scope(*this)->hasOwnPropertyBindings(name: propertyName);
1094}
1095
1096/*!
1097 Returns this Element's property bindings which are not defined on its base
1098 or extension objects.
1099 */
1100Binding::Bindings Element::ownPropertyBindings() const
1101{
1102 return BindingsPrivate::createBindings(QQmlJSScope::scope(*this)->ownPropertyBindings());
1103}
1104
1105/*!
1106 Returns this Element's property bindings which are not defined on its base
1107 or extension objects and that have the name \a propertyName.
1108 */
1109Binding::Bindings Element::ownPropertyBindings(const QString &propertyName) const
1110{
1111 return BindingsPrivate::createBindings(
1112 QQmlJSScope::scope(*this)->ownPropertyBindings(name: propertyName));
1113}
1114
1115/*!
1116 Returns this Element's property bindings that have the name \a propertyName.
1117 */
1118QList<Binding> Element::propertyBindings(const QString &propertyName) const
1119{
1120 const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(name: propertyName);
1121
1122 QList<Binding> saBindings;
1123 for (const auto &jsBinding : bindings) {
1124 saBindings.push_back(t: BindingPrivate::createBinding(binding: jsBinding));
1125 }
1126 return saBindings;
1127}
1128
1129QQmlSA::Binding::Bindings
1130BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash)
1131{
1132 QMultiHash<QString, QQmlSA::Binding> saBindings;
1133 for (const auto &[key, value] : hash.asKeyValueRange()) {
1134 saBindings.insert(key, value: BindingPrivate::createBinding(binding: value));
1135 }
1136
1137 QQmlSA::Binding::Bindings bindings;
1138 bindings.d_func()->m_bindings = std::move(saBindings);
1139 return bindings;
1140}
1141
1142QQmlSA::Binding::Bindings BindingsPrivate::createBindings(
1143 std::pair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
1144 QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> iterators)
1145{
1146 QMultiHash<QString, QQmlSA::Binding> saBindings;
1147 for (auto it = iterators.first; it != iterators.second; ++it) {
1148 saBindings.insert(key: it.key(), value: BindingPrivate::createBinding(binding: it.value()));
1149 }
1150
1151 QQmlSA::Binding::Bindings bindings;
1152 bindings.d_func()->m_bindings = std::move(saBindings);
1153 return bindings;
1154}
1155
1156/*!
1157 Returns \c true if this element is not null, \c false otherwise.
1158 */
1159Element::operator bool() const
1160{
1161 return bool(QQmlJSScope::scope(*this));
1162}
1163
1164/*!
1165 Returns \c true if this element is null, \c false otherwise.
1166 */
1167bool Element::operator!() const
1168{
1169 return !QQmlJSScope::scope(*this);
1170}
1171
1172/*!
1173 Returns the name of this Element.
1174 */
1175QString Element::name() const
1176{
1177 if (isNull())
1178 return {};
1179 return QQmlJSScope::prettyName(name: QQmlJSScope::scope(*this)->internalName());
1180}
1181
1182/*!
1183 \fn friend inline bool Element::operator==(const Element &lhs, const Element &rhs)
1184 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
1185 */
1186/*!
1187 \fn friend inline bool Element::operator!=(const Element &lhs, const Element &rhs)
1188 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
1189 */
1190
1191bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs)
1192{
1193 return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs);
1194}
1195
1196/*!
1197 \fn friend inline qsizetype Element::qHash(const Element &key, qsizetype seed) noexcept
1198 Returns the hash for \a key using \a seed to seed the calculation.
1199*/
1200
1201qsizetype Element::qHashImpl(const Element &key, qsizetype seed) noexcept
1202{
1203 return qHash(ptr: QQmlJSScope::scope(key), seed);
1204}
1205
1206/*!
1207 \class QQmlSA::GenericPass
1208 \inmodule QtQmlCompiler
1209
1210 \brief The base class for static analysis passes.
1211
1212 This class contains common functionality used by more specific passses.
1213 Custom passes should not directly derive from it, but rather from one of
1214 its subclasses.
1215 \sa ElementPass, PropertyPass
1216 */
1217
1218class GenericPassPrivate {
1219 Q_DECLARE_PUBLIC(GenericPass);
1220
1221public:
1222 GenericPassPrivate(GenericPass *interface, PassManager *manager)
1223 : m_manager{ manager }, q_ptr{ interface }
1224 {
1225 Q_ASSERT(manager);
1226 }
1227
1228private:
1229 PassManager *m_manager = nullptr;
1230
1231 GenericPass *q_ptr = nullptr;
1232};
1233
1234/*!
1235 Creates a generic pass.
1236 */
1237GenericPass::GenericPass(PassManager *manager)
1238 : d_ptr{ new GenericPassPrivate{ this, manager } } { }
1239
1240/*!
1241 Destroys the GenericPass instance.
1242 */
1243GenericPass::~GenericPass() = default;
1244
1245/*!
1246 Emits a warning message \a diagnostic about an issue of type \a id.
1247 */
1248void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id)
1249{
1250 emitWarning(diagnostic, id, srcLocation: QQmlSA::SourceLocation{});
1251}
1252
1253/*!
1254 Emits warning message \a diagnostic about an issue of type \a id located at
1255 \a srcLocation.
1256 */
1257void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
1258 QQmlSA::SourceLocation srcLocation)
1259{
1260 Q_D(const GenericPass);
1261 PassManagerPrivate::visitor(*d->m_manager)
1262 ->logger()
1263 ->log(message: diagnostic.toString(), id,
1264 srcLocation: QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: srcLocation));
1265}
1266
1267/*!
1268 Emits a warning message \a diagnostic about an issue of type \a id located at
1269 \a srcLocation and with suggested fix \a fix.
1270 */
1271void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
1272 QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix)
1273{
1274 Q_D(const GenericPass);
1275 PassManagerPrivate::visitor(*d->m_manager)
1276 ->logger()
1277 ->log(message: diagnostic.toString(), id,
1278 srcLocation: QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: srcLocation), showContext: true, showFileName: true,
1279 suggestion: FixSuggestionPrivate::fixSuggestion(fix));
1280}
1281
1282/*!
1283 Returns the type corresponding to \a typeName inside the
1284 currently analysed file.
1285 */
1286Element GenericPass::resolveTypeInFileScope(QAnyStringView typeName)
1287{
1288 Q_D(const GenericPass);
1289 const auto scope =
1290 PassManagerPrivate::visitor(*d->m_manager)->imports().type(name: typeName.toString()).scope;
1291 return QQmlJSScope::createQQmlSAElement(scope);
1292}
1293
1294/*!
1295 Returns the attached type corresponding to \a typeName used inside
1296 the currently analysed file.
1297 */
1298Element GenericPass::resolveAttachedInFileScope(QAnyStringView typeName)
1299{
1300 const auto type = resolveTypeInFileScope(typeName);
1301 const auto scope = QQmlJSScope::scope(type);
1302
1303 if (scope.isNull())
1304 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::ConstPtr(nullptr));
1305
1306 return QQmlJSScope::createQQmlSAElement(scope->attachedType());
1307}
1308
1309/*!
1310 Returns the type of \a typeName defined in module \a moduleName.
1311 If an attached type and a non-attached type share the same name
1312 (for example, \c ListView), the \l Element corresponding to the
1313 non-attached type is returned.
1314 To obtain the attached type, use \l resolveAttached.
1315 */
1316Element GenericPass::resolveType(QAnyStringView moduleName, QAnyStringView typeName)
1317{
1318 Q_D(const GenericPass);
1319 QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
1320 const auto module = typeImporter->importModule(module: moduleName.toString());
1321 const auto scope = module.type(name: typeName.toString()).scope;
1322 return QQmlJSScope::createQQmlSAElement(scope);
1323}
1324
1325/*!
1326 Returns the type of the built-in type identified by \a typeName.
1327 Built-in types encompass \c{C++} types which the QML engine can handle
1328 without any imports (e.g. \l QDateTime and \l QString), global EcmaScript
1329 objects like \c Number, as well as the \l {QML Global Object}
1330 {global Qt object}.
1331 */
1332Element GenericPass::resolveBuiltinType(QAnyStringView typeName) const
1333{
1334 Q_D(const GenericPass);
1335 QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
1336 auto typeNameString = typeName.toString();
1337 // we have to check both cpp names
1338 auto scope = typeImporter->builtinInternalNames().type(name: typeNameString).scope;
1339 if (!scope) {
1340 // and qml names (e.g. for bool) - builtinImportHelper is private, so we can't do it in one call
1341 auto builtins = typeImporter->importHardCodedBuiltins();
1342 scope = builtins.type(name: typeNameString).scope;
1343 }
1344 return QQmlJSScope::createQQmlSAElement(scope);
1345}
1346
1347/*!
1348 Returns the attached type of \a typeName defined in module \a moduleName.
1349 */
1350Element GenericPass::resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
1351{
1352 const auto &resolvedType = resolveType(moduleName, typeName);
1353 return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(resolvedType)->attachedType());
1354}
1355
1356/*!
1357 Returns the element representing the type of literal in \a binding. If the
1358 binding does not contain a literal value, a null Element is returned.
1359 */
1360Element GenericPass::resolveLiteralType(const QQmlSA::Binding &binding)
1361{
1362 Q_D(const GenericPass);
1363
1364 return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding).literalType(
1365 resolver: PassManagerPrivate::resolver(*d->m_manager)));
1366}
1367
1368/*!
1369 Returns the element in \a context that has id \a id.
1370 */
1371Element GenericPass::resolveIdToElement(QAnyStringView id, const Element &context)
1372{
1373 Q_D(const GenericPass);
1374 const auto scope = PassManagerPrivate::visitor(*d->m_manager)
1375 ->addressableScopes()
1376 .scope(id: id.toString(), referrer: QQmlJSScope::scope(context));
1377 return QQmlJSScope::createQQmlSAElement(scope);
1378}
1379
1380/*!
1381 Returns the id of \a element in a given \a context.
1382 */
1383QString GenericPass::resolveElementToId(const Element &element, const Element &context)
1384{
1385 Q_D(const GenericPass);
1386 return PassManagerPrivate::visitor(*d->m_manager)
1387 ->addressableScopes()
1388 .id(scope: QQmlJSScope::scope(element), referrer: QQmlJSScope::scope(context));
1389}
1390
1391/*!
1392 Returns the source code located within \a location.
1393 */
1394QString GenericPass::sourceCode(QQmlSA::SourceLocation location)
1395{
1396 Q_D(const GenericPass);
1397 return PassManagerPrivate::visitor(*d->m_manager)
1398 ->logger()
1399 ->code()
1400 .mid(position: location.offset(), n: location.length());
1401}
1402
1403/*!
1404 \class QQmlSA::PassManager
1405 \inmodule QtQmlCompiler
1406
1407 \brief Can analyze an element and its children with static analysis passes.
1408 */
1409
1410// explicitly defaulted out-of-line for PIMPL
1411PassManager::PassManager() = default;
1412PassManager::~PassManager() = default;
1413
1414/*!
1415 Registers a static analysis \a pass to be run on all elements.
1416 */
1417void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass)
1418{
1419 Q_D(PassManager);
1420 d->registerElementPass(pass: std::move(pass));
1421}
1422
1423/*!
1424 \internal
1425 \brief PassManager::registerElementPass registers ElementPass
1426 with the pass manager.
1427 \param pass The registered pass. Ownership is transferred to the pass manager.
1428 */
1429void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass)
1430{
1431 m_elementPasses.push_back(x: std::move(pass));
1432}
1433
1434enum LookupMode { Register, Lookup };
1435static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup)
1436{
1437 QString name;
1438 if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) {
1439 // Bail out with an invalid name, this type is so screwed up we can't do anything reasonable
1440 // with it We should have warned about it in another plac
1441 if (element.isNull() || element.baseType().isNull())
1442 return u"$INVALID$"_s;
1443 name = QQmlJSScope::scope(element.baseType())->internalName();
1444 } else {
1445 name = QQmlJSScope::scope(element)->internalName();
1446 }
1447
1448 const QString filePath =
1449 (mode == Register || !element.baseType() ? element : element.baseType()).filePath();
1450
1451 if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(s: u".h"))
1452 name += u'@' + filePath;
1453 return name;
1454}
1455
1456/*!
1457 Registers a static analysis pass for properties. The \a pass will be run on
1458 every property matching the \a moduleName, \a typeName and \a propertyName.
1459
1460 Omitting the \a propertyName will register this pass for all properties
1461 matching the \a typeName and \a moduleName.
1462
1463 Setting \a allowInheritance to \c true means that the filtering on the type
1464 also accepts types deriving from \a typeName.
1465
1466 \a pass is passed as a \c{std::shared_ptr} to allow reusing the same pass
1467 on multiple elements:
1468 \code
1469 auto titleValiadorPass = std::make_shared<TitleValidatorPass>(manager);
1470 manager->registerPropertyPass(titleValidatorPass,
1471 "QtQuick", "Window", "title");
1472 manager->registerPropertyPass(titleValidatorPass,
1473 "QtQuick.Controls", "Dialog", "title");
1474 \endcode
1475
1476 \note Running analysis passes on too many items can be expensive. This is
1477 why it is generally good to filter down the set of properties of a pass
1478 using the \a moduleName, \a typeName and \a propertyName.
1479
1480 Returns \c true if the pass was successfully added, \c false otherwise.
1481 Adding a pass fails when the \l{QQmlSA::Element}{Element} specified by
1482 \a moduleName and \a typeName does not exist.
1483
1484 \sa PropertyPass
1485*/
1486bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
1487 QAnyStringView moduleName, QAnyStringView typeName,
1488 QAnyStringView propertyName, bool allowInheritance)
1489{
1490 Q_D(PassManager);
1491 return d->registerPropertyPass(pass, moduleName, typeName, propertyName, allowInheritance);
1492}
1493
1494bool PassManagerPrivate::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
1495 QAnyStringView moduleName, QAnyStringView typeName,
1496 QAnyStringView propertyName, bool allowInheritance)
1497{
1498 if (moduleName.isEmpty() != typeName.isEmpty()) {
1499 qWarning() << "Both the moduleName and the typeName must be specified "
1500 "for the pass to be registered for a specific element.";
1501 }
1502
1503 QString name;
1504 if (!moduleName.isEmpty() && !typeName.isEmpty()) {
1505 auto typeImporter = m_visitor->importer();
1506 auto module = typeImporter->importModule(module: moduleName.toString());
1507 auto element = QQmlJSScope::createQQmlSAElement(module.type(name: typeName.toString()).scope);
1508
1509 if (element.isNull())
1510 return false;
1511
1512 name = lookupName(element, mode: Register);
1513 }
1514 const QQmlSA::PropertyPassInvocation passInfo{ .property: propertyName.toString(), .pass: std::move(pass),
1515 .allowInheritance: allowInheritance };
1516 m_propertyPasses.insert(x: { name, passInfo });
1517
1518 return true;
1519}
1520
1521bool PassManagerPrivate::registerPropertyPassOnBuiltinType(std::shared_ptr<PropertyPass> pass,
1522 QAnyStringView builtinTypeName,
1523 QAnyStringView propertyName,
1524 bool allowInheritance)
1525{
1526 auto typeImporter = m_visitor->importer();
1527 const auto scope = typeImporter->builtinInternalNames().type(name: builtinTypeName.toString()).scope;
1528 const auto element = QQmlJSScope::createQQmlSAElement(scope);
1529
1530 if (element.isNull())
1531 return false;
1532
1533 const QString name = lookupName(element, mode: Register);
1534
1535 const QQmlSA::PropertyPassInvocation passInfo{ .property: propertyName.toString(), .pass: std::move(pass),
1536 .allowInheritance: allowInheritance };
1537 m_propertyPasses.insert(x: { name, passInfo });
1538
1539 return true;
1540}
1541
1542void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope,
1543 const QString prefix, bool isAttached)
1544{
1545 const Element &currentScope = scope.isNull() ? element : scope;
1546 auto ownBindings = currentScope.ownPropertyBindings();
1547 for (auto &binding : ownBindings) {
1548 switch (binding.bindingType()) {
1549 case QQmlSA::BindingType::GroupProperty:
1550 addBindingSourceLocations(element, scope: Element{ binding.groupType() },
1551 prefix: prefix + binding.propertyName() + u'.');
1552 continue; // don't insert into m_bindingsByLocation
1553 case QQmlSA::BindingType::AttachedProperty:
1554 addBindingSourceLocations(element, scope: Element{ binding.attachedType() },
1555 prefix: prefix + binding.propertyName() + u'.', isAttached: true);
1556 continue; // don't insert into m_bindingsByLocation
1557 case QQmlSA::BindingType::Translation: {
1558 analyzeCall(element: QQmlJSScope::createQQmlSAElement(m_typeResolver->jsGlobalObject()),
1559 propertyName: u"qsTr"_s, readScope: element, location: binding.sourceLocation());
1560 break;
1561 }
1562 case QQmlSA::BindingType::TranslationById: {
1563 analyzeCall(element: QQmlJSScope::createQQmlSAElement(m_typeResolver->jsGlobalObject()),
1564 propertyName: u"qsTrId"_s, readScope: element, location: binding.sourceLocation());
1565 break;
1566 }
1567 default:
1568 break;
1569 }
1570
1571 BindingPrivate::get(binding: &binding)->setBindingScope(currentScope);
1572 BindingPrivate::get(binding: &binding)->setPropertyName(prefix + binding.propertyName());
1573 BindingPrivate::get(binding: &binding)->setIsAttached(isAttached);
1574 m_bindingsByLocation.insert(x: { binding.sourceLocation().offset(), binding });
1575 if (binding.bindingType() != QQmlSA::BindingType::Script)
1576 analyzeBinding(element, value: QQmlSA::Element(), location: binding.sourceLocation());
1577 }
1578}
1579
1580/*!
1581 Runs the element passes over \a root and all its children.
1582 */
1583void PassManager::analyze(const Element &root)
1584{
1585 Q_D(PassManager);
1586 d->analyze(root);
1587}
1588
1589static QQmlJS::ChildScopesIterator childScopesBegin(const Element &element)
1590{
1591 return QQmlJSScope::scope(element)->childScopesBegin();
1592}
1593
1594static QQmlJS::ChildScopesIterator childScopesEnd(const Element &element)
1595{
1596 return QQmlJSScope::scope(element)->childScopesEnd();
1597}
1598
1599void PassManagerPrivate::analyze(const Element &root)
1600{
1601 QList<Element> runStack;
1602 runStack.push_back(t: root);
1603 while (!runStack.isEmpty()) {
1604 auto element = runStack.takeLast();
1605 addBindingSourceLocations(element);
1606 for (auto &elementPass : m_elementPasses)
1607 if (elementPass->shouldRun(element))
1608 elementPass->run(element);
1609
1610 for (auto it = childScopesBegin(element), end = childScopesEnd(element); it != end; ++it) {
1611 if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope)
1612 runStack.push_back(t: QQmlJSScope::createQQmlSAElement(*it));
1613 }
1614 }
1615}
1616
1617void PassManagerPrivate::analyzeWrite(const Element &element, const QString &propertyName,
1618 const Element &value, const Element &writeScope,
1619 const QQmlSA::SourceLocation &location)
1620{
1621 const auto &passes = findPropertyUsePasses(element, propertyName);
1622 for (PropertyPass *pass : passes)
1623 pass->onWrite(element, propertyName, value, writeScope, location);
1624}
1625
1626void PassManagerPrivate::analyzeRead(const Element &element, const QString &propertyName,
1627 const Element &readScope, const QQmlSA::SourceLocation &location)
1628{
1629 const auto &passes = findPropertyUsePasses(element, propertyName);
1630 for (PropertyPass *pass : passes)
1631 pass->onRead(element, propertyName, readScope, location);
1632}
1633
1634void PassManagerPrivate::analyzeCall(const Element &element, const QString &propertyName,
1635 const Element &readScope, const QQmlSA::SourceLocation &location)
1636{
1637 const auto &passes = findPropertyUsePasses(element, propertyName);
1638 for (PropertyPass *pass : passes)
1639 pass->onCall(element, propertyName, readScope, location);
1640}
1641
1642void PassManagerPrivate::analyzeBinding(const Element &element, const QQmlSA::Element &value,
1643 const QQmlSA::SourceLocation &location)
1644{
1645 const auto it = m_bindingsByLocation.find(x: location.offset());
1646
1647 // If there's no matching binding that means we're in a nested Ret somewhere inside an
1648 // expression
1649 if (it == m_bindingsByLocation.cend())
1650 return;
1651
1652 const auto &[offset, binding] = *it;
1653 const QQmlSA::Element &bindingScope = binding.bindingScope();
1654 const QString &propertyName = binding.propertyName();
1655
1656 const auto elementPasses = findPropertyUsePasses(element, propertyName);
1657 for (PropertyPass *pass : elementPasses)
1658 pass->onBinding(element, propertyName, binding, bindingScope, value);
1659
1660 if (!binding.isAttached() || bindingScope.baseType().isNull())
1661 return;
1662
1663 const auto bindingScopePasses = findPropertyUsePasses(element: bindingScope.baseType(), propertyName);
1664 for (PropertyPass *pass : bindingScopePasses) {
1665 if (!elementPasses.contains(value: pass))
1666 pass->onBinding(element, propertyName, binding, bindingScope, value);
1667 }
1668}
1669
1670/*!
1671 Returns \c true if the module named \a module has been imported by the
1672 QML to be analyzed, \c false otherwise.
1673
1674 This can be used to skip registering a pass which is specific to a specific
1675 module.
1676
1677 \code
1678 if (passManager->hasImportedModule("QtPositioning"))
1679 passManager->registerElementPass(
1680 std::make_unique<PositioningPass>(passManager)
1681 );
1682 \endcode
1683
1684 \sa registerPropertyPass(), registerElementPass()
1685 */
1686bool PassManager::hasImportedModule(QAnyStringView module) const
1687{
1688 return PassManagerPrivate::visitor(*this)->imports().hasType(name: u"$module$." + module.toString());
1689}
1690
1691/*!
1692 Returns \c true if warnings of \a category are enabled, \c false otherwise.
1693 */
1694bool PassManager::isCategoryEnabled(LoggerWarningId category) const
1695{
1696 return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(id: category);
1697}
1698
1699QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager)
1700{
1701 return manager.d_func()->m_visitor;
1702}
1703
1704QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager)
1705{
1706 return manager.d_func()->m_typeResolver;
1707}
1708
1709QSet<PropertyPass *> PassManagerPrivate::findPropertyUsePasses(const QQmlSA::Element &element,
1710 const QString &propertyName)
1711{
1712 QStringList typeNames;
1713
1714 for (auto it = QQmlJSScope::scope(element); it; it = it->baseType())
1715 typeNames.append(t: lookupName(element: QQmlJSScope::createQQmlSAElement(it)));
1716
1717 QSet<PropertyPass *> passes;
1718
1719 for (const QString &typeName : typeNames) {
1720 for (auto &pass :
1721 { m_propertyPasses.equal_range(x: u""_s), m_propertyPasses.equal_range(x: typeName) }) {
1722 if (pass.first == pass.second)
1723 continue;
1724
1725 for (auto it = pass.first; it != pass.second; it++) {
1726 if (typeName != typeNames.constFirst() && !it->second.allowInheritance)
1727 continue;
1728 if (it->second.property.isEmpty() || it->second.property == propertyName)
1729 passes.insert(value: it->second.pass.get());
1730 }
1731 }
1732 }
1733 return passes;
1734}
1735
1736void DebugElementPass::run(const Element &element) {
1737 emitWarning(diagnostic: u"Type: " + element.baseTypeName(), id: qmlPlugin);
1738 if (auto bindings = element.propertyBindings(propertyName: u"objectName"_s); !bindings.isEmpty()) {
1739 emitWarning(diagnostic: u"is named: " + bindings.first().stringValue(), id: qmlPlugin);
1740 }
1741 if (auto defPropName = element.defaultPropertyName(); !defPropName.isEmpty()) {
1742 emitWarning(diagnostic: u"binding " + QString::number(element.propertyBindings(propertyName: defPropName).size())
1743 + u" elements to property "_s + defPropName,
1744 id: qmlPlugin);
1745 }
1746}
1747
1748/*!
1749 \class QQmlSA::LintPlugin
1750 \inmodule QtQmlCompiler
1751
1752 \brief Base class for all static analysis plugins.
1753 */
1754
1755/*!
1756 \fn LintPlugin::LintPlugin()
1757 Constructs a LintPlugin object.
1758 */
1759
1760/*!
1761 \fn virtual LintPlugin::~LintPlugin()
1762 Destroys the LintPlugin instance.
1763 */
1764
1765/*!
1766 \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement)
1767
1768 Adds a pass \a manager that will be executed on \a rootElement.
1769 */
1770
1771/*!
1772 \class QQmlSA::ElementPass
1773 \inmodule QtQmlCompiler
1774
1775 \brief Base class for all static analysis passes on elements.
1776
1777 ElementPass is the simpler of the two analysis passes. It will consider every element in
1778 a file. The \l shouldRun() method can be used to filter out irrelevant elements, and the
1779 \l run() method is doing the initial work.
1780
1781 Common tasks suitable for an ElementPass are
1782 \list
1783 \li checking that properties of an Element are not combined in a nonsensical way
1784 \li validating property values (e.g. that a property takes only certain enum values)
1785 \li checking behavior dependent on an Element's parent (e.g. not using \l {Item::width}
1786 when the parent element is a \c Layout).
1787 \endlist
1788
1789 As shown in the snippet below, it is recommended to do necessary type resolution in the
1790 constructor of the ElementPass and cache it in local members, and to implement some
1791 filtering via \l shouldRun() to keep the static analysis performant.
1792
1793 \code
1794 using namespace QQmlSA;
1795 class MyElementPass : public ElementPass
1796 {
1797 Element myType;
1798 public:
1799 MyElementPass(QQmlSA::PassManager *manager)
1800 : myType(resolveType("MyModule", "MyType")) {}
1801
1802 bool shouldRun(const Element &element) override
1803 {
1804 return element.inherits(myType);
1805 }
1806 void run(const Element &element) override
1807 {
1808 // actual pass logic
1809 }
1810 }
1811 \endcode
1812
1813 ElementPasses have limited insight into how an element's properties are used. If you need
1814 that information, consider using a \l PropertyPass instead.
1815
1816 \note ElementPass will only ever consider instantiable types. Therefore, it is unsuitable
1817 to analyze attached types and singletons. Those need to be handled via a PropertyPass.
1818 */
1819
1820/*!
1821 \fn ElementPass::ElementPass(PassManager *manager)
1822 Creates an ElementPass object and uses \a manager to refer to the pass manager.
1823*/
1824
1825/*!
1826 \fn void QQmlSA::ElementPass::run(const Element &element)
1827
1828 Executes if \c shouldRun() returns \c true. Performs the real computation
1829 of the pass on \a element.
1830 This method is meant to be overridden. Calling the base method is not
1831 necessary.
1832 */
1833
1834/*!
1835 Controls whether the \c run() function should be executed on the given \a element.
1836 Subclasses can override this method to improve performance of the analysis by
1837 filtering out elements which are not relevant.
1838
1839 The default implementation unconditionally returns \c true.
1840 */
1841bool ElementPass::shouldRun(const Element &element)
1842{
1843 (void)element;
1844 return true;
1845}
1846
1847/*!
1848 \class QQmlSA::PropertyPass
1849 \inmodule QtQmlCompiler
1850
1851 \brief Base class for all static analysis passes on properties.
1852 */
1853
1854
1855/*!
1856 Creates a PropertyPass object and uses \a manager to refer to the pass manager.
1857 */
1858PropertyPass::PropertyPass(PassManager *manager) : GenericPass(manager) { }
1859
1860/*!
1861 Executes whenever a property gets bound to a value.
1862
1863 The property \a propertyName of \a element is bound to the \a value within
1864 \a bindingScope with \a binding.
1865 */
1866void PropertyPass::onBinding(const Element &element, const QString &propertyName,
1867 const QQmlSA::Binding &binding, const Element &bindingScope,
1868 const Element &value)
1869{
1870 Q_UNUSED(element);
1871 Q_UNUSED(propertyName);
1872 Q_UNUSED(binding);
1873 Q_UNUSED(bindingScope);
1874 Q_UNUSED(value);
1875}
1876
1877/*!
1878 Executes whenever a property is read.
1879
1880 The property \a propertyName of \a element is read by an instruction within
1881 \a readScope defined at \a location.
1882
1883 This is also executed if the property \a propertyName is called as a function as that requires
1884 the property to be read first.
1885 */
1886void PropertyPass::onRead(const Element &element, const QString &propertyName,
1887 const Element &readScope, QQmlSA::SourceLocation location)
1888{
1889 Q_UNUSED(element);
1890 Q_UNUSED(propertyName);
1891 Q_UNUSED(readScope);
1892 Q_UNUSED(location);
1893}
1894
1895/*!
1896 Executes whenever a property or method is called.
1897
1898 The property or method \a propertyName of \a element is called as a function by an instruction
1899 within \a readScope defined at \a location.
1900
1901 \note Currently only direct calls of methods or properties are supported, indirect calls, for
1902 example by storing a method into a JavaScript variable and then calling the variable, are not
1903 recognized.
1904 */
1905void PropertyPass::onCall(const Element &element, const QString &propertyName,
1906 const Element &readScope, QQmlSA::SourceLocation location)
1907{
1908 Q_UNUSED(element);
1909 Q_UNUSED(propertyName);
1910 Q_UNUSED(readScope);
1911 Q_UNUSED(location);
1912}
1913
1914/*!
1915 Executes whenever a property is written to.
1916
1917 The property \a propertyName of \a element is written to by an instruction
1918 within \a writeScope defined at \a location. The type of the expression
1919 written to \a propertyName is \a expressionType.
1920 */
1921void PropertyPass::onWrite(const Element &element, const QString &propertyName,
1922 const Element &expressionType, const Element &writeScope,
1923 QQmlSA::SourceLocation location)
1924{
1925 Q_UNUSED(element);
1926 Q_UNUSED(propertyName);
1927 Q_UNUSED(writeScope);
1928 Q_UNUSED(expressionType);
1929 Q_UNUSED(location);
1930}
1931
1932DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager)
1933{
1934}
1935
1936void DebugPropertyPass::onRead(const QQmlSA::Element &element, const QString &propertyName,
1937 const QQmlSA::Element &readScope, QQmlSA::SourceLocation location)
1938{
1939 emitWarning(diagnostic: u"onRead "_s
1940 + (QQmlJSScope::scope(element)->internalName().isEmpty()
1941 ? element.baseTypeName()
1942 : QQmlJSScope::scope(element)->internalName())
1943 + u' ' + propertyName + u' ' + QQmlJSScope::scope(readScope)->internalName()
1944 + u' ' + QString::number(location.startLine()) + u':'
1945 + QString::number(location.startColumn()),
1946 id: qmlPlugin, srcLocation: location);
1947}
1948
1949void DebugPropertyPass::onBinding(const QQmlSA::Element &element, const QString &propertyName,
1950 const QQmlSA::Binding &binding,
1951 const QQmlSA::Element &bindingScope, const QQmlSA::Element &value)
1952{
1953 const auto location = QQmlSA::SourceLocation{ binding.sourceLocation() };
1954 emitWarning(diagnostic: u"onBinding element: '"_s
1955 + (QQmlJSScope::scope(element)->internalName().isEmpty()
1956 ? element.baseTypeName()
1957 : QQmlJSScope::scope(element)->internalName())
1958 + u"' property: '"_s + propertyName + u"' value: '"_s
1959 + (value.isNull() ? u"NULL"_s
1960 : (QQmlJSScope::scope(value)->internalName().isNull()
1961 ? value.baseTypeName()
1962 : QQmlJSScope::scope(value)->internalName()))
1963 + u"' binding_scope: '"_s
1964 + (QQmlJSScope::scope(bindingScope)->internalName().isEmpty()
1965 ? bindingScope.baseTypeName()
1966 : QQmlJSScope::scope(bindingScope)->internalName())
1967 + u"' "_s + QString::number(location.startLine()) + u':'
1968 + QString::number(location.startColumn()),
1969 id: qmlPlugin, srcLocation: location);
1970}
1971
1972void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &propertyName,
1973 const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
1974 QQmlSA::SourceLocation location)
1975{
1976 emitWarning(diagnostic: u"onWrite "_s + element.baseTypeName() + u' ' + propertyName + u' '
1977 + QQmlJSScope::scope(value)->internalName() + u' '
1978 + QQmlJSScope::scope(writeScope)->internalName() + u' '
1979 + QString::number(location.startLine()) + u':'
1980 + QString::number(location.startColumn()),
1981 id: qmlPlugin, srcLocation: location);
1982}
1983
1984/*!
1985 Returns bindings by their source location.
1986 */
1987std::unordered_map<quint32, Binding> PassManager::bindingsByLocation() const
1988{
1989 Q_D(const PassManager);
1990 return d->m_bindingsByLocation;
1991}
1992
1993FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { }
1994
1995FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, const QString &fixDescription,
1996 const QQmlSA::SourceLocation &location,
1997 const QString &replacement)
1998 : m_fixSuggestion{ fixDescription, QQmlSA::SourceLocationPrivate::sourceLocation(sourceLocation: location),
1999 replacement },
2000 q_ptr{ interface }
2001{
2002}
2003
2004FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface,
2005 const FixSuggestionPrivate &other)
2006 : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface }
2007{
2008}
2009
2010FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other)
2011 : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface }
2012{
2013}
2014
2015QString FixSuggestionPrivate::fixDescription() const
2016{
2017 return m_fixSuggestion.fixDescription();
2018}
2019
2020QQmlSA::SourceLocation FixSuggestionPrivate::location() const
2021{
2022 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: m_fixSuggestion.location());
2023}
2024
2025QString FixSuggestionPrivate::replacement() const
2026{
2027 return m_fixSuggestion.replacement();
2028}
2029
2030void FixSuggestionPrivate::setFileName(const QString &fileName)
2031{
2032 m_fixSuggestion.setFilename(fileName);
2033}
2034
2035QString FixSuggestionPrivate::fileName() const
2036{
2037 return m_fixSuggestion.filename();
2038}
2039
2040void FixSuggestionPrivate::setHint(const QString &hint)
2041{
2042 m_fixSuggestion.setHint(hint);
2043}
2044
2045QString FixSuggestionPrivate::hint() const
2046{
2047 return m_fixSuggestion.hint();
2048}
2049
2050void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable)
2051{
2052 m_fixSuggestion.setAutoApplicable(autoApplicable);
2053}
2054
2055bool FixSuggestionPrivate::isAutoApplicable() const
2056{
2057 return m_fixSuggestion.isAutoApplicable();
2058}
2059
2060QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion)
2061{
2062 return saFixSuggestion.d_func()->m_fixSuggestion;
2063}
2064
2065const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion)
2066{
2067 return saFixSuggestion.d_func()->m_fixSuggestion;
2068}
2069
2070/*!
2071 \class QQmlSA::FixSuggestion
2072 \inmodule QtQmlCompiler
2073
2074 \brief Represents a suggested fix for an issue in the source code.
2075 */
2076
2077
2078/*!
2079 Creates a FixSuggestion object.
2080 */
2081FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location,
2082 const QString &replacement)
2083 : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } }
2084{
2085}
2086
2087/*!
2088 Creates a copy of \a other.
2089 */
2090FixSuggestion::FixSuggestion(const FixSuggestion &other)
2091 : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } }
2092{
2093}
2094
2095/*!
2096 Move-constructs a FixSuggestion instance.
2097 */
2098FixSuggestion::FixSuggestion(FixSuggestion &&other) noexcept
2099 : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } }
2100{
2101}
2102
2103/*!
2104 Assigns \a other to this FixSuggestion instance.
2105 */
2106FixSuggestion &FixSuggestion::operator=(const FixSuggestion &other)
2107{
2108 if (*this == other)
2109 return *this;
2110
2111 d_func()->m_fixSuggestion = other.d_func()->m_fixSuggestion;
2112 return *this;
2113}
2114
2115/*!
2116 Move-assigns \a other to this FixSuggestion instance.
2117 */
2118FixSuggestion &FixSuggestion::operator=(FixSuggestion &&other) noexcept
2119{
2120 if (*this == other)
2121 return *this;
2122
2123 d_func()->m_fixSuggestion = std::move(other.d_func()->m_fixSuggestion);
2124 return *this;
2125}
2126
2127/*!
2128 Destorys the FixSuggestion instance.
2129 */
2130FixSuggestion::~FixSuggestion() = default;
2131
2132/*!
2133 Returns the description of the fix.
2134 */
2135QString QQmlSA::FixSuggestion::fixDescription() const
2136{
2137 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).fixDescription();
2138}
2139
2140/*!
2141 Returns the location where the fix would be applied.
2142 */
2143QQmlSA::SourceLocation FixSuggestion::location() const
2144{
2145 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
2146 jsLocation: FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).location());
2147}
2148
2149/*!
2150 Returns the fix that will replace the problematic source code.
2151 */
2152QString FixSuggestion::replacement() const
2153{
2154 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).replacement();
2155}
2156
2157/*!
2158 Sets \a fileName as the name of the file where this fix suggestion applies.
2159 */
2160void FixSuggestion::setFileName(const QString &fileName)
2161{
2162 FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setFilename(fileName);
2163}
2164
2165/*!
2166 Returns the name of the file where this fix suggestion applies.
2167 */
2168QString FixSuggestion::fileName() const
2169{
2170 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).filename();
2171}
2172
2173/*!
2174 Sets \a hint as the hint for this fix suggestion.
2175 */
2176void FixSuggestion::setHint(const QString &hint)
2177{
2178 FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setHint(hint);
2179}
2180
2181/*!
2182 Returns the hint for this fix suggestion.
2183 */
2184QString FixSuggestion::hint() const
2185{
2186 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).hint();
2187}
2188
2189/*!
2190 Sets \a autoApplicable to determine whether this suggested fix can be
2191 applied automatically.
2192 */
2193void FixSuggestion::setAutoApplicable(bool autoApplicable)
2194{
2195 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion&: *this).setAutoApplicable(autoApplicable);
2196}
2197
2198/*!
2199 Returns whether this suggested fix can be applied automatically.
2200 */
2201bool QQmlSA::FixSuggestion::isAutoApplicable() const
2202{
2203 return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).isAutoApplicable();
2204}
2205
2206/*!
2207 \fn friend bool FixSuggestion::operator==(const FixSuggestion &lhs, const FixSuggestion &rhs)
2208 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
2209 */
2210/*!
2211 \fn friend bool FixSuggestion::operator!=(const FixSuggestion &lhs, const FixSuggestion &rhs)
2212 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
2213 */
2214
2215bool FixSuggestion::operatorEqualsImpl(const FixSuggestion &lhs, const FixSuggestion &rhs)
2216{
2217 return lhs.d_func()->m_fixSuggestion == rhs.d_func()->m_fixSuggestion;
2218}
2219
2220void emitWarningWithOptionalFix(GenericPass &pass, QAnyStringView diagnostic,
2221 const LoggerWarningId &id,
2222 const QQmlSA::SourceLocation &srcLocation,
2223 const std::optional<QQmlJSFixSuggestion> &fix)
2224{
2225 if (!fix.has_value()) {
2226 pass.emitWarning(diagnostic, id, srcLocation);
2227 return;
2228 }
2229
2230 const QQmlSA::SourceLocation location =
2231 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: fix->location());
2232 const QQmlSA::FixSuggestion saFix{ fix->fixDescription(), location, fix->replacement() };
2233 pass.emitWarning(diagnostic, id, srcLocation, fix: saFix);
2234}
2235
2236bool isRegularBindingType(BindingType type)
2237{
2238 return type >= BindingType::BoolLiteral && type <= BindingType::Object;
2239}
2240
2241} // namespace QQmlSA
2242
2243QT_END_NAMESPACE
2244

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