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 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | using namespace Qt::StringLiterals; |
23 | |
24 | namespace QQmlSA { |
25 | |
26 | static_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 | |
42 | Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { } |
43 | |
44 | BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { } |
45 | |
46 | Binding::Bindings::Bindings(const Bindings &other) |
47 | : d_ptr{ new BindingsPrivate{ this, *other.d_func() } } |
48 | { |
49 | } |
50 | |
51 | Binding::Bindings::~Bindings() = default; |
52 | |
53 | BindingsPrivate::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 | |
58 | BindingsPrivate::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 | */ |
66 | QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const |
67 | { |
68 | Q_D(const Bindings); |
69 | return d->constBegin(); |
70 | } |
71 | |
72 | QMultiHash<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 | */ |
80 | QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const |
81 | { |
82 | Q_D(const Bindings); |
83 | return d->constEnd(); |
84 | } |
85 | |
86 | QMultiHash<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 | |
98 | Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { } |
99 | |
100 | BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { } |
101 | |
102 | Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { } |
103 | |
104 | Binding::Binding(Binding &&other) noexcept |
105 | : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { } |
106 | |
107 | Binding &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 | |
117 | Binding &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 | |
127 | Binding::~Binding() = default; |
128 | |
129 | bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs) |
130 | { |
131 | return lhs.d_func()->m_binding == rhs.d_func()->m_binding; |
132 | } |
133 | |
134 | BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other) |
135 | : m_binding{ other.m_binding }, q_ptr{ interface } |
136 | { |
137 | } |
138 | |
139 | QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding) |
140 | { |
141 | QQmlSA::Binding saBinding; |
142 | saBinding.d_func()->m_binding = binding; |
143 | return saBinding; |
144 | } |
145 | |
146 | QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding) |
147 | { |
148 | return binding.d_func()->m_binding; |
149 | } |
150 | |
151 | const 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 | */ |
160 | Element Binding::groupType() const |
161 | { |
162 | return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).groupType()); |
163 | } |
164 | |
165 | QQmlSA::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 | */ |
174 | QString 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 | */ |
182 | QString 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 | */ |
191 | Element 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 | */ |
199 | QQmlSA::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 | */ |
209 | double 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 | */ |
218 | QQmlSA::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 | */ |
226 | bool 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 | */ |
235 | QQmlSA::Element Binding::objectType() const |
236 | { |
237 | return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding: *this).objectType()); |
238 | } |
239 | |
240 | bool 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 | */ |
252 | bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType) |
253 | { |
254 | return QQmlJSMetaPropertyBinding::isLiteralBinding(type: bindingType); |
255 | } |
256 | |
257 | QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { } |
258 | |
259 | QQmlSA::Method::Methods::Methods(const Methods &other) |
260 | : d_ptr{ new MethodsPrivate{ this, *other.d_func() } } |
261 | { |
262 | } |
263 | |
264 | QQmlSA::Method::Methods::~Methods() = default; |
265 | |
266 | /*! |
267 | Returns an iterator to the beginning of the methods. |
268 | */ |
269 | QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const |
270 | { |
271 | Q_D(const Methods); |
272 | return d->constBegin(); |
273 | } |
274 | |
275 | QMultiHash<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 | */ |
283 | QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const |
284 | { |
285 | Q_D(const Methods); |
286 | return d->constEnd(); |
287 | } |
288 | QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const |
289 | { |
290 | return m_methods.constEnd(); |
291 | } |
292 | |
293 | MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { } |
294 | |
295 | MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other) |
296 | : m_methods{ other.m_methods }, q_ptr{ interface } |
297 | { |
298 | } |
299 | |
300 | MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other) |
301 | : m_methods{ std::move(other.m_methods) }, q_ptr{ interface } |
302 | { |
303 | } |
304 | |
305 | MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { } |
306 | |
307 | MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other) |
308 | : m_method{ other.m_method }, q_ptr{ interface } |
309 | { |
310 | } |
311 | |
312 | QString MethodPrivate::methodName() const |
313 | { |
314 | return m_method.methodName(); |
315 | } |
316 | |
317 | QQmlSA::SourceLocation MethodPrivate::sourceLocation() const |
318 | { |
319 | return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: m_method.sourceLocation()); |
320 | } |
321 | |
322 | MethodType 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 | |
334 | Method::Method() : d_ptr{ new MethodPrivate{ this } } { } |
335 | |
336 | Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { } |
337 | |
338 | Method::Method(Method &&other) noexcept |
339 | : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } } |
340 | { |
341 | } |
342 | |
343 | Method &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 | |
353 | Method &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 | |
363 | Method::~Method() = default; |
364 | |
365 | /*! |
366 | Returns the name of the this method. |
367 | */ |
368 | QString 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 | */ |
378 | MethodType 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 | */ |
387 | QQmlSA::SourceLocation Method::sourceLocation() const |
388 | { |
389 | Q_D(const Method); |
390 | return d->sourceLocation(); |
391 | } |
392 | |
393 | bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs) |
394 | { |
395 | return lhs.d_func()->m_method == rhs.d_func()->m_method; |
396 | } |
397 | |
398 | QQmlSA::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 | |
406 | QQmlSA::Method::Methods |
407 | MethodsPrivate::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 | |
419 | QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method) |
420 | { |
421 | return method.d_func()->m_method; |
422 | } |
423 | |
424 | PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { } |
425 | |
426 | PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other) |
427 | : m_property{ other.m_property }, q_ptr{ interface } |
428 | { |
429 | } |
430 | |
431 | PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other) |
432 | : m_property{ std::move(other.m_property) }, q_ptr{ interface } |
433 | { |
434 | } |
435 | |
436 | QString PropertyPrivate::typeName() const |
437 | { |
438 | return m_property.typeName(); |
439 | } |
440 | |
441 | bool 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 | */ |
451 | bool PropertyPrivate::isReadonly() const |
452 | { |
453 | return !m_property.isWritable(); |
454 | } |
455 | |
456 | /*! |
457 | Returns the type that this property was defined with. |
458 | */ |
459 | QQmlSA::Element PropertyPrivate::type() const |
460 | { |
461 | return QQmlJSScope::createQQmlSAElement(m_property.type()); |
462 | } |
463 | |
464 | QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property) |
465 | { |
466 | return property.d_func()->m_property; |
467 | } |
468 | |
469 | QQmlSA::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 | |
484 | Property::Property() : d_ptr{ new PropertyPrivate{ this } } { } |
485 | |
486 | Property::Property(const Property &other) |
487 | : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { } |
488 | |
489 | Property::Property(Property &&other) noexcept |
490 | : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } } |
491 | { |
492 | } |
493 | |
494 | Property &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 | |
504 | Property &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 | |
514 | Property::~Property() = default; |
515 | |
516 | /*! |
517 | Returns the name of the type of this property. |
518 | */ |
519 | QString Property::typeName() const |
520 | { |
521 | Q_D(const Property); |
522 | return d->typeName(); |
523 | } |
524 | |
525 | bool Property::isValid() const |
526 | { |
527 | Q_D(const Property); |
528 | return d->isValid(); |
529 | } |
530 | |
531 | bool Property::isReadonly() const |
532 | { |
533 | Q_D(const Property); |
534 | return d->isReadonly(); |
535 | } |
536 | |
537 | QQmlSA::Element Property::type() const |
538 | { |
539 | Q_D(const Property); |
540 | return d->type(); |
541 | } |
542 | |
543 | |
544 | bool 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 | |
556 | Element::Element() |
557 | { |
558 | new (m_data) QQmlJSScope::ConstPtr(); |
559 | } |
560 | |
561 | Element::Element(const Element &other) |
562 | { |
563 | new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other)); |
564 | } |
565 | |
566 | Element &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 | |
575 | Element::~Element() |
576 | { |
577 | (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr(); |
578 | } |
579 | |
580 | /*! |
581 | Returns the type of Element's scope. |
582 | */ |
583 | QQmlJSScope::ScopeType Element::scopeType() const |
584 | { |
585 | return QQmlJSScope::scope(*this)->scopeType(); |
586 | } |
587 | |
588 | /*! |
589 | Returns the Element this Element derives from. |
590 | */ |
591 | Element 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 | */ |
599 | QString 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 | */ |
607 | Element 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 | */ |
615 | bool Element::inherits(const Element &element) const |
616 | { |
617 | return QQmlJSScope::scope(*this)->inherits(base: QQmlJSScope::scope(element)); |
618 | } |
619 | |
620 | bool Element::isNull() const |
621 | { |
622 | return QQmlJSScope::scope(*this).isNull(); |
623 | } |
624 | |
625 | /*! |
626 | \internal |
627 | */ |
628 | QString 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 | */ |
637 | AccessSemantics 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 | */ |
645 | bool 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 | */ |
653 | bool 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 | */ |
662 | bool 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 | */ |
671 | QQmlSA::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 | */ |
680 | bool 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 | */ |
689 | QString 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 | */ |
697 | bool 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 | */ |
713 | Method::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 | */ |
721 | QQmlSA::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 | */ |
730 | QString 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 | */ |
738 | bool 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 | */ |
747 | bool 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 | */ |
756 | Binding::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 | */ |
765 | Binding::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 | */ |
774 | QList<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 | |
785 | QQmlSA::Binding::Bindings |
786 | BindingsPrivate::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 | |
798 | QQmlSA::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 | |
812 | Element::operator bool() const |
813 | { |
814 | return bool(QQmlJSScope::scope(*this)); |
815 | } |
816 | |
817 | bool Element::operator!() const |
818 | { |
819 | return !QQmlJSScope::scope(*this); |
820 | } |
821 | |
822 | /*! |
823 | Returns the name of this Element. |
824 | */ |
825 | QString Element::name() const |
826 | { |
827 | if (isNull()) |
828 | return {}; |
829 | return QQmlJSScope::prettyName(name: QQmlJSScope::scope(*this)->internalName()); |
830 | } |
831 | |
832 | bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs) |
833 | { |
834 | return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs); |
835 | } |
836 | |
837 | qsizetype 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 | |
854 | class GenericPassPrivate { |
855 | Q_DECLARE_PUBLIC(GenericPass); |
856 | |
857 | public: |
858 | GenericPassPrivate(GenericPass *interface, PassManager *manager) |
859 | : m_manager{ manager }, q_ptr{ interface } |
860 | { |
861 | Q_ASSERT(manager); |
862 | } |
863 | |
864 | private: |
865 | PassManager *m_manager; |
866 | |
867 | GenericPass *q_ptr; |
868 | }; |
869 | |
870 | GenericPass::~GenericPass() = default; |
871 | |
872 | /*! |
873 | Creates a generic pass. |
874 | */ |
875 | GenericPass::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 | */ |
881 | void 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 | */ |
890 | void 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 | */ |
904 | void 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 | */ |
919 | Element 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 | */ |
931 | Element 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 | */ |
949 | Element 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 | */ |
965 | Element 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 | */ |
983 | Element 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 | */ |
993 | Element 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 | */ |
1004 | Element 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 | */ |
1016 | QString 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 | */ |
1027 | QString 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 |
1044 | PassManager::PassManager() = default; |
1045 | PassManager::~PassManager() = default; |
1046 | |
1047 | /*! |
1048 | Registers a static analysis \a pass to be run on all elements. |
1049 | */ |
1050 | void 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 | */ |
1062 | void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass) |
1063 | { |
1064 | m_elementPasses.push_back(x: std::move(pass)); |
1065 | } |
1066 | |
1067 | enum LookupMode { Register, Lookup }; |
1068 | static 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 | */ |
1119 | bool 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 | |
1127 | bool 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 | |
1156 | void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope, |
1157 | const QString prefix, bool isAttached) |
1158 | { |
1159 | const Element ¤tScope = 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 | */ |
1185 | void PassManager::analyze(const Element &root) |
1186 | { |
1187 | Q_D(PassManager); |
1188 | d->analyze(root); |
1189 | } |
1190 | |
1191 | static QQmlJS::ConstPtrWrapperIterator childScopesBegin(const Element &element) |
1192 | { |
1193 | return QQmlJSScope::scope(element)->childScopesBegin(); |
1194 | } |
1195 | |
1196 | static QQmlJS::ConstPtrWrapperIterator childScopesEnd(const Element &element) |
1197 | { |
1198 | return QQmlJSScope::scope(element)->childScopesEnd(); |
1199 | } |
1200 | |
1201 | void 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 | |
1219 | void 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 | |
1227 | void 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 | |
1234 | void 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 | */ |
1274 | bool 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 | */ |
1282 | bool PassManager::isCategoryEnabled(LoggerWarningId category) const |
1283 | { |
1284 | return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(id: category); |
1285 | } |
1286 | |
1287 | QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager) |
1288 | { |
1289 | return manager.d_func()->m_visitor; |
1290 | } |
1291 | |
1292 | QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager) |
1293 | { |
1294 | return manager.d_func()->m_typeResolver; |
1295 | } |
1296 | |
1297 | QSet<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 | |
1331 | void 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 | */ |
1421 | bool 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 | |
1435 | PropertyPass::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 | */ |
1442 | void 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 | */ |
1459 | void 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 | */ |
1475 | void 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 | |
1486 | DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager) |
1487 | { |
1488 | } |
1489 | |
1490 | void 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 | |
1503 | void 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 | |
1526 | void 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 | */ |
1541 | std::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 | */ |
1550 | std::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 | */ |
1559 | std::unordered_map<quint32, BindingInfo> PassManager::bindingsByLocation() const |
1560 | { |
1561 | Q_D(const PassManager); |
1562 | return d->m_bindingsByLocation; |
1563 | } |
1564 | |
1565 | FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { } |
1566 | |
1567 | FixSuggestionPrivate::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 | |
1576 | FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, |
1577 | const FixSuggestionPrivate &other) |
1578 | : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface } |
1579 | { |
1580 | } |
1581 | |
1582 | FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other) |
1583 | : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface } |
1584 | { |
1585 | } |
1586 | |
1587 | QString FixSuggestionPrivate::fixDescription() const |
1588 | { |
1589 | return m_fixSuggestion.fixDescription(); |
1590 | } |
1591 | |
1592 | QQmlSA::SourceLocation FixSuggestionPrivate::location() const |
1593 | { |
1594 | return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(jsLocation: m_fixSuggestion.location()); |
1595 | } |
1596 | |
1597 | QString FixSuggestionPrivate::replacement() const |
1598 | { |
1599 | return m_fixSuggestion.replacement(); |
1600 | } |
1601 | |
1602 | void FixSuggestionPrivate::setFileName(const QString &fileName) |
1603 | { |
1604 | m_fixSuggestion.setFilename(fileName); |
1605 | } |
1606 | |
1607 | QString FixSuggestionPrivate::fileName() const |
1608 | { |
1609 | return m_fixSuggestion.filename(); |
1610 | } |
1611 | |
1612 | void FixSuggestionPrivate::setHint(const QString &hint) |
1613 | { |
1614 | m_fixSuggestion.setHint(hint); |
1615 | } |
1616 | |
1617 | QString FixSuggestionPrivate::hint() const |
1618 | { |
1619 | return m_fixSuggestion.hint(); |
1620 | } |
1621 | |
1622 | void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable) |
1623 | { |
1624 | m_fixSuggestion.setAutoApplicable(autoApplicable); |
1625 | } |
1626 | |
1627 | bool FixSuggestionPrivate::isAutoApplicable() const |
1628 | { |
1629 | return m_fixSuggestion.isAutoApplicable(); |
1630 | } |
1631 | |
1632 | QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion) |
1633 | { |
1634 | return saFixSuggestion.d_func()->m_fixSuggestion; |
1635 | } |
1636 | |
1637 | const 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 | |
1650 | FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location, |
1651 | const QString &replacement) |
1652 | : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } } |
1653 | { |
1654 | } |
1655 | |
1656 | FixSuggestion::FixSuggestion(const FixSuggestion &other) |
1657 | : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } } |
1658 | { |
1659 | } |
1660 | |
1661 | FixSuggestion::FixSuggestion(FixSuggestion &&other) noexcept |
1662 | : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } } |
1663 | { |
1664 | } |
1665 | |
1666 | FixSuggestion &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 | |
1675 | FixSuggestion &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 | |
1684 | FixSuggestion::~FixSuggestion() = default; |
1685 | |
1686 | /*! |
1687 | Returns the description of the fix. |
1688 | */ |
1689 | QString 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 | */ |
1697 | QQmlSA::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 | */ |
1706 | QString 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 | */ |
1714 | void 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 | */ |
1722 | QString 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 | */ |
1730 | void 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 | */ |
1738 | QString 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 | */ |
1747 | void 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 | */ |
1755 | bool QQmlSA::FixSuggestion::isAutoApplicable() const |
1756 | { |
1757 | return FixSuggestionPrivate::fixSuggestion(saFixSuggestion: *this).isAutoApplicable(); |
1758 | } |
1759 | |
1760 | bool 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 | |
1767 | QT_END_NAMESPACE |
1768 |
Definitions
- Bindings
- BindingsPrivate
- Bindings
- ~Bindings
- BindingsPrivate
- BindingsPrivate
- constBegin
- constBegin
- constEnd
- constEnd
- Binding
- BindingPrivate
- Binding
- Binding
- operator=
- operator=
- ~Binding
- operatorEqualsImpl
- BindingPrivate
- createBinding
- binding
- binding
- groupType
- bindingType
- stringValue
- propertyName
- attachingType
- sourceLocation
- numberValue
- scriptKind
- hasObject
- objectType
- hasUndefinedScriptValue
- isLiteralBinding
- Methods
- Methods
- ~Methods
- constBegin
- constBegin
- constEnd
- constEnd
- MethodsPrivate
- MethodsPrivate
- MethodsPrivate
- MethodPrivate
- MethodPrivate
- methodName
- sourceLocation
- methodType
- Method
- Method
- Method
- operator=
- operator=
- ~Method
- methodName
- methodType
- sourceLocation
- operatorEqualsImpl
- createMethod
- createMethods
- method
- PropertyPrivate
- PropertyPrivate
- PropertyPrivate
- typeName
- isValid
- isReadonly
- type
- property
- createProperty
- Property
- Property
- Property
- operator=
- operator=
- ~Property
- typeName
- isValid
- isReadonly
- type
- operatorEqualsImpl
- Element
- Element
- operator=
- ~Element
- scopeType
- baseType
- baseTypeName
- parentScope
- inherits
- isNull
- internalId
- accessSemantics
- isComposite
- hasProperty
- hasOwnProperty
- property
- isPropertyRequired
- defaultPropertyName
- hasMethod
- ownMethods
- sourceLocation
- filePath
- hasPropertyBindings
- hasOwnPropertyBindings
- ownPropertyBindings
- ownPropertyBindings
- propertyBindings
- createBindings
- createBindings
- operator bool
- operator!
- name
- operatorEqualsImpl
- qHashImpl
- GenericPassPrivate
- GenericPassPrivate
- ~GenericPass
- GenericPass
- emitWarning
- emitWarning
- emitWarning
- resolveTypeInFileScope
- resolveAttachedInFileScope
- resolveType
- resolveBuiltinType
- resolveAttached
- resolveLiteralType
- resolveIdToElement
- resolveElementToId
- sourceCode
- PassManager
- ~PassManager
- registerElementPass
- registerElementPass
- LookupMode
- lookupName
- registerPropertyPass
- registerPropertyPass
- addBindingSourceLocations
- analyze
- childScopesBegin
- childScopesEnd
- analyze
- analyzeWrite
- analyzeRead
- analyzeBinding
- hasImportedModule
- isCategoryEnabled
- visitor
- resolver
- findPropertyUsePasses
- run
- shouldRun
- PropertyPass
- onBinding
- onRead
- onWrite
- DebugPropertyPass
- onRead
- onBinding
- onWrite
- elementPasses
- propertyPasses
- bindingsByLocation
- FixSuggestionPrivate
- FixSuggestionPrivate
- FixSuggestionPrivate
- FixSuggestionPrivate
- fixDescription
- location
- replacement
- setFileName
- fileName
- setHint
- hint
- setAutoApplicable
- isAutoApplicable
- fixSuggestion
- fixSuggestion
- FixSuggestion
- FixSuggestion
- FixSuggestion
- operator=
- operator=
- ~FixSuggestion
- fixDescription
- location
- replacement
- setFileName
- fileName
- setHint
- hint
- setAutoApplicable
- isAutoApplicable
Learn to use CMake with our Intro Training
Find out more