Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlsa.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
5#include "qqmlsa.h"
6#include "qqmlsa_p.h"
8
10#include "qqmljslogger_p.h"
13#include "qqmljsutils_p.h"
15
16#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
17
18#include <memory>
19
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 \enum QQmlSA::MethodType
37 \inmodule QtQmlCompiler
38
39 \brief Describes the type of a \l{QQmlSA::Method}.
40 \value Signal The method is a signal
41 \value Slot The method is a slot
42 \value Method The method is a \l{Q_INVOKABLE} method
43 \value StaticMethod The method is a \l{Q_INVOKABLE} static method
44*/
45
46/*!
47 \enum QQmlSA::AccessSemantics
48 \inmodule QtQmlCompiler
49
50 \brief Describes how a type is accessed and shared.
51 \value Reference The type behaves like an \l{QML Object Types}{Object type}
52 \value Value The type behaves like a \l{QML Value Types}{Value type}
53 \value None The type is a \l{QML Namespaces}{namespace}, or is invalid
54 \value Sequence The type behaves like a \l{QML Sequence Types}{Sequence type}
55
56 \sa {The QML Type System}
57*/
58
59/*!
60 \enum QQmlSA::BindingType
61 \inmodule QtQmlCompiler
62
63 \brief Describes the type of a \l{QQmlSA::Binding}.
64 \value Invalid There is no binding
65 \value BoolLiteral The binding is a bool literal
66 \value NumberLiteral The binding is a number literal
67 \value StringLiteral The binding is a string literal
68 \value RegExpLiteral The binding is a regular expression literal
69 \value Null The binding is a null literal
70 \value Translation The binding is a \l{Text ID based translations}{translation}
71 \value TranslationById The binding is a \l{Text ID based translations}{translation} by id
72 \value Script The binding is a regular script
73 \value Object The binging is an \l{QML Object Types}{Object}
74 \value Interceptor The binding is an interceptor that can intercept writes to properties such as \l[Quick]{Behavior}
75 \value ValueSource The binging is a \l{Defining QML Types from C++#Property Value Sources}{property value source}
76 \value AttachedProperty The binding is an \l{QML Object Attributes#Attached Properties and Attached Signal Handlers}{attached object}
77 \value GroupProperty The binding is a \l{QML Object Attributes#Grouped Properties}{grouped property}
78*/
79
80/*!
81 \enum QQmlSA::ScriptBindingKind
82 \inmodule QtQmlCompiler
83
84 \brief Describes the script type of a \l{QQmlSA::Binding} of type \l{Script}.
85 \value Invalid The binding has an invalid script
86 \value PropertyBinding The binding is bound to a property
87 \value SignalHandler The binding is a \l{Signal and Handler Event System#Receiving signals with signal handlers}{signal handler}
88 \value ChangeHandler The binding is a \l{Signal and Handler Event System#Property change signal handlers}{change handler}
89*/
90
91/*!
92 \enum QQmlSA::ScopeType
93 \brief Describes the type of QML scope.
94 \value JSFunctionScope The scope is a JavaScript function:
95 \badcode
96 Item {
97 function f() : int { <- begin
98 return 1
99 } <- end
100 }
101 \endcode
102 \value JSLexicalScope The scope is a JavaScript lexical scope:
103 \badcode
104 property int i: { <- begin
105 let a = 1
106 { <- begin
107 console.log("hello")
108 } <- end
109 return a
110 } <- end
111 \endcode
112 \value QMLScope The scope is a QML Object:
113 \badcode
114 Item { <- begin
115 x: 50
116 } <- end
117 \endcode
118 \value GroupedPropertyScope The scope is a \l{QML Object Attributes#Grouped Properties}{grouped property}:
119 \badcode
120 Text {
121 font { <- begin
122 pixelSize: 12
123 bold: true
124 } <- end
125 }
126 \endcode
127 \value AttachedPropertyScope The scope is an \l{QML Object Attributes#Attached Properties and Attached Signal Handlers}{attached property}:
128 \badcode
129 Item {
130 Component.onCompleted: console.log("Hello")
131 ^^^^^^^^^
132 \ Scope of attached property Component
133 }
134 \endcode
135 \value EnumScope The scope is a QML \l{QML Enumerations}{enum}:
136 \badcode
137 enum E { <- begin
138 A,
139 B,
140 C
141 } <- end
142 \endcode
143 \value [since 6.11] BindingFunctionScope The scope represents the expression of a binding:
144 \badcode
145 Item {
146 width: <- begin 10 +
147 2 *parent.width <- end
148 }
149 \endcode
150 Before Qt 6.11, this was reported as a JSFunctionScope
151 \value [since 6.11] SignalHandlerFunctionScope The scope represents a function used as a signal handler:
152 \badcode
153 Timer {
154 onTriggered: <- begin console.log("hi") <- end
155 }
156 \endcode
157 Before Qt 6.11, this was reported as a JSFunctionScope
158
159 Each entry is shown with an example scope of the matching type in QML code.
160*/
161
162/*!
163 \class QQmlSA::Binding::Bindings
164 \inmodule QtQmlCompiler
165
166 \brief Holds multiple property name to property binding associations.
167 */
168
169/*!
170 Constructs a new Bindings object.
171 */
173
174BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *iface) : q_ptr{ iface } { }
175
176/*!
177 Creates a copy of \a other.
178 */
180 : d_ptr{ new BindingsPrivate{ this, *other.d_func() } }
181{
182}
183
184/*!
185 Destroys the Bindings object.
186 */
187Binding::Bindings::~Bindings() = default;
188
189BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *iface, const BindingsPrivate &other)
190 : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ iface }
191{
192}
193
194BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *iface, BindingsPrivate &&other)
195 : m_bindings{ std::move(other.m_bindings) }, q_ptr{ iface }
196{
197}
198
199/*!
200 Returns an iterator to the beginning of the bindings.
201 */
203{
204 Q_D(const Bindings);
205 return d->constBegin();
206}
207
208/*!
209 \fn QMultiHash<QString, Binding>::const_iterator Binding::Bindings::begin() const
210 Same as constBegin().
211 */
212
214{
215 return m_bindings.constBegin();
216}
217
218/*!
219 Returns an iterator to the end of the bindings.
220 */
222{
223 Q_D(const Bindings);
224 return d->constEnd();
225}
226
227/*!
228 \fn QMultiHash<QString, Binding>::const_iterator Binding::Bindings::end() const
229 Same as constEnd().
230 */
231
233{
234 return m_bindings.constEnd();
235}
236
237/*!
238 \class QQmlSA::Binding
239 \inmodule QtQmlCompiler
240
241 \brief Represents a single QML property binding for a specific type.
242 */
243
244/*!
245 Constructs a new Binding object.
246 */
247Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { }
248
249BindingPrivate::BindingPrivate(Binding *iface) : q_ptr{ iface } { }
250
251/*!
252 Creates a copy of \a other.
253*/
254Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
255
256/*!
257 Move-constructs a \c Binding instance.
258*/
260 : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
261
262
263/*!
264 Assigns \a other to this Binding instance.
265*/
267{
268 if (*this == other)
269 return *this;
270
271 Q_D(Binding);
274 d->q_ptr = this;
276 return *this;
277}
278
279/*!
280 Move-assigns \a other to this Binding instance.
281*/
283{
284 if (*this == other)
285 return *this;
286
287 Q_D(Binding);
290 d->q_ptr = this;
292 return *this;
293}
294
295/*!
296 Destroys the binding.
297*/
298Binding::~Binding() = default;
299
300bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs)
301{
302 return lhs.d_func()->m_binding == rhs.d_func()->m_binding
305}
306
307BindingPrivate::BindingPrivate(Binding *iface, const BindingPrivate &other)
309 m_isAttached{ other.m_isAttached }
310{
311}
312
313QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding)
314{
315 QQmlSA::Binding saBinding;
316 saBinding.d_func()->m_binding = binding;
317 return saBinding;
318}
319
320QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding)
321{
322 return binding.d_func()->m_binding;
323}
324
325const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding)
326{
327 return binding.d_func()->m_binding;
328}
329
330/*!
331 Returns the type of the property of this binding if it is a group property,
332 otherwise returns an invalid Element.
333 */
338
339/*!
340 Returns the Element scope in which the binding is defined.
341 */
343{
344 return BindingPrivate::get(this)->m_bindingScope;
345}
346
347/*!
348 Returns the type of this binding.
349 */
354
355/*!
356 Returns the associated string literal if the content type of this binding is
357 StringLiteral, otherwise returns an empty string.
358 */
360{
361 return BindingPrivate::binding(*this).stringValue();
362}
363
364/*!
365 Returns the name of the property bound with this binding.
366 */
368{
369 return BindingPrivate::binding(*this).propertyName();
370}
371
372/*!
373 Returns \c true if this type is attached to another one, \c false otherwise.
374 */
375bool Binding::isAttached() const
376{
377 return BindingPrivate::get(this)->m_isAttached;
378}
379
380/*!
381 Returns the attached type if the content type of this binding is
382 AttachedProperty, otherwise returns an invalid Element.
383 */
388
389#if QT_DEPRECATED_SINCE(6, 9)
390/*!
391 Returns the attached type if the content type of this binding is
392 AttachedProperty, otherwise returns an invalid Element.
393
394 \deprecated [6.9] Use the better named attachedType() method.
395 */
397{
398 return attachedType();
399}
400#endif
401
402/*!
403 Returns the location in the QML code where this binding is defined.
404 */
410
411/*!
412 Returns the associated number if the content type of this binding is
413 NumberLiteral, otherwise returns 0.
414 */
415double Binding::numberValue() const
416{
417 return BindingPrivate::binding(*this).numberValue();
418}
419
420/*!
421 Returns the kind of the associated script if the content type of this
422 binding is Script, otherwise returns Invalid.
423 */
428
429/*!
430 Returns \c true if this binding has an objects, otherwise returns \c false.
431 */
432bool Binding::hasObject() const
433{
434 return BindingPrivate::binding(*this).hasObject();
435}
436
437/*!
438 Returns the type of the associated object if the content type of this
439 binding is Object, otherwise returns an invalid Element.
440 */
445
446/*!
447 Returns whether this binding has script value type undefined like when it
448 is assigned \c undefined. If the content type of this binding is not
449 \l{QQmlSA::BindingType::Script}, returns \c false.
450 */
457
458/*!
459 Returns whether this binding has script value type function like when it
460 is assigned a (lambda) method, an arrow function or a statement block. If
461 the content type of this binding is not \l{QQmlSA::BindingType::Script},
462 returns \c false.
463 */
470
471/*!
472 Returns \c true if \a bindingType is a literal type, and \c false
473 otherwise.
474 */
479
480/*!
481 \fn friend bool Binding::operator==(const Binding &lhs, const Binding &rhs)
482 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise. Two
483 \c Bindings are considered equal if their property name, content type, and
484 source location match.
485 */
486/*!
487 \fn friend bool Binding::operator!=(const Binding &lhs, const Binding &rhs)
488 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
489 Two \c Bindings are considered equal if their property name, content type,
490 and source location match.
491 */
492
493/*!
494 Constructs a new Methods object.
495*/
497
498/*!
499 Creates a copy of \a other.
500 */
502 : d_ptr{ new MethodsPrivate{ this, *other.d_func() } }
503{
504}
505
506/*!
507 Destroys the Methods instance.
508 */
509QQmlSA::Method::Methods::~Methods() = default;
510
511/*!
512 Returns an iterator to the beginning of the methods.
513 */
515{
516 Q_D(const Methods);
517 return d->constBegin();
518}
519
520/*!
521 \fn QMultiHash<QString, QQmlSA::Method>::const_iterator QQmlSA::Method::Methods::begin() const
522 Returns an iterator to the beginning of the methods.
523 */
524
526{
527 return m_methods.constBegin();
528}
529
530/*!
531 Returns an iterator to the end of the methods.
532 */
534{
535 Q_D(const Methods);
536 return d->constEnd();
537}
538
539/*!
540 \fn QMultiHash<QString, QQmlSA::Method>::const_iterator QQmlSA::Method::Methods::end() const
541 Returns an iterator to the end of the methods.
542 */
543
545{
546 return m_methods.constEnd();
547}
548
549MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *iface) : q_ptr{ iface } { }
550
551MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *iface, const MethodsPrivate &other)
553{
554}
555
556MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *iface, MethodsPrivate &&other)
558{
559}
560
561MethodPrivate::MethodPrivate(Method *iface) : q_ptr{ iface } { }
562
563MethodPrivate::MethodPrivate(Method *iface, const MethodPrivate &other)
565{
566}
567
569{
570 return m_method.methodName();
571}
572
573QQmlSA::SourceLocation MethodPrivate::sourceLocation() const
574{
575 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_method.sourceLocation());
576}
577
579{
580 return m_method.methodType();
581}
582
583/*!
584 \class QQmlSA::Method
585 \inmodule QtQmlCompiler
586
587 \brief Represents a QML method.
588 */
589
590/*!
591 Constructs a new Method object.
592 */
593Method::Method() : d_ptr{ new MethodPrivate{ this } } { }
594
595/*!
596 Creates a copy of \a other.
597 */
598Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { }
599
600/*!
601 Move-constructs a Method instance.
602 */
604 : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } }
605{
606}
607
608/*!
609 Assigns \a other to this Method instance.
610 */
612{
613 if (*this == other)
614 return *this;
615
617 d_func()->q_ptr = this;
618 return *this;
619}
620
621/*!
622 Move-assigns \a other to this Method instance.
623 */
625{
626 if (*this == other)
627 return *this;
628
630 d_func()->q_ptr = this;
631 return *this;
632}
633
634/*!
635 Destroys the Method.
636 */
637Method::~Method() = default;
638
639/*!
640 Returns the name of the this method.
641 */
643{
644 Q_D(const Method);
645 return d->methodName();
646}
647
648/*!
649 Returns the type of this method.
650 */
652{
653 Q_D(const Method);
654 return d->methodType();
655}
656
657/*!
658 \fn friend bool Method::operator==(const Method &lhs, const Method &rhs)
659 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
660 */
661/*!
662 \fn friend bool Method::operator!=(const Method &lhs, const Method &rhs)
663 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
664 */
665
666/*!
667 Returns the location in the QML code where this method is defined.
668 */
670{
671 Q_D(const Method);
672 return d->sourceLocation();
673}
674
675bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs)
676{
677 return lhs.d_func()->m_method == rhs.d_func()->m_method;
678}
679
680QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod)
681{
682 QQmlSA::Method saMethod;
683 auto &wrappedMethod = saMethod.d_func()->m_method;
684 wrappedMethod = jsMethod;
685 return saMethod;
686}
687
688QQmlSA::Method::Methods
689MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash)
690{
691 QMultiHash<QString, QQmlSA::Method> saMethods;
692 for (const auto &[key, value] : hash.asKeyValueRange()) {
693 saMethods.insert(key, MethodPrivate::createMethod(value));
694 }
695
696 QQmlSA::Method::Methods methods;
697 methods.d_func()->m_methods = std::move(saMethods);
698 return methods;
699}
700
701QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method)
702{
703 return method.d_func()->m_method;
704}
705
706/*!
707 \class QQmlSA::PropertyPrivate
708 \inmodule QtQmlCompiler
709 \internal
710*/
712
717
722
724{
725 return m_property.typeName();
726}
727
729{
730 return m_property.isValid();
731}
732
733/*!
734 Returns whether this property is readonly. Properties defined in QML are readonly when their
735 definition has the 'readonly' keyword. Properties defined in C++ are readonly when they do not
736 have a WRITE accessor function.
737 */
739{
740 return !m_property.isWritable();
741}
742
743/*!
744 Returns the type that this property was defined with.
745 */
750
755
763
764/*!
765 \class QQmlSA::Property
766 \inmodule QtQmlCompiler
767
768 \brief Represents a QML property.
769 */
770
771/*!
772 Constructs a new Property object.
773 */
774Property::Property() : d_ptr{ new PropertyPrivate{ this } } { }
775
776/*!
777 Creates a copy of \a other.
778 */
780 : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { }
781
782/*!
783 Move-constructs a Property instance.
784 */
786 : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } }
787{
788}
789
790/*!
791 Assigns \a other to this Property instance.
792 */
794{
795 if (*this == other)
796 return *this;
797
799 d_func()->q_ptr = this;
800 return *this;
801}
802
803/*!
804 Move-assigns \a other to this Property instance.
805 */
807{
808 if (*this == other)
809 return *this;
810
812 d_func()->q_ptr = this;
813 return *this;
814}
815
816/*!
817 Destroys this property.
818 */
819Property::~Property() = default;
820
821/*!
822 Returns the name of the type of this property.
823 */
825{
826 Q_D(const Property);
827 return d->typeName();
828}
829
830/*!
831 Returns \c true if this property is valid, \c false otherwise.
832 */
833bool Property::isValid() const
834{
835 Q_D(const Property);
836 return d->isValid();
837}
838
839/*!
840 Returns \c true if this property is read-only, \c false otherwise.
841 */
842bool Property::isReadonly() const
843{
844 Q_D(const Property);
845 return d->isReadonly();
846}
847
848/*!
849 Returns the type of this property.
850*/
852{
853 Q_D(const Property);
854 return d->type();
855}
856
857/*!
858 \fn friend bool Property::operator==(const Property &lhs, const Property &rhs)
859 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
860 */
861/*!
862 \fn friend bool Property::operator!=(const Property &lhs, const Property &rhs)
863 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
864 */
865
866
868{
869 return lhs.d_func()->m_property == rhs.d_func()->m_property;
870}
871
872/*!
873 \class QQmlSA::Element
874 \inmodule QtQmlCompiler
875
876 \brief Represents a QML type.
877 */
878
879/*!
880 Constructs a new Element object.
881 */
883{
884 new (m_data) QQmlJSScope::ConstPtr();
885}
886
887/*!
888 Creates a copy of \a other.
889 */
894
895/*!
896 \fn Element::Element(Element &&other) noexcept
897 Move-constructs an Element instance.
898 */
899
900/*!
901 Assigns \a other to this element instance.
902 */
904{
905 if (this == &other)
906 return *this;
907
908 *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other);
909 return *this;
910}
911
912/*!
913 \fn QQmlSA::Element &QQmlSA::Element::operator=(QQmlSA::Element &&other)
914 Move-assigns \a other to this Element instance.
915 */
916
917/*!
918 Destroys the element.
919 */
921{
922 (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr();
923}
924
925/*!
926 Returns the type of Element's scope.
927 */
929{
930 return QQmlJSScope::scope(*this)->scopeType();
931}
932
933/*!
934 Returns the Element this Element derives from.
935 */
940
941/*!
942 Returns the name of the Element this Element derives from.
943 */
948
949/*!
950 Returns the Element that encloses this Element.
951 */
956
957/*!
958 Returns whether this Element inherits from \a element.
959 */
960bool Element::inherits(const Element &element) const
961{
963}
964
965/*!
966 Returns whether this Element is the root component of its QML file.
967 */
969{
970 return QQmlJSScope::scope(*this)->isFileRootComponent();
971}
972
973/*!
974 Returns \c true if this element is null, \c false otherwise.
975 */
976bool Element::isNull() const
977{
978 return QQmlJSScope::scope(*this).isNull();
979}
980
981/*!
982 \internal
983 */
985{
986 return QQmlJSScope::scope(*this)->internalName();
987}
988
989/*!
990 Returns the access semantics of this Element. For example, Reference,
991 Value or Sequence.
992 */
994{
995 return QQmlJSScope::scope(*this)->accessSemantics();
996}
997
998/*!
999 Returns \c true for objects defined from Qml, and \c false for objects declared from C++.
1000 */
1002{
1003 return QQmlJSScope::scope(*this)->isComposite();
1004}
1005
1006/*!
1007 Returns whether this Element has a property with the name \a propertyName.
1008 */
1010{
1011 return QQmlJSScope::scope(*this)->hasProperty(propertyName);
1012}
1013
1014/*!
1015 Returns whether this Element defines a property with the name \a propertyName
1016 which is not defined on its base or extension objects.
1017 */
1019{
1021}
1022
1023/*!
1024 Returns the property with the name \a propertyName if it is found in this
1025 Element or its base and extension objects, otherwise returns an invalid property.
1026 */
1031
1032/*!
1033 Returns whether the property with the name \a propertyName resolved on this
1034 Element is required. Returns false if the the property couldn't be found.
1035 */
1040
1041/*!
1042 Returns the name of the default property of this Element. If it doesn't
1043 have one, returns an empty string.
1044 */
1046{
1047 return QQmlJSScope::scope(*this)->defaultPropertyName();
1048}
1049
1050/*!
1051 Returns whether this Element has a method with the name \a methodName.
1052 */
1054{
1055 return QQmlJSScope::scope(*this)->hasMethod(methodName);
1056}
1057
1058/*!
1059 \class QQmlSA::Method::Methods
1060 \inmodule QtQmlCompiler
1061
1062 \brief Holds multiple method name to method associations.
1063 */
1064
1065/*!
1066 Returns this Elements's methods, which are not defined on its base or
1067 extension objects.
1068 */
1073
1074/*!
1075 Returns the location in the QML code where this Element is defined.
1076 */
1082
1083/*!
1084 Returns the location in the QML code where this Element is assigned its id, if it has one.
1085 */
1091
1092/*!
1093 Returns the file path of the QML code that defines this Element.
1094 */
1096{
1097 return QQmlJSScope::scope(*this)->filePath();
1098}
1099
1100/*!
1101 Returns whether this Element has a property binding with the name \a name.
1102 */
1104{
1105 return QQmlJSScope::scope(*this)->hasPropertyBindings(name);
1106}
1107
1108/*!
1109 Returns whether this Element has property bindings which are not defined in
1110 its base or extension objects and that have name \a propertyName.
1111 */
1116
1117/*!
1118 Returns this Element's property bindings which are not defined on its base
1119 or extension objects.
1120 */
1125
1126/*!
1127 Returns this Element's property bindings which are not defined on its base
1128 or extension objects and that have the name \a propertyName.
1129 */
1135
1136/*!
1137 Returns this Element's property bindings that have the name \a propertyName.
1138 */
1140{
1141 const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(propertyName);
1142
1144 for (const auto &jsBinding : bindings) {
1146 }
1147 return saBindings;
1148}
1149
1150QQmlSA::Binding::Bindings
1151BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash)
1152{
1153 QMultiHash<QString, QQmlSA::Binding> saBindings;
1154 for (const auto &[key, value] : hash.asKeyValueRange()) {
1155 saBindings.insert(key, BindingPrivate::createBinding(value));
1156 }
1157
1158 QQmlSA::Binding::Bindings bindings;
1159 bindings.d_func()->m_bindings = std::move(saBindings);
1160 return bindings;
1161}
1162
1176
1177/*!
1178 Returns \c true if this element is not null, \c false otherwise.
1179 */
1180Element::operator bool() const
1181{
1182 return bool(QQmlJSScope::scope(*this));
1183}
1184
1185/*!
1186 Returns \c true if this element is null, \c false otherwise.
1187 */
1188bool Element::operator!() const
1189{
1190 return !QQmlJSScope::scope(*this);
1191}
1192
1193/*!
1194 Returns the name of this Element.
1195 */
1197{
1198 if (isNull())
1199 return {};
1201}
1202
1203/*!
1204 \fn friend inline bool Element::operator==(const Element &lhs, const Element &rhs)
1205 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
1206 */
1207/*!
1208 \fn friend inline bool Element::operator!=(const Element &lhs, const Element &rhs)
1209 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
1210 */
1211
1212bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs)
1213{
1214 return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs);
1215}
1216
1217/*!
1218 \fn friend inline qsizetype Element::qHash(const Element &key, qsizetype seed) noexcept
1219 Returns the hash for \a key using \a seed to seed the calculation.
1220*/
1221
1223{
1224 return qHash(QQmlJSScope::scope(key), seed);
1225}
1226
1227/*!
1228 \class QQmlSA::GenericPass
1229 \inmodule QtQmlCompiler
1230
1231 \brief The base class for static analysis passes.
1232
1233 This class contains common functionality used by more specific passses.
1234 Custom passes should not directly derive from it, but rather from one of
1235 its subclasses.
1236 \sa ElementPass, PropertyPass
1237 */
1238
1240 Q_DECLARE_PUBLIC(GenericPass);
1241
1242public:
1243 GenericPassPrivate(GenericPass *iface, PassManager *manager)
1244 : m_manager{ manager }, q_ptr{ iface }
1245 {
1246 Q_ASSERT(manager);
1247 }
1248
1249private:
1250 PassManager *m_manager = nullptr;
1251
1252 GenericPass *q_ptr = nullptr;
1253};
1254
1255/*!
1256 Creates a generic pass.
1257 */
1260
1261/*!
1262 Destroys the GenericPass instance.
1263 */
1264GenericPass::~GenericPass() = default;
1265
1266/*!
1267 Emits a warning message \a diagnostic about an issue of type \a id.
1268 */
1273
1274/*!
1275 Emits warning message \a diagnostic about an issue of type \a id located at
1276 \a srcLocation.
1277 */
1287
1288/*!
1289 Emits a warning message \a diagnostic about an issue of type \a id located at
1290 \a srcLocation and with suggested fix \a fix.
1291 */
1302
1303/*!
1304 Returns the type corresponding to \a typeName inside the
1305 currently analysed file.
1306 */
1314
1315/*!
1316 Returns the attached type corresponding to \a typeName used inside
1317 the currently analysed file.
1318 */
1329
1330/*!
1331 Returns the type of \a typeName defined in module \a moduleName.
1332 If an attached type and a non-attached type share the same name
1333 (for example, \c ListView), the \l Element corresponding to the
1334 non-attached type is returned.
1335 To obtain the attached type, use \l resolveAttached.
1336 */
1346
1347/*!
1348 Returns the type of the built-in type identified by \a typeName.
1349 Built-in types encompass \c{C++} types which the QML engine can handle
1350 without any imports (e.g. \l QDateTime and \l QString), global EcmaScript
1351 objects like \c Number, as well as the \l {QML Global Object}
1352 {global Qt object}.
1353 */
1355{
1356 Q_D(const GenericPass);
1359 // we have to check both cpp names
1361 if (!scope) {
1362 // and qml names (e.g. for bool) - builtinImportHelper is private, so we can't do it in one call
1365 }
1367}
1368
1369/*!
1370 Returns the attached type of \a typeName defined in module \a moduleName.
1371 */
1377
1378/*!
1379 Returns the element representing the type of literal in \a binding. If the
1380 binding does not contain a literal value, a null Element is returned.
1381 */
1389
1390/*!
1391 Returns the element in \a context that has id \a id.
1392 */
1401
1402/*!
1403 Returns the id of \a element in a given \a context.
1404 */
1413
1414/*!
1415 Returns the source code located within \a location.
1416 */
1425
1426/*!
1427 \class QQmlSA::PassManager
1428 \inmodule QtQmlCompiler
1429
1430 \brief Can analyze an element and its children with static analysis passes.
1431 */
1432
1433// explicitly defaulted out-of-line for PIMPL
1434PassManager::PassManager() = default;
1435PassManager::~PassManager() = default;
1436
1437/*!
1438 Registers a static analysis \a pass to be run on all elements.
1439 */
1445
1446/*!
1447 \internal
1448 \brief PassManager::registerElementPass registers ElementPass
1449 with the pass manager.
1450 \param pass The registered pass. Ownership is transferred to the pass manager.
1451 */
1456
1458static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup)
1459{
1460 QString name;
1461 if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) {
1462 // Bail out with an invalid name, this type is so screwed up we can't do anything reasonable
1463 // with it We should have warned about it in another plac
1464 if (element.isNull() || element.baseType().isNull())
1465 return u"$INVALID$"_s;
1466 name = QQmlJSScope::scope(element.baseType())->internalName();
1467 } else {
1468 name = QQmlJSScope::scope(element)->internalName();
1469 }
1470
1471 const QString filePath =
1472 (mode == Register || !element.baseType() ? element : element.baseType()).filePath();
1473
1474 if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(u".h"))
1475 name += u'@' + filePath;
1476 return name;
1477}
1478
1479/*!
1480 Registers a static analysis pass for properties. The \a pass will be run on
1481 every property matching the \a moduleName, \a typeName and \a propertyName.
1482
1483 Omitting the \a propertyName will register this pass for all properties
1484 matching the \a typeName and \a moduleName.
1485
1486 Setting \a allowInheritance to \c true means that the filtering on the type
1487 also accepts types deriving from \a typeName.
1488
1489 \a pass is passed as a \c{std::shared_ptr} to allow reusing the same pass
1490 on multiple elements:
1491 \code
1492 auto titleValiadorPass = std::make_shared<TitleValidatorPass>(manager);
1493 manager->registerPropertyPass(titleValidatorPass,
1494 "QtQuick", "Window", "title");
1495 manager->registerPropertyPass(titleValidatorPass,
1496 "QtQuick.Controls", "Dialog", "title");
1497 \endcode
1498
1499 \note Running analysis passes on too many items can be expensive. This is
1500 why it is generally good to filter down the set of properties of a pass
1501 using the \a moduleName, \a typeName and \a propertyName.
1502
1503 Returns \c true if the pass was successfully added, \c false otherwise.
1504 Adding a pass fails when the \l{QQmlSA::Element}{Element} specified by
1505 \a moduleName and \a typeName does not exist.
1506
1507 \sa PropertyPass
1508*/
1516
1520{
1521 if (moduleName.isEmpty() != typeName.isEmpty()) {
1522 qWarning() << "Both the moduleName and the typeName must be specified "
1523 "for the pass to be registered for a specific element.";
1524 }
1525
1526 QString name;
1527 if (!moduleName.isEmpty() && !typeName.isEmpty()) {
1532
1533 if (element.isNull())
1534 return false;
1535
1537 }
1541
1542 return true;
1543}
1544
1565
1567 const QString prefix, bool isAttached)
1568{
1571 for (auto &binding : ownBindings) {
1572 switch (binding.bindingType()) {
1575 prefix + binding.propertyName() + u'.');
1576 continue; // don't insert into m_bindingsByLocation
1579 prefix + binding.propertyName() + u'.', true);
1580 continue; // don't insert into m_bindingsByLocation
1583 u"qsTr"_s, element, binding.sourceLocation());
1584 break;
1585 }
1588 u"qsTrId"_s, element, binding.sourceLocation());
1589 break;
1590 }
1591 default:
1592 break;
1593 }
1594
1601 }
1602}
1603
1604/*!
1605 Runs the element passes over \a root and all its children.
1606 */
1608{
1610 d->analyze(root);
1611}
1612
1613static QQmlJS::ChildScopesIterator childScopesBegin(const Element &element)
1614{
1615 return QQmlJSScope::scope(element)->childScopesBegin();
1616}
1617
1618static QQmlJS::ChildScopesIterator childScopesEnd(const Element &element)
1619{
1620 return QQmlJSScope::scope(element)->childScopesEnd();
1621}
1622
1624{
1627 while (!runStack.isEmpty()) {
1628 auto element = runStack.takeLast();
1630 for (auto &elementPass : m_elementPasses)
1633
1634 for (auto it = childScopesBegin(element), end = childScopesEnd(element); it != end; ++it) {
1635 if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope)
1637 }
1638 }
1639}
1640
1649
1657
1665
1668{
1669 const auto it = m_bindingsByLocation.find(location.offset());
1670
1671 // If there's no matching binding that means we're in a nested Ret somewhere inside an
1672 // expression
1673 if (it == m_bindingsByLocation.cend())
1674 return;
1675
1676 const auto &[offset, binding] = *it;
1679
1683
1685 return;
1686
1691 }
1692}
1693
1694/*!
1695 Returns \c true if the module named \a module has been imported by the
1696 QML to be analyzed, \c false otherwise.
1697
1698 This can be used to skip registering a pass which is specific to a specific
1699 module.
1700
1701 \code
1702 if (passManager->hasImportedModule("QtPositioning"))
1703 passManager->registerElementPass(
1704 std::make_unique<PositioningPass>(passManager)
1705 );
1706 \endcode
1707
1708 \sa registerPropertyPass(), registerElementPass()
1709 */
1711{
1712 return PassManagerPrivate::visitor(*this)->imports().hasType(u"$module$." + module.toString());
1713}
1714
1715/*!
1716 Returns \c true if warnings of \a category are enabled, \c false otherwise.
1717 */
1723
1728
1733
1735 const QString &propertyName)
1736{
1738
1739 for (auto it = QQmlJSScope::scope(element); it; it = it->baseType())
1741
1743
1744 for (const QString &typeName : typeNames) {
1745 for (auto &pass :
1747 if (pass.first == pass.second)
1748 continue;
1749
1750 for (auto it = pass.first; it != pass.second; it++) {
1752 continue;
1755 }
1756 }
1757 }
1758 return passes;
1759}
1760
1761/*!
1762 \class QQmlSA::LintPlugin
1763 \inmodule QtQmlCompiler
1764
1765 \brief Base class for all static analysis plugins.
1766 */
1767
1768/*!
1769 \fn LintPlugin::LintPlugin()
1770 Constructs a LintPlugin object.
1771 */
1772
1773/*!
1774 \fn virtual LintPlugin::~LintPlugin()
1775 Destroys the LintPlugin instance.
1776 */
1777
1778/*!
1779 \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement)
1780
1781 Adds a pass \a manager that will be executed on \a rootElement.
1782 */
1783
1784/*!
1785 \class QQmlSA::ElementPass
1786 \inmodule QtQmlCompiler
1787
1788 \brief Base class for all static analysis passes on elements.
1789
1790 ElementPass is the simpler of the two analysis passes. It will consider every element in
1791 a file. The \l shouldRun() method can be used to filter out irrelevant elements, and the
1792 \l run() method is doing the initial work.
1793
1794 Common tasks suitable for an ElementPass are
1795 \list
1796 \li checking that properties of an Element are not combined in a nonsensical way
1797 \li validating property values (e.g. that a property takes only certain enum values)
1798 \li checking behavior dependent on an Element's parent (e.g. not using \l {Item::width}
1799 when the parent element is a \c Layout).
1800 \endlist
1801
1802 As shown in the snippet below, it is recommended to do necessary type resolution in the
1803 constructor of the ElementPass and cache it in local members, and to implement some
1804 filtering via \l shouldRun() to keep the static analysis performant.
1805
1806 \code
1807 using namespace QQmlSA;
1808 class MyElementPass : public ElementPass
1809 {
1810 Element myType;
1811 public:
1812 MyElementPass(QQmlSA::PassManager *manager)
1813 : myType(resolveType("MyModule", "MyType")) {}
1814
1815 bool shouldRun(const Element &element) override
1816 {
1817 return element.inherits(myType);
1818 }
1819 void run(const Element &element) override
1820 {
1821 // actual pass logic
1822 }
1823 }
1824 \endcode
1825
1826 ElementPasses have limited insight into how an element's properties are used. If you need
1827 that information, consider using a \l PropertyPass instead.
1828
1829 \note ElementPass will only ever consider instantiable types. Therefore, it is unsuitable
1830 to analyze attached types and singletons. Those need to be handled via a PropertyPass.
1831 */
1832
1833/*!
1834 \fn ElementPass::ElementPass(PassManager *manager)
1835 Creates an ElementPass object and uses \a manager to refer to the pass manager.
1836*/
1837
1838/*!
1839 \fn void QQmlSA::ElementPass::run(const Element &element)
1840
1841 Executes if \c shouldRun() returns \c true. Performs the real computation
1842 of the pass on \a element.
1843 This method is meant to be overridden. Calling the base method is not
1844 necessary.
1845 */
1846
1847/*!
1848 Controls whether the \c run() function should be executed on the given \a element.
1849 Subclasses can override this method to improve performance of the analysis by
1850 filtering out elements which are not relevant.
1851
1852 The default implementation unconditionally returns \c true.
1853 */
1855{
1856 (void)element;
1857 return true;
1858}
1859
1860/*!
1861 \class QQmlSA::PropertyPass
1862 \inmodule QtQmlCompiler
1863
1864 \brief Base class for all static analysis passes on properties.
1865 */
1866
1867
1868/*!
1869 Creates a PropertyPass object and uses \a manager to refer to the pass manager.
1870 */
1872
1873/*!
1874 Executes whenever a property gets bound to a value.
1875
1876 The property \a propertyName of \a element is bound to the \a value within
1877 \a bindingScope with \a binding.
1878 */
1889
1890/*!
1891 Executes whenever a property is read.
1892
1893 The property \a propertyName of \a element is read by an instruction within
1894 \a readScope defined at \a location.
1895
1896 This is also executed if the property \a propertyName is called as a function as that requires
1897 the property to be read first.
1898 */
1907
1908/*!
1909 Executes whenever a property or method is called.
1910
1911 The property or method \a propertyName of \a element is called as a function by an instruction
1912 within \a readScope defined at \a location.
1913
1914 \note Currently only direct calls of methods or properties are supported, indirect calls, for
1915 example by storing a method into a JavaScript variable and then calling the variable, are not
1916 recognized.
1917 */
1926
1927/*!
1928 Executes whenever a property is written to.
1929
1930 The property \a propertyName of \a element is written to by an instruction
1931 within \a writeScope defined at \a location. The type of the expression
1932 written to \a propertyName is \a expressionType.
1933 */
1944
1945/*!
1946 Returns bindings by their source location.
1947 */
1953
1954FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *iface) : q_ptr{ iface } { }
1955
1956FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *iface, const QString &description,
1957 const QQmlSA::SourceLocation &location,
1958 const QString &replacement)
1959 : m_fixSuggestion{ description, QQmlSA::SourceLocationPrivate::sourceLocation(location),
1960 replacement },
1961 q_ptr{ iface }
1962{
1963}
1964
1966 const FixSuggestionPrivate &other)
1967 : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ iface }
1968{
1969}
1970
1972 : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ iface }
1973{
1974}
1975
1977{
1978 return m_fixSuggestion.description();
1979}
1980
1981QQmlSA::SourceLocation FixSuggestionPrivate::location() const
1982{
1983 return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_fixSuggestion.location());
1984}
1985
1987{
1988 return m_fixSuggestion.replacement();
1989}
1990
1991void FixSuggestionPrivate::setFileName(const QString &fileName)
1992{
1993 m_fixSuggestion.setFilename(fileName);
1994}
1995
1997{
1998 return m_fixSuggestion.filename();
1999}
2000
2001void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable)
2002{
2003 m_fixSuggestion.setAutoApplicable(autoApplicable);
2004}
2005
2007{
2008 return m_fixSuggestion.isAutoApplicable();
2009}
2010
2011QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion)
2012{
2014}
2015
2016const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion)
2017{
2018 return saFixSuggestion.d_func()->m_fixSuggestion;
2019}
2020
2021/*!
2022 \class QQmlSA::FixSuggestion
2023 \inmodule QtQmlCompiler
2024
2025 \brief Represents a suggested fix for an issue in the source code.
2026 */
2027
2028
2029/*!
2030 Creates a FixSuggestion object.
2031 */
2037
2038/*!
2039 Creates a copy of \a other.
2040 */
2045
2046/*!
2047 Move-constructs a FixSuggestion instance.
2048 */
2050 : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } }
2051{
2052}
2053
2054/*!
2055 Assigns \a other to this FixSuggestion instance.
2056 */
2058{
2059 if (*this == other)
2060 return *this;
2061
2063 return *this;
2064}
2065
2066/*!
2067 Move-assigns \a other to this FixSuggestion instance.
2068 */
2070{
2071 if (*this == other)
2072 return *this;
2073
2075 return *this;
2076}
2077
2078/*!
2079 Destorys the FixSuggestion instance.
2080 */
2081FixSuggestion::~FixSuggestion() = default;
2082
2083/*!
2084 Returns the description of the fix.
2085 */
2087{
2088 Q_D(const FixSuggestion);
2089 return d->description();
2090}
2091
2092/*!
2093 Returns the location where the fix would be applied.
2094 */
2096{
2097 Q_D(const FixSuggestion);
2098 return d->location();
2099}
2100
2101/*!
2102 Returns the fix that will replace the problematic source code.
2103 */
2105{
2106 Q_D(const FixSuggestion);
2107 return d->replacement();
2108}
2109
2110/*!
2111 Sets \a fileName as the name of the file where this fix suggestion applies.
2112 */
2117
2118/*!
2119 Returns the name of the file where this fix suggestion applies.
2120 */
2122{
2124}
2125
2126/*!
2127 Sets \a autoApplicable to determine whether this suggested fix can be
2128 applied automatically.
2129 */
2135
2136/*!
2137 Returns whether this suggested fix can be applied automatically.
2138 */
2140{
2141 Q_D(const FixSuggestion);
2142 return d->isAutoApplicable();
2143}
2144
2145/*!
2146 \fn friend bool FixSuggestion::operator==(const FixSuggestion &lhs, const FixSuggestion &rhs)
2147 Returns \c true if \a lhs and \a rhs are equal, and \c false otherwise.
2148 */
2149/*!
2150 \fn friend bool FixSuggestion::operator!=(const FixSuggestion &lhs, const FixSuggestion &rhs)
2151 Returns \c true if \a lhs and \a rhs are not equal, and \c false otherwise.
2152 */
2153
2155{
2157}
2158
2174
2176{
2177 return type >= BindingType::BoolLiteral && type <= BindingType::Object;
2178}
2179
2180} // namespace QQmlSA
2181
2182QT_END_NAMESPACE
BindingPrivate(Binding *, const BindingPrivate &)
Definition qqmlsa.cpp:307
\inmodule QtQmlCompiler
Definition qqmlsa.h:52
QMultiHash< QString, Binding >::const_iterator constBegin() const
Definition qqmlsa.cpp:213
QMultiHash< QString, Binding >::const_iterator constEnd() const
Definition qqmlsa.cpp:232
QQmlSA::SourceLocation location() const
Definition qqmlsa.cpp:1981
QString description() const
Definition qqmlsa.cpp:1976
void setFileName(const QString &)
Definition qqmlsa.cpp:1991
FixSuggestionPrivate(FixSuggestion *)
Definition qqmlsa.cpp:1954
void setAutoApplicable(bool autoApplicable=true)
Definition qqmlsa.cpp:2001
FixSuggestionPrivate(FixSuggestion *, FixSuggestionPrivate &&)
Definition qqmlsa.cpp:1971
QString replacement() const
Definition qqmlsa.cpp:1986
FixSuggestionPrivate(FixSuggestion *, const FixSuggestionPrivate &)
Definition qqmlsa.cpp:1965
GenericPassPrivate(GenericPass *iface, PassManager *manager)
Definition qqmlsa.cpp:1243
MethodPrivate(Method *, const MethodPrivate &)
Definition qqmlsa.cpp:563
QQmlSA::SourceLocation sourceLocation() const
Definition qqmlsa.cpp:573
MethodType methodType() const
Definition qqmlsa.cpp:578
QString methodName() const
Definition qqmlsa.cpp:568
MethodsPrivate(QQmlSA::Method::Methods *, MethodsPrivate &&)
Definition qqmlsa.cpp:556
QMultiHash< QString, Method >::const_iterator constEnd() const
Definition qqmlsa.cpp:544
MethodsPrivate(QQmlSA::Method::Methods *, const MethodsPrivate &)
Definition qqmlsa.cpp:551
QMultiHash< QString, Method >::const_iterator constBegin() const
Definition qqmlsa.cpp:525
\inmodule QtQmlCompiler
MethodType
Definition qqmlsa.h:48
Q_QMLCOMPILER_EXPORT void emitWarningWithOptionalFix(GenericPass &pass, QAnyStringView diagnostic, const LoggerWarningId &id, const QQmlSA::SourceLocation &srcLocation, const std::optional< QQmlJSFixSuggestion > &fix)
Definition qqmlsa.cpp:2159
@ Register
Definition qqmlsa.cpp:1457
static QQmlJS::ChildScopesIterator childScopesEnd(const Element &element)
Definition qqmlsa.cpp:1618
static QQmlJS::ChildScopesIterator childScopesBegin(const Element &element)
Definition qqmlsa.cpp:1613
bool isRegularBindingType(BindingType type)
Definition qqmlsa.cpp:2175
static QString lookupName(const QQmlSA::Element &element, LookupMode mode=Lookup)
Definition qqmlsa.cpp:1458
Combined button and popup list for selecting options.