aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmlsa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmlsa.cpp')
-rw-r--r--src/qmlcompiler/qqmlsa.cpp1767
1 files changed, 1767 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp
new file mode 100644
index 0000000000..d2d0e6a74e
--- /dev/null
+++ b/src/qmlcompiler/qqmlsa.cpp
@@ -0,0 +1,1767 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmlsa.h"
+#include "qqmlsa_p.h"
+#include "qqmlsasourcelocation.h"
+
+#include "qqmljsscope_p.h"
+#include "qqmljslogger_p.h"
+#include "qqmljstyperesolver_p.h"
+#include "qqmljsimportvisitor_p.h"
+#include "qqmljsutils_p.h"
+#include "qdeferredpointer_p.h"
+
+#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
+
+#include <memory>
+#include <new>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QQmlSA {
+
+static_assert(QQmlJSScope::sizeofQQmlSAElement() == sizeof(Element));
+
+/*!
+ \namespace QQmlSA
+ \inmodule QtQmlCompiler
+
+ \brief Provides tools for static analysis on QML programs.
+ */
+
+/*!
+ \class QQmlSA::Binding::Bindings
+ \inmodule QtQmlCompiler
+
+ \brief Holds multiple property name to property binding associations.
+ */
+
+Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { }
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { }
+
+Binding::Bindings::Bindings(const Bindings &other)
+ : d_ptr{ new BindingsPrivate{ this, *other.d_func() } }
+{
+}
+
+Binding::Bindings::~Bindings() = default;
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, const BindingsPrivate &other)
+ : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ interface }
+{
+}
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, BindingsPrivate &&other)
+ : m_bindings{ std::move(other.m_bindings) }, q_ptr{ interface }
+{
+}
+
+/*!
+ Returns an iterator to the beginning of the bindings.
+ */
+QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const
+{
+ Q_D(const Bindings);
+ return d->constBegin();
+}
+
+QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constBegin() const
+{
+ return m_bindings.constBegin();
+}
+
+/*!
+ Returns an iterator to the end of the bindings.
+ */
+QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const
+{
+ Q_D(const Bindings);
+ return d->constEnd();
+}
+
+QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constEnd() const
+{
+ return m_bindings.constEnd();
+}
+
+/*!
+ \class QQmlSA::Binding
+ \inmodule QtQmlCompiler
+
+ \brief Represents a single QML property binding for a specific type.
+ */
+
+Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { }
+
+BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { }
+
+Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
+
+Binding::Binding(Binding &&other) noexcept
+ : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
+
+Binding &Binding::operator=(const Binding &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_binding = other.d_func()->m_binding;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Binding &Binding::operator=(Binding &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_binding = std::move(other.d_func()->m_binding);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Binding::~Binding() = default;
+
+bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs)
+{
+ return lhs.d_func()->m_binding == rhs.d_func()->m_binding;
+}
+
+BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other)
+ : m_binding{ other.m_binding }, q_ptr{ interface }
+{
+}
+
+QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding)
+{
+ QQmlSA::Binding saBinding;
+ saBinding.d_func()->m_binding = binding;
+ return saBinding;
+}
+
+QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding)
+{
+ return binding.d_func()->m_binding;
+}
+
+const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding)
+{
+ return binding.d_func()->m_binding;
+}
+
+/*!
+ Returns the type of the property of this binding if it is a group property,
+ otherwise returns an invalid Element.
+ */
+Element Binding::groupType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).groupType());
+}
+
+QQmlSA::BindingType Binding::bindingType() const
+{
+ return BindingPrivate::binding(*this).bindingType();
+}
+
+/*!
+ Returns the associated string literal if the content type of this binding is
+ StringLiteral, otherwise returns an empty string.
+ */
+QString Binding::stringValue() const
+{
+ return BindingPrivate::binding(*this).stringValue();
+}
+
+/*!
+ Returns the name of the property bound with this binding.
+ */
+QString Binding::propertyName() const
+{
+ return BindingPrivate::binding(*this).propertyName();
+}
+
+/*!
+ Returns the attached type if the content type of this binding is
+ AttachedProperty, otherwise returns an invalid Element.
+ */
+Element Binding::attachingType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).attachingType());
+}
+
+/*!
+ Returns the location in the QML code where this binding is defined.
+ */
+QQmlSA::SourceLocation Binding::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ BindingPrivate::binding(*this).sourceLocation());
+}
+
+/*!
+ Returns the associated number if the content type of this binding is
+ NumberLiteral, otherwise returns 0.
+ */
+double Binding::numberValue() const
+{
+ return BindingPrivate::binding(*this).numberValue();
+}
+
+/*!
+ Returns the kind of the associated script if the content type of this
+ binding is Script, otherwise returns Script_Invalid.
+ */
+QQmlSA::ScriptBindingKind Binding::scriptKind() const
+{
+ return BindingPrivate::binding(*this).scriptKind();
+}
+
+/*!
+ Returns \c true if this binding has an objects, otherwise returns \c false.
+ */
+bool Binding::hasObject() const
+{
+ return BindingPrivate::binding(*this).hasObject();
+}
+
+/*!
+ Returns the type of the associated object if the content type of this
+ binding is Object, otherwise returns an invalid Element.
+ */
+QQmlSA::Element Binding::objectType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).objectType());
+}
+
+bool Binding::hasUndefinedScriptValue() const
+{
+ const auto &jsBinding = BindingPrivate::binding(*this);
+ return jsBinding.bindingType() == BindingType::Script
+ && jsBinding.scriptValueType() == ScriptValue_Undefined;
+}
+
+/*!
+ Returns \c true if \a bindingType is a literal type, and \c false
+ otherwise. Literal types include strings, booleans, numbers, regular
+ expressions.
+ */
+bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType)
+{
+ return QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType);
+}
+
+QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { }
+
+QQmlSA::Method::Methods::Methods(const Methods &other)
+ : d_ptr{ new MethodsPrivate{ this, *other.d_func() } }
+{
+}
+
+QQmlSA::Method::Methods::~Methods() = default;
+
+/*!
+ Returns an iterator to the beginning of the methods.
+ */
+QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const
+{
+ Q_D(const Methods);
+ return d->constBegin();
+}
+
+QMultiHash<QString, Method>::const_iterator MethodsPrivate::constBegin() const
+{
+ return m_methods.constBegin();
+}
+
+/*!
+ Returns an iterator to the end of the methods.
+ */
+QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const
+{
+ Q_D(const Methods);
+ return d->constEnd();
+}
+QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const
+{
+ return m_methods.constEnd();
+}
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { }
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other)
+ : m_methods{ other.m_methods }, q_ptr{ interface }
+{
+}
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other)
+ : m_methods{ std::move(other.m_methods) }, q_ptr{ interface }
+{
+}
+
+MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { }
+
+MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other)
+ : m_method{ other.m_method }, q_ptr{ interface }
+{
+}
+
+QString MethodPrivate::methodName() const
+{
+ return m_method.methodName();
+}
+
+QQmlSA::SourceLocation MethodPrivate::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_method.sourceLocation());
+}
+
+MethodType MethodPrivate::methodType() const
+{
+ return m_method.methodType();
+}
+
+/*!
+ \class QQmlSA::Method
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML method.
+ */
+
+Method::Method() : d_ptr{ new MethodPrivate{ this } } { }
+
+Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { }
+
+Method::Method(Method &&other) noexcept
+ : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } }
+{
+}
+
+Method &Method::operator=(const Method &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_method = other.d_func()->m_method;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Method &Method::operator=(Method &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_method = std::move(other.d_func()->m_method);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Method::~Method() = default;
+
+/*!
+ Returns the name of the this method.
+ */
+QString Method::methodName() const
+{
+ Q_D(const Method);
+ return d->methodName();
+}
+
+/*!
+ Returns the type of this method. For example, Signal, Slot, Method or
+ StaticMethod.
+ */
+MethodType Method::methodType() const
+{
+ Q_D(const Method);
+ return d->methodType();
+}
+
+/*!
+ Returns the location in the QML code where this method is defined.
+ */
+QQmlSA::SourceLocation Method::sourceLocation() const
+{
+ Q_D(const Method);
+ return d->sourceLocation();
+}
+
+bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs)
+{
+ return lhs.d_func()->m_method == rhs.d_func()->m_method;
+}
+
+QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod)
+{
+ QQmlSA::Method saMethod;
+ auto &wrappedMethod = saMethod.d_func()->m_method;
+ wrappedMethod = jsMethod;
+ return saMethod;
+}
+
+QQmlSA::Method::Methods
+MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash)
+{
+ QMultiHash<QString, QQmlSA::Method> saMethods;
+ for (const auto &[key, value] : hash.asKeyValueRange()) {
+ saMethods.insert(key, MethodPrivate::createMethod(value));
+ }
+
+ QQmlSA::Method::Methods methods;
+ methods.d_func()->m_methods = std::move(saMethods);
+ return methods;
+}
+
+QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method)
+{
+ return method.d_func()->m_method;
+}
+
+PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { }
+
+PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other)
+ : m_property{ other.m_property }, q_ptr{ interface }
+{
+}
+
+PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other)
+ : m_property{ std::move(other.m_property) }, q_ptr{ interface }
+{
+}
+
+QString PropertyPrivate::typeName() const
+{
+ return m_property.typeName();
+}
+
+bool PropertyPrivate::isValid() const
+{
+ return m_property.isValid();
+}
+
+/*!
+ Returns whether this property is readonly. Properties defined in QML are readonly when their
+ definition has the 'readonly' keyword. Properties defined in C++ are readonly when they do not
+ have a WRITE accessor function.
+ */
+bool PropertyPrivate::isReadonly() const
+{
+ return !m_property.isWritable();
+}
+
+/*!
+ Returns the type that this property was defined with.
+ */
+QQmlSA::Element PropertyPrivate::type() const
+{
+ return QQmlJSScope::createQQmlSAElement(m_property.type());
+}
+
+QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property)
+{
+ return property.d_func()->m_property;
+}
+
+QQmlSA::Property PropertyPrivate::createProperty(const QQmlJSMetaProperty &property)
+{
+ QQmlSA::Property saProperty;
+ auto &wrappedProperty = saProperty.d_func()->m_property;
+ wrappedProperty = property;
+ return saProperty;
+}
+
+/*!
+ \class QQmlSA::Property
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML property.
+ */
+
+Property::Property() : d_ptr{ new PropertyPrivate{ this } } { }
+
+Property::Property(const Property &other)
+ : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { }
+
+Property::Property(Property &&other) noexcept
+ : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } }
+{
+}
+
+Property &Property::operator=(const Property &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_property = other.d_func()->m_property;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Property &Property::operator=(Property &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_property = std::move(other.d_func()->m_property);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Property::~Property() = default;
+
+/*!
+ Returns the name of the type of this property.
+ */
+QString Property::typeName() const
+{
+ Q_D(const Property);
+ return d->typeName();
+}
+
+bool Property::isValid() const
+{
+ Q_D(const Property);
+ return d->isValid();
+}
+
+bool Property::isReadonly() const
+{
+ Q_D(const Property);
+ return d->isReadonly();
+}
+
+QQmlSA::Element Property::type() const
+{
+ Q_D(const Property);
+ return d->type();
+}
+
+
+bool Property::operatorEqualsImpl(const Property &lhs, const Property &rhs)
+{
+ return lhs.d_func()->m_property == rhs.d_func()->m_property;
+}
+
+/*!
+ \class QQmlSA::Element
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML type.
+ */
+
+Element::Element()
+{
+ new (m_data) QQmlJSScope::ConstPtr();
+}
+
+Element::Element(const Element &other)
+{
+ new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other));
+}
+
+Element &Element::operator=(const Element &other)
+{
+ if (this == &other)
+ return *this;
+
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other);
+ return *this;
+}
+
+Element::~Element()
+{
+ (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr();
+}
+
+/*!
+ Returns the type of Element's scope.
+ */
+QQmlJSScope::ScopeType Element::scopeType() const
+{
+ return QQmlJSScope::scope(*this)->scopeType();
+}
+
+/*!
+ Returns the Element this Element derives from.
+ */
+Element Element::baseType() const
+{
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->baseType());
+}
+
+/*!
+ Returns the name of the Element this Element derives from.
+ */
+QString Element::baseTypeName() const
+{
+ return QQmlJSScope::prettyName(QQmlJSScope::scope(*this)->baseTypeName());
+}
+
+/*!
+ Returns the Element that encloses this Element.
+ */
+Element Element::parentScope() const
+{
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->parentScope());
+}
+
+/*!
+ Returns whether this Element inherits from \a element.
+ */
+bool Element::inherits(const Element &element) const
+{
+ return QQmlJSScope::scope(*this)->inherits(QQmlJSScope::scope(element));
+}
+
+bool Element::isNull() const
+{
+ return QQmlJSScope::scope(*this).isNull();
+}
+
+/*!
+ \internal
+ */
+QString Element::internalId() const
+{
+ return QQmlJSScope::scope(*this)->internalName();
+}
+
+/*!
+ Returns the access semantics of this Element. For example, Reference,
+ Value or Sequence.
+ */
+AccessSemantics Element::accessSemantics() const
+{
+ return QQmlJSScope::scope(*this)->accessSemantics();
+}
+
+/*!
+ Returns true for objects defined from Qml, and false for objects declared from C++.
+ */
+bool QQmlSA::Element::isComposite() const
+{
+ return QQmlJSScope::scope(*this)->isComposite();
+}
+
+/*!
+ Returns whether this Element has a property with the name \a propertyName.
+ */
+bool Element::hasProperty(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasProperty(propertyName);
+}
+
+/*!
+ Returns whether this Element defines a property with the name \a propertyName
+ which is not defined on its base or extension objects.
+ */
+bool Element::hasOwnProperty(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasOwnProperty(propertyName);
+}
+
+/*!
+ Returns the property with the name \a propertyName if it is found in this
+ Element or its base and extension objects, otherwise returns an invalid property.
+ */
+QQmlSA::Property Element::property(const QString &propertyName) const
+{
+ return PropertyPrivate::createProperty(QQmlJSScope::scope(*this)->property(propertyName));
+}
+
+/*!
+ Returns whether the property with the name \a propertyName resolved on this
+ Element is required. Returns false if the the property couldn't be found.
+ */
+bool Element::isPropertyRequired(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->isPropertyRequired(propertyName);
+}
+
+/*!
+ Returns the name of the default property of this Element. If it doesn't
+ have one, returns an empty string.
+ */
+QString Element::defaultPropertyName() const
+{
+ return QQmlJSScope::scope(*this)->defaultPropertyName();
+}
+
+/*!
+ Returns whether this Element has a method with the name \a methodName.
+ */
+bool Element::hasMethod(const QString &methodName) const
+{
+ return QQmlJSScope::scope(*this)->hasMethod(methodName);
+}
+
+/*!
+ \class QQmlSA::Method::Methods
+ \inmodule QtQmlCompiler
+
+ \brief Holds multiple method name to method associations.
+ */
+
+/*!
+ Returns this Elements's methods, which are not defined on its base or
+ extension objects.
+ */
+Method::Methods Element::ownMethods() const
+{
+ return MethodsPrivate::createMethods(QQmlJSScope::scope(*this)->ownMethods());
+}
+
+/*!
+ Returns the location in the QML code where this Element is defined.
+ */
+QQmlSA::SourceLocation Element::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ QQmlJSScope::scope(*this)->sourceLocation());
+}
+
+/*!
+ Returns the file path of the QML code that defines this Element.
+ */
+QString Element::filePath() const
+{
+ return QQmlJSScope::scope(*this)->filePath();
+}
+
+/*!
+ Returns whether this Element has a property binding with the name \a name.
+ */
+bool Element::hasPropertyBindings(const QString &name) const
+{
+ return QQmlJSScope::scope(*this)->hasPropertyBindings(name);
+}
+
+/*!
+ Returns whether this Element has property bindings which are not defined in
+ its base or extension objects and that have name \a propertyName.
+ */
+bool Element::hasOwnPropertyBindings(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasOwnPropertyBindings(propertyName);
+}
+
+/*!
+ Returns this Element's property bindings which are not defined on its base
+ or extension objects.
+ */
+Binding::Bindings Element::ownPropertyBindings() const
+{
+ return BindingsPrivate::createBindings(QQmlJSScope::scope(*this)->ownPropertyBindings());
+}
+
+/*!
+ Returns this Element's property bindings which are not defined on its base
+ or extension objects and that have the name \a propertyName.
+ */
+Binding::Bindings Element::ownPropertyBindings(const QString &propertyName) const
+{
+ return BindingsPrivate::createBindings(
+ QQmlJSScope::scope(*this)->ownPropertyBindings(propertyName));
+}
+
+/*!
+ Returns this Element's property bindings that have the name \a propertyName.
+ */
+QList<Binding> Element::propertyBindings(const QString &propertyName) const
+{
+ const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(propertyName);
+
+ QList<Binding> saBindings;
+ for (const auto &jsBinding : bindings) {
+ saBindings.push_back(BindingPrivate::createBinding(jsBinding));
+ }
+ return saBindings;
+}
+
+QQmlSA::Binding::Bindings
+BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash)
+{
+ QMultiHash<QString, QQmlSA::Binding> saBindings;
+ for (const auto &[key, value] : hash.asKeyValueRange()) {
+ saBindings.insert(key, BindingPrivate::createBinding(value));
+ }
+
+ QQmlSA::Binding::Bindings bindings;
+ bindings.d_func()->m_bindings = std::move(saBindings);
+ return bindings;
+}
+
+QQmlSA::Binding::Bindings BindingsPrivate::createBindings(
+ QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
+ QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> iterators)
+{
+ QMultiHash<QString, QQmlSA::Binding> saBindings;
+ for (auto it = iterators.first; it != iterators.second; ++it) {
+ saBindings.insert(it.key(), BindingPrivate::createBinding(it.value()));
+ }
+
+ QQmlSA::Binding::Bindings bindings;
+ bindings.d_func()->m_bindings = std::move(saBindings);
+ return bindings;
+}
+
+Element::operator bool() const
+{
+ return bool(QQmlJSScope::scope(*this));
+}
+
+bool Element::operator!() const
+{
+ return !QQmlJSScope::scope(*this);
+}
+
+/*!
+ Returns the name of this Element.
+ */
+QString Element::name() const
+{
+ if (isNull())
+ return {};
+ return QQmlJSScope::prettyName(QQmlJSScope::scope(*this)->internalName());
+}
+
+bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs)
+{
+ return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs);
+}
+
+qsizetype Element::qHashImpl(const Element &key, qsizetype seed) noexcept
+{
+ return qHash(QQmlJSScope::scope(key), seed);
+}
+
+/*!
+ \class QQmlSA::GenericPass
+ \inmodule QtQmlCompiler
+
+ \brief The base class for static analysis passes.
+
+ This class contains common functionality used by more specific passses.
+ Custom passes should not directly derive from it, but rather from one of
+ its subclasses.
+ \sa ElementPass, PropertyPass
+ */
+
+class GenericPassPrivate {
+ Q_DECLARE_PUBLIC(GenericPass);
+
+public:
+ GenericPassPrivate(GenericPass *interface, PassManager *manager)
+ : m_manager{ manager }, q_ptr{ interface }
+ {
+ Q_ASSERT(manager);
+ }
+
+private:
+ PassManager *m_manager;
+
+ GenericPass *q_ptr;
+};
+
+GenericPass::~GenericPass() = default;
+
+/*!
+ Creates a generic pass.
+ */
+GenericPass::GenericPass(PassManager *manager)
+ : d_ptr{ new GenericPassPrivate{ this, manager } } { }
+
+/*!
+ Emits a warning message \a diagnostic about an issue of type \a id.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id)
+{
+ emitWarning(diagnostic, id, QQmlSA::SourceLocation{});
+}
+
+/*!
+ Emits warning message \a diagnostic about an issue of type \a id located at
+ \a srcLocation.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation)
+{
+ Q_D(const GenericPass);
+ PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->log(diagnostic.toString(), id,
+ QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation));
+}
+
+/*!
+ Emits a warning message \a diagnostic about an issue of type \a id located at
+ \a srcLocation and with suggested fix \a fix.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix)
+{
+ Q_D(const GenericPass);
+ PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->log(diagnostic.toString(), id,
+ QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation), true, true,
+ FixSuggestionPrivate::fixSuggestion(fix));
+}
+
+/*!
+ Returns the type corresponding to \a typeName inside the
+ currently analysed file.
+ */
+Element GenericPass::resolveTypeInFileScope(QAnyStringView typeName)
+{
+ Q_D(const GenericPass);
+ const auto scope =
+ PassManagerPrivate::visitor(*d->m_manager)->imports().type(typeName.toString()).scope;
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the attached type corresponding to \a typeName used inside
+ the currently analysed file.
+ */
+Element GenericPass::resolveAttachedInFileScope(QAnyStringView typeName)
+{
+ const auto type = resolveTypeInFileScope(typeName);
+ const auto scope = QQmlJSScope::scope(type);
+
+ if (scope.isNull())
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::ConstPtr(nullptr));
+
+ return QQmlJSScope::createQQmlSAElement(scope->attachedType());
+}
+
+/*!
+ Returns the type of \a typeName defined in module \a moduleName.
+ If an attached type and a non-attached type share the same name
+ (for example, \c ListView), the \l Element corresponding to the
+ non-attached type is returned.
+ To obtain the attached type, use \l resolveAttached.
+ */
+Element GenericPass::resolveType(QAnyStringView moduleName, QAnyStringView typeName)
+{
+ Q_D(const GenericPass);
+ QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
+ const auto module = typeImporter->importModule(moduleName.toString());
+ const auto scope = module.type(typeName.toString()).scope;
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the type of the built-in type identified by \a typeName.
+ Built-in types encompass \c{C++} types which the QML engine can handle
+ without any imports (e.g. \l QDateTime and \l QString), global EcmaScript
+ objects like \c Number, as well as the \l {QML Global Object}
+ {global Qt object}.
+ */
+Element GenericPass::resolveBuiltinType(QAnyStringView typeName) const
+{
+ Q_D(const GenericPass);
+ QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
+ auto typeNameString = typeName.toString();
+ // we have to check both cpp names
+ auto scope = typeImporter->builtinInternalNames().type(typeNameString).scope;
+ if (!scope) {
+ // and qml names (e.g. for bool) - builtinImportHelper is private, so we can't do it in one call
+ auto builtins = typeImporter->importBuiltins();
+ scope = builtins.type(typeNameString).scope;
+ }
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the attached type of \a typeName defined in module \a moduleName.
+ */
+Element GenericPass::resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
+{
+ const auto &resolvedType = resolveType(moduleName, typeName);
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(resolvedType)->attachedType());
+}
+
+/*!
+ Returns the element representing the type of literal in \a binding. If the
+ binding does not contain a literal value, a null Element is returned.
+ */
+Element GenericPass::resolveLiteralType(const QQmlSA::Binding &binding)
+{
+ Q_D(const GenericPass);
+
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding).literalType(
+ PassManagerPrivate::resolver(*d->m_manager)));
+}
+
+/*!
+ Returns the element in \a context that has id \a id.
+ */
+Element GenericPass::resolveIdToElement(QAnyStringView id, const Element &context)
+{
+ Q_D(const GenericPass);
+ const auto scope = PassManagerPrivate::visitor(*d->m_manager)
+ ->addressableScopes()
+ .scope(id.toString(), QQmlJSScope::scope(context));
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the id of \a element in a given \a context.
+ */
+QString GenericPass::resolveElementToId(const Element &element, const Element &context)
+{
+ Q_D(const GenericPass);
+ return PassManagerPrivate::visitor(*d->m_manager)
+ ->addressableScopes()
+ .id(QQmlJSScope::scope(element), QQmlJSScope::scope(context));
+}
+
+/*!
+ Returns the source code located within \a location.
+ */
+QString GenericPass::sourceCode(QQmlSA::SourceLocation location)
+{
+ Q_D(const GenericPass);
+ return PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->code()
+ .mid(location.offset(), location.length());
+}
+
+/*!
+ \class QQmlSA::PassManager
+ \inmodule QtQmlCompiler
+
+ \brief Can analyze an element and its children with static analysis passes.
+ */
+
+// explicitly defaulted out-of-line for PIMPL
+PassManager::PassManager() = default;
+PassManager::~PassManager() = default;
+
+/*!
+ Registers a static analysis \a pass to be run on all elements.
+ */
+void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass)
+{
+ Q_D(PassManager);
+ d->registerElementPass(std::move(pass));
+}
+
+/*!
+ \internal
+ \brief PassManager::registerElementPass registers ElementPass
+ with the pass manager.
+ \param pass The registered pass. Ownership is transferred to the pass manager.
+ */
+void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass)
+{
+ m_elementPasses.push_back(std::move(pass));
+}
+
+enum LookupMode { Register, Lookup };
+static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup)
+{
+ QString name;
+ if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) {
+ // Bail out with an invalid name, this type is so screwed up we can't do anything reasonable
+ // with it We should have warned about it in another plac
+ if (element.isNull() || element.baseType().isNull())
+ return u"$INVALID$"_s;
+ name = QQmlJSScope::scope(element.baseType())->internalName();
+ } else {
+ name = QQmlJSScope::scope(element)->internalName();
+ }
+
+ const QString filePath =
+ (mode == Register || !element.baseType() ? element : element.baseType()).filePath();
+
+ if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(u".h"))
+ name += u'@' + filePath;
+ return name;
+}
+
+/*!
+ Registers a static analysis pass for properties. The \a pass will be run on
+ every property matching the \a moduleName, \a typeName and \a propertyName.
+
+ Omitting the \a propertyName will register this pass for all properties
+ matching the \a typeName and \a moduleName.
+
+ Setting \a allowInheritance to \c true means that the filtering on the type
+ also accepts types deriving from \a typeName.
+
+ \a pass is passed as a \c{std::shared_ptr} to allow reusing the same pass
+ on multiple elements:
+ \code
+ auto titleValiadorPass = std::make_shared<TitleValidatorPass>(manager);
+ manager->registerPropertyPass(titleValidatorPass,
+ "QtQuick", "Window", "title");
+ manager->registerPropertyPass(titleValidatorPass,
+ "QtQuick.Controls", "Dialog", "title");
+ \endcode
+
+ \note Running analysis passes on too many items can be expensive. This is
+ why it is generally good to filter down the set of properties of a pass
+ using the \a moduleName, \a typeName and \a propertyName.
+
+ Returns \c true if the pass was successfully added, \c false otherwise.
+ Adding a pass fails when the \l{QQmlSA::Element}{Element} specified by
+ \a moduleName and \a typeName does not exist.
+
+ \sa PropertyPass
+*/
+bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
+ QAnyStringView moduleName, QAnyStringView typeName,
+ QAnyStringView propertyName, bool allowInheritance)
+{
+ Q_D(PassManager);
+ return d->registerPropertyPass(pass, moduleName, typeName, propertyName, allowInheritance);
+}
+
+bool PassManagerPrivate::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
+ QAnyStringView moduleName, QAnyStringView typeName,
+ QAnyStringView propertyName, bool allowInheritance)
+{
+ if (moduleName.isEmpty() != typeName.isEmpty()) {
+ qWarning() << "Both the moduleName and the typeName must be specified "
+ "for the pass to be registered for a specific element.";
+ }
+
+ QString name;
+ if (!moduleName.isEmpty() && !typeName.isEmpty()) {
+ auto typeImporter = m_visitor->importer();
+ auto module = typeImporter->importModule(moduleName.toString());
+ auto element = QQmlJSScope::createQQmlSAElement(module.type(typeName.toString()).scope);
+
+ if (element.isNull())
+ return false;
+
+ name = lookupName(element, Register);
+ }
+ const QQmlSA::PropertyPassInfo passInfo{ propertyName.isEmpty()
+ ? QStringList{}
+ : QStringList{ propertyName.toString() },
+ std::move(pass), allowInheritance };
+ m_propertyPasses.insert({ name, passInfo });
+
+ return true;
+}
+
+void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope,
+ const QString prefix, bool isAttached)
+{
+ const Element &currentScope = scope.isNull() ? element : scope;
+ const auto ownBindings = currentScope.ownPropertyBindings();
+ for (const auto &binding : ownBindings) {
+ switch (binding.bindingType()) {
+ case QQmlSA::BindingType::GroupProperty:
+ addBindingSourceLocations(element, Element{ binding.groupType() },
+ prefix + binding.propertyName() + u'.');
+ break;
+ case QQmlSA::BindingType::AttachedProperty:
+ addBindingSourceLocations(element, Element{ binding.attachingType() },
+ prefix + binding.propertyName() + u'.', true);
+ break;
+ default:
+ m_bindingsByLocation.insert({ binding.sourceLocation().offset(),
+ BindingInfo{ prefix + binding.propertyName(), binding,
+ currentScope, isAttached } });
+
+ if (binding.bindingType() != QQmlSA::BindingType::Script)
+ analyzeBinding(element, QQmlSA::Element(), binding.sourceLocation());
+ }
+ }
+}
+
+/*!
+ Runs the element passes over \a root and all its children.
+ */
+void PassManager::analyze(const Element &root)
+{
+ Q_D(PassManager);
+ d->analyze(root);
+}
+
+static QQmlJS::ConstPtrWrapperIterator childScopesBegin(const Element &element)
+{
+ return QQmlJSScope::scope(element)->childScopesBegin();
+}
+
+static QQmlJS::ConstPtrWrapperIterator childScopesEnd(const Element &element)
+{
+ return QQmlJSScope::scope(element)->childScopesEnd();
+}
+
+void PassManagerPrivate::analyze(const Element &root)
+{
+ QList<Element> runStack;
+ runStack.push_back(root);
+ while (!runStack.isEmpty()) {
+ auto element = runStack.takeLast();
+ addBindingSourceLocations(element);
+ for (auto &elementPass : m_elementPasses)
+ if (elementPass->shouldRun(element))
+ elementPass->run(element);
+
+ for (auto it = childScopesBegin(element), end = childScopesEnd(element); it != end; ++it) {
+ if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope)
+ runStack.push_back(QQmlJSScope::createQQmlSAElement(*it));
+ }
+ }
+}
+
+void PassManagerPrivate::analyzeWrite(const Element &element, QString propertyName,
+ const Element &value, const Element &writeScope,
+ QQmlSA::SourceLocation location)
+{
+ for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
+ pass->onWrite(element, propertyName, value, writeScope, location);
+}
+
+void PassManagerPrivate::analyzeRead(const Element &element, QString propertyName,
+ const Element &readScope, QQmlSA::SourceLocation location)
+{
+ for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
+ pass->onRead(element, propertyName, readScope, location);
+}
+
+void PassManagerPrivate::analyzeBinding(const Element &element, const QQmlSA::Element &value,
+ QQmlSA::SourceLocation location)
+{
+ const auto info = m_bindingsByLocation.find(location.offset());
+
+ // If there's no matching binding that means we're in a nested Ret somewhere inside an
+ // expression
+ if (info == m_bindingsByLocation.end())
+ return;
+
+ const QQmlSA::Element &bindingScope = info->second.bindingScope;
+ const QQmlSA::Binding &binding = info->second.binding;
+ const QString &propertyName = info->second.fullPropertyName;
+
+ for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
+ pass->onBinding(element, propertyName, binding, bindingScope, value);
+
+ if (!info->second.isAttached || bindingScope.baseType().isNull())
+ return;
+
+ for (PropertyPass *pass : findPropertyUsePasses(bindingScope.baseType(), propertyName))
+ pass->onBinding(element, propertyName, binding, bindingScope, value);
+}
+
+/*!
+ Returns \c true if the module named \a module has been imported by the
+ QML to be analyzed, \c false otherwise.
+
+ This can be used to skip registering a pass which is specific to a specific
+ module.
+
+ \code
+ if (passManager->hasImportedModule("QtPositioning"))
+ passManager->registerElementPass(
+ std::make_unique<PositioningPass>(passManager)
+ );
+ \endcode
+
+ \sa registerPropertyPass(), registerElementPass()
+ */
+bool PassManager::hasImportedModule(QAnyStringView module) const
+{
+ return PassManagerPrivate::visitor(*this)->imports().hasType(u"$module$." + module.toString());
+}
+
+/*!
+ Returns \c true if warnings of \a category are enabled, \c false otherwise.
+ */
+bool PassManager::isCategoryEnabled(LoggerWarningId category) const
+{
+ return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(category);
+}
+
+QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager)
+{
+ return manager.d_func()->m_visitor;
+}
+
+QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager)
+{
+ return manager.d_func()->m_typeResolver;
+}
+
+QSet<PropertyPass *> PassManagerPrivate::findPropertyUsePasses(const QQmlSA::Element &element,
+ const QString &propertyName)
+{
+ QStringList typeNames { lookupName(element) };
+
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ QQmlJSScope::scope(element),
+ [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
+ Q_UNUSED(mode);
+ typeNames.append(lookupName(QQmlJSScope::createQQmlSAElement(scope)));
+ return false;
+ });
+
+ QSet<PropertyPass *> passes;
+
+ for (const QString &typeName : typeNames) {
+ for (auto &pass :
+ { m_propertyPasses.equal_range(u""_s), m_propertyPasses.equal_range(typeName) }) {
+ if (pass.first == pass.second)
+ continue;
+
+ for (auto it = pass.first; it != pass.second; it++) {
+ if (typeName != typeNames.constFirst() && !it->second.allowInheritance)
+ continue;
+ if (it->second.properties.isEmpty()
+ || it->second.properties.contains(propertyName)) {
+ passes.insert(it->second.pass.get());
+ }
+ }
+ }
+ }
+ return passes;
+}
+
+void DebugElementPass::run(const Element &element) {
+ emitWarning(u"Type: " + element.baseTypeName(), qmlPlugin);
+ if (auto bindings = element.propertyBindings(u"objectName"_s); !bindings.isEmpty()) {
+ emitWarning(u"is named: " + bindings.first().stringValue(), qmlPlugin);
+ }
+ if (auto defPropName = element.defaultPropertyName(); !defPropName.isEmpty()) {
+ emitWarning(u"binding " + QString::number(element.propertyBindings(defPropName).size())
+ + u" elements to property "_s + defPropName,
+ qmlPlugin);
+ }
+}
+
+/*!
+ \class QQmlSA::LintPlugin
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis plugins.
+ */
+
+/*!
+ \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement)
+
+ Adds a pass \a manager that will be executed on \a rootElement.
+ */
+
+/*!
+ \class QQmlSA::ElementPass
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis passes on elements.
+
+ ElementPass is the simpler of the two analysis passes. It will consider every element in
+ a file. The \l shouldRun() method can be used to filter out irrelevant elements, and the
+ \l run() method is doing the initial work.
+
+ Common tasks suitable for an ElementPass are
+ \list
+ \li checking that properties of an Element are not combined in a nonsensical way
+ \li validating property values (e.g. that a property takes only certain enum values)
+ \li checking behavior dependent on an Element's parent (e.g. not using \l {Item::width}
+ when the parent element is a \c Layout).
+ \endlist
+
+ As shown in the snippet below, it is recommended to do necessary type resolution in the
+ constructor of the ElementPass and cache it in local members, and to implement some
+ filtering via \l shouldRun() to keep the static analysis performant.
+
+ \code
+ using namespace QQmlSA;
+ class MyElementPass : public ElementPass
+ {
+ Element myType;
+ public:
+ MyElementPass(QQmlSA::PassManager *manager)
+ : myType(resolveType("MyModule", "MyType")) {}
+
+ bool shouldRun(const Element &element) override
+ {
+ return element.inherits(myType);
+ }
+ void run(const Element &element) override
+ {
+ // actual pass logic
+ }
+ }
+ \endcode
+
+ ElementPasses have limited insight into how an element's properties are used. If you need
+ that information, consider using a \l PropertyPass instead.
+
+ \note ElementPass will only ever consider instantiable types. Therefore, it is unsuitable
+ to analyze attached types and singletons. Those need to be handled via a PropertyPass.
+ */
+
+/*!
+ \fn void QQmlSA::ElementPass::run(const Element &element)
+
+ Executes if \c shouldRun() returns \c true. Performs the real computation
+ of the pass on \a element.
+ This method is meant to be overridden. Calling the base method is not
+ necessary.
+ */
+
+/*!
+ Controls whether the \c run() function should be executed on the given \a element.
+ Subclasses can override this method to improve performance of the analysis by
+ filtering out elements which are not relevant.
+
+ The default implementation unconditionally returns \c true.
+ */
+bool ElementPass::shouldRun(const Element &element)
+{
+ (void)element;
+ return true;
+}
+
+/*!
+ \class QQmlSA::PropertyPass
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis passes on properties.
+ */
+
+
+PropertyPass::PropertyPass(PassManager *manager) : GenericPass(manager) { }
+/*!
+ Executes whenever a property gets bound to a value.
+
+ The property \a propertyName of \a element is bound to the \a value within
+ \a bindingScope with \a binding.
+ */
+void PropertyPass::onBinding(const Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding, const Element &bindingScope,
+ const Element &value)
+{
+ Q_UNUSED(element);
+ Q_UNUSED(propertyName);
+ Q_UNUSED(binding);
+ Q_UNUSED(bindingScope);
+ Q_UNUSED(value);
+}
+
+/*!
+ Executes whenever a property is read.
+
+ The property \a propertyName of \a element is read by an instruction within
+ \a readScope defined at \a location.
+ */
+void PropertyPass::onRead(const Element &element, const QString &propertyName,
+ const Element &readScope, QQmlSA::SourceLocation location)
+{
+ Q_UNUSED(element);
+ Q_UNUSED(propertyName);
+ Q_UNUSED(readScope);
+ Q_UNUSED(location);
+}
+
+/*!
+ Executes whenever a property is written to.
+
+ The property \a propertyName of \a element is written to by an instruction
+ within \a writeScope defined at \a location. The type of the expression
+ written to \a propertyName is \a expressionType.
+ */
+void PropertyPass::onWrite(const Element &element, const QString &propertyName,
+ const Element &expressionType, const Element &writeScope,
+ QQmlSA::SourceLocation location)
+{
+ Q_UNUSED(element);
+ Q_UNUSED(propertyName);
+ Q_UNUSED(writeScope);
+ Q_UNUSED(expressionType);
+ Q_UNUSED(location);
+}
+
+DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager)
+{
+}
+
+void DebugPropertyPass::onRead(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location)
+{
+ emitWarning(u"onRead "_s
+ + (QQmlJSScope::scope(element)->internalName().isEmpty()
+ ? element.baseTypeName()
+ : QQmlJSScope::scope(element)->internalName())
+ + u' ' + propertyName + u' ' + QQmlJSScope::scope(readScope)->internalName()
+ + u' ' + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
+ qmlPlugin, location);
+}
+
+void DebugPropertyPass::onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding,
+ const QQmlSA::Element &bindingScope, const QQmlSA::Element &value)
+{
+ const auto location = QQmlSA::SourceLocation{ binding.sourceLocation() };
+ emitWarning(u"onBinding element: '"_s
+ + (QQmlJSScope::scope(element)->internalName().isEmpty()
+ ? element.baseTypeName()
+ : QQmlJSScope::scope(element)->internalName())
+ + u"' property: '"_s + propertyName + u"' value: '"_s
+ + (value.isNull() ? u"NULL"_s
+ : (QQmlJSScope::scope(value)->internalName().isNull()
+ ? value.baseTypeName()
+ : QQmlJSScope::scope(value)->internalName()))
+ + u"' binding_scope: '"_s
+ + (QQmlJSScope::scope(bindingScope)->internalName().isEmpty()
+ ? bindingScope.baseTypeName()
+ : QQmlJSScope::scope(bindingScope)->internalName())
+ + u"' "_s + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
+ qmlPlugin, location);
+}
+
+void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
+ QQmlSA::SourceLocation location)
+{
+ emitWarning(u"onWrite "_s + element.baseTypeName() + u' ' + propertyName + u' '
+ + QQmlJSScope::scope(value)->internalName() + u' '
+ + QQmlJSScope::scope(writeScope)->internalName() + u' '
+ + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
+ qmlPlugin, location);
+}
+
+/*!
+ Returns the list of element passes.
+ */
+std::vector<std::shared_ptr<ElementPass>> PassManager::elementPasses() const
+{
+ Q_D(const PassManager);
+ return d->m_elementPasses;
+}
+
+/*!
+ Returns the list of property passes.
+ */
+std::multimap<QString, PropertyPassInfo> PassManager::propertyPasses() const
+{
+ Q_D(const PassManager);
+ return d->m_propertyPasses;
+}
+
+/*!
+ Returns bindings by their source location.
+ */
+std::unordered_map<quint32, BindingInfo> PassManager::bindingsByLocation() const
+{
+ Q_D(const PassManager);
+ return d->m_bindingsByLocation;
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { }
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, const QString &fixDescription,
+ const QQmlSA::SourceLocation &location,
+ const QString &replacement)
+ : m_fixSuggestion{ fixDescription, QQmlSA::SourceLocationPrivate::sourceLocation(location),
+ replacement },
+ q_ptr{ interface }
+{
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface,
+ const FixSuggestionPrivate &other)
+ : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface }
+{
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other)
+ : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface }
+{
+}
+
+QString FixSuggestionPrivate::fixDescription() const
+{
+ return m_fixSuggestion.fixDescription();
+}
+
+QQmlSA::SourceLocation FixSuggestionPrivate::location() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_fixSuggestion.location());
+}
+
+QString FixSuggestionPrivate::replacement() const
+{
+ return m_fixSuggestion.replacement();
+}
+
+void FixSuggestionPrivate::setFileName(const QString &fileName)
+{
+ m_fixSuggestion.setFilename(fileName);
+}
+
+QString FixSuggestionPrivate::fileName() const
+{
+ return m_fixSuggestion.filename();
+}
+
+void FixSuggestionPrivate::setHint(const QString &hint)
+{
+ m_fixSuggestion.setHint(hint);
+}
+
+QString FixSuggestionPrivate::hint() const
+{
+ return m_fixSuggestion.hint();
+}
+
+void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable)
+{
+ m_fixSuggestion.setAutoApplicable(autoApplicable);
+}
+
+bool FixSuggestionPrivate::isAutoApplicable() const
+{
+ return m_fixSuggestion.isAutoApplicable();
+}
+
+QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion)
+{
+ return saFixSuggestion.d_func()->m_fixSuggestion;
+}
+
+const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion)
+{
+ return saFixSuggestion.d_func()->m_fixSuggestion;
+}
+
+/*!
+ \class QQmlSA::FixSuggestion
+ \inmodule QtQmlCompiler
+
+ \brief Represents a suggested fix for an issue in the source code.
+ */
+
+
+FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location,
+ const QString &replacement)
+ : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } }
+{
+}
+
+FixSuggestion::FixSuggestion(const FixSuggestion &other)
+ : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } }
+{
+}
+
+FixSuggestion::FixSuggestion(FixSuggestion &&other) noexcept
+ : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } }
+{
+}
+
+FixSuggestion &FixSuggestion::operator=(const FixSuggestion &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_fixSuggestion = other.d_func()->m_fixSuggestion;
+ return *this;
+}
+
+FixSuggestion &FixSuggestion::operator=(FixSuggestion &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_fixSuggestion = std::move(other.d_func()->m_fixSuggestion);
+ return *this;
+}
+
+FixSuggestion::~FixSuggestion() = default;
+
+/*!
+ Returns the description of the fix.
+ */
+QString QQmlSA::FixSuggestion::fixDescription() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).fixDescription();
+}
+
+/*!
+ Returns the location where the fix would be applied.
+ */
+QQmlSA::SourceLocation FixSuggestion::location() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ FixSuggestionPrivate::fixSuggestion(*this).location());
+}
+
+/*!
+ Returns the fix that will replace the problematic source code.
+ */
+QString FixSuggestion::replacement() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).replacement();
+}
+
+/*!
+ Sets \a fileName as the name of the file where this fix suggestion applies.
+ */
+void FixSuggestion::setFileName(const QString &fileName)
+{
+ FixSuggestionPrivate::fixSuggestion(*this).setFilename(fileName);
+}
+
+/*!
+ Returns the name of the file where this fix suggestion applies.
+ */
+QString FixSuggestion::fileName() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).filename();
+}
+
+/*!
+ Sets \a hint as the hint for this fix suggestion.
+ */
+void FixSuggestion::setHint(const QString &hint)
+{
+ FixSuggestionPrivate::fixSuggestion(*this).setHint(hint);
+}
+
+/*!
+ Returns the hint for this fix suggestion.
+ */
+QString FixSuggestion::hint() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).hint();
+}
+
+/*!
+ Sets \a autoApplicable to determine whether this suggested fix can be
+ applied automatically.
+ */
+void FixSuggestion::setAutoApplicable(bool autoApplicable)
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).setAutoApplicable(autoApplicable);
+}
+
+/*!
+ Returns whether this suggested fix can be applied automatically.
+ */
+bool QQmlSA::FixSuggestion::isAutoApplicable() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).isAutoApplicable();
+}
+
+bool FixSuggestion::operatorEqualsImpl(const FixSuggestion &lhs, const FixSuggestion &rhs)
+{
+ return lhs.d_func()->m_fixSuggestion == rhs.d_func()->m_fixSuggestion;
+}
+
+} // namespace QQmlSA
+
+QT_END_NAMESPACE