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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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