145 lines
5.4 KiB
C++
145 lines
5.4 KiB
C++
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
#include "lintplugin.h"
|
|
#include <QtQmlCompiler/private/qqmlsa_p.h>
|
|
#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
static constexpr QQmlSA::LoggerWarningId plugin{ "testPlugin.test" };
|
|
|
|
class ElementTest : public QQmlSA::ElementPass
|
|
{
|
|
public:
|
|
ElementTest(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager)
|
|
{
|
|
m_rectangle = resolveType(u"QtQuick", u"Rectangle");
|
|
}
|
|
|
|
bool shouldRun(const QQmlSA::Element &element) override
|
|
{
|
|
return element.baseType() == m_rectangle;
|
|
}
|
|
|
|
void run(const QQmlSA::Element &element) override
|
|
{
|
|
auto property = element.property(u"radius"_s);
|
|
if (!property.isValid() || element.property(u"radius"_s).typeName() != u"double") {
|
|
emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation());
|
|
return;
|
|
}
|
|
|
|
auto bindings = element.propertyBindings(u"radius"_s);
|
|
if (bindings.isEmpty() || bindings.constFirst().numberValue() != 5) {
|
|
emitWarning(u"Failed to verify radius property binding", plugin,
|
|
element.sourceLocation());
|
|
return;
|
|
}
|
|
|
|
emitWarning(u"ElementTest OK", plugin, element.sourceLocation());
|
|
}
|
|
|
|
private:
|
|
QQmlSA::Element m_rectangle;
|
|
};
|
|
|
|
class PropertyTest : public QQmlSA::PropertyPass
|
|
{
|
|
public:
|
|
PropertyTest(QQmlSA::PassManager *manager)
|
|
: QQmlSA::PropertyPass(manager)
|
|
, m_manager(manager) { }
|
|
|
|
void onBinding(const QQmlSA::Element &element, const QString &propertyName,
|
|
const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
|
|
const QQmlSA::Element &value) override
|
|
{
|
|
auto resolver = QQmlSA::PassManagerPrivate::resolver(*m_manager);
|
|
if (resolver->jsGlobalObject().isNull()) {
|
|
emitWarning(u"Type resolution is broken", plugin);
|
|
return;
|
|
}
|
|
emitWarning(u"Saw binding on %1 property %2 with value %3 (and type %4) in scope %5"_s
|
|
.arg(element.baseTypeName(), propertyName,
|
|
value.isNull()
|
|
? u"NULL"_s
|
|
: (value.name().isNull() ? value.baseTypeName()
|
|
: value.name()))
|
|
.arg(qToUnderlying(binding.bindingType()))
|
|
.arg(bindingScope.baseTypeName()),
|
|
plugin, bindingScope.sourceLocation());
|
|
}
|
|
|
|
void onRead(const QQmlSA::Element &element, const QString &propertyName,
|
|
const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override
|
|
{
|
|
emitWarning(u"Saw read on %1 property %2 in scope %3"_s.arg(
|
|
element.baseTypeName(), propertyName, readScope.baseTypeName()),
|
|
plugin, location);
|
|
}
|
|
|
|
void onWrite(const QQmlSA::Element &element, const QString &propertyName,
|
|
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
|
|
QQmlSA::SourceLocation location) override
|
|
{
|
|
emitWarning(u"Saw write on %1 property %2 with value %3 in scope %4"_s.arg(
|
|
element.baseTypeName(), propertyName,
|
|
(value.name().isNull() ? value.baseTypeName()
|
|
: value.name()),
|
|
writeScope.baseTypeName()),
|
|
plugin, location);
|
|
}
|
|
|
|
private:
|
|
QQmlSA::PassManager *m_manager;
|
|
};
|
|
|
|
class HasImportedModuleTest : public QQmlSA::ElementPass
|
|
{
|
|
public:
|
|
HasImportedModuleTest(QQmlSA::PassManager *manager, QString message)
|
|
: QQmlSA::ElementPass(manager), m_message(message)
|
|
{
|
|
}
|
|
|
|
bool shouldRun(const QQmlSA::Element &element) override
|
|
{
|
|
Q_UNUSED(element)
|
|
return true;
|
|
}
|
|
|
|
void run(const QQmlSA::Element &element) override
|
|
{
|
|
Q_UNUSED(element)
|
|
emitWarning(m_message, plugin);
|
|
}
|
|
|
|
private:
|
|
QString m_message;
|
|
};
|
|
|
|
void LintPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
|
|
{
|
|
if (!rootElement.filePath().endsWith(u"_pluginTest.qml"))
|
|
return;
|
|
|
|
manager->registerElementPass(std::make_unique<ElementTest>(manager));
|
|
manager->registerPropertyPass(std::make_unique<PropertyTest>(manager), "QtQuick", "Text",
|
|
"text");
|
|
manager->registerPropertyPass(std::make_unique<PropertyTest>(manager), "", "", "x");
|
|
manager->registerPropertyPass(std::make_unique<PropertyTest>(manager), "", "", "log");
|
|
manager->registerPropertyPass(std::make_unique<PropertyTest>(manager), "QtQuick", "ListView");
|
|
if (manager->hasImportedModule("QtQuick.Controls")) {
|
|
if (manager->hasImportedModule("QtQuick")) {
|
|
if (manager->hasImportedModule("QtQuick.Window")) {
|
|
manager->registerElementPass(std::make_unique<HasImportedModuleTest>(
|
|
manager, "QtQuick.Controls, QtQuick and QtQuick.Window present"));
|
|
}
|
|
} else {
|
|
manager->registerElementPass(std::make_unique<HasImportedModuleTest>(
|
|
manager, "QtQuick.Controls and NO QtQuick present"));
|
|
}
|
|
}
|
|
}
|