aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2022-04-26 12:50:08 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2022-04-27 15:29:24 +0200
commit3265013769b566039436358ae7762916ffa88628 (patch)
treeec318358aafc7d7db42c0c9d468ad6cfd24b53f7
parent4b3e99b9a28b0641265a4afed720e5359e35ffda (diff)
QmlLintQuickPlugin: Warn about attached types used in wrong elements
The engine will warn when various attached types are used in elements where they are not supported. This patch replicates this behavior in qmllint with the exception that we cannot handle them being used in bindings right now. Task-number: QTBUG-102277 Task-number: QTBUG-102859 Change-Id: Ic41c9338d8625c5185dbd658cc8987f3c00f18c3 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.cpp96
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.h26
-rw-r--r--tests/auto/qml/qmllint/data/pluginQuick_attached.qml19
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp12
4 files changed, 151 insertions, 2 deletions
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp
index 9d9e4e1350..b2248d3896 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.cpp
+++ b/src/plugins/qmllint/quick/quicklintplugin.cpp
@@ -76,6 +76,60 @@ void ForbiddenChildrenPropertyValidatorPass::run(const QQmlSA::Element &element)
}
}
+AttachedPropertyTypeValidatorPass::AttachedPropertyTypeValidatorPass(QQmlSA::PassManager *manager)
+ : QQmlSA::ElementPass(manager)
+{
+}
+
+void AttachedPropertyTypeValidatorPass::addWarning(
+ QAnyStringView attachedTypeName,
+ QList<AttachedPropertyTypeValidatorPass::TypeDescription> allowedTypes,
+ QAnyStringView warning)
+{
+ AttachedPropertyTypeValidatorPass::Warning warningInfo;
+ warningInfo.message = warning.toString();
+
+ for (const TypeDescription &description : allowedTypes) {
+ auto type = resolveType(description.module, description.name);
+
+ if (type.isNull())
+ continue;
+
+ warningInfo.allowedTypes.push_back(type);
+ }
+
+ m_attachedTypes[attachedTypeName.toString()] = warningInfo;
+}
+
+bool AttachedPropertyTypeValidatorPass::shouldRun(const QQmlSA::Element &element)
+{
+ for (const auto &pair : m_attachedTypes.asKeyValueRange()) {
+ if (element->hasOwnPropertyBindings(pair.first))
+ return true;
+ }
+
+ return false;
+}
+
+void AttachedPropertyTypeValidatorPass::run(const QQmlSA::Element &element)
+{
+ for (const auto &pair : m_attachedTypes.asKeyValueRange()) {
+ if (element->hasOwnPropertyBindings(pair.first)) {
+ bool hasAllowedType = false;
+ for (const QQmlSA::Element &type : pair.second.allowedTypes) {
+ if (element->inherits(type)) {
+ hasAllowedType = true;
+ break;
+ }
+ }
+ if (!hasAllowedType) {
+ auto binding = *element->ownPropertyBindings(pair.first).first;
+ emitWarning(pair.second.message, binding.sourceLocation());
+ }
+ }
+ }
+}
+
ControlsNativeValidatorPass::ControlsNativeValidatorPass(QQmlSA::PassManager *manager)
: QQmlSA::ElementPass(manager)
{
@@ -230,9 +284,14 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
const QQmlSA::Element &rootElement)
{
+ const bool hasQuick = manager->hasImportedModule("QtQuick");
+ const bool hasQuickLayouts = manager->hasImportedModule("QtQuick.Layouts");
+ const bool hasQuickControls = manager->hasImportedModule("QtQuick.Templates")
+ || manager->hasImportedModule("QtQuick.Controls");
+
Q_UNUSED(rootElement);
- if (manager->hasImportedModule("QtQuick")) {
+ if (hasQuick) {
manager->registerElementPass(std::make_unique<AnchorsValidatorPass>(manager));
auto forbiddenChildProperty =
@@ -247,7 +306,7 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
}
}
- if (manager->hasImportedModule("QtQuick.Layouts")) {
+ if (hasQuickLayouts) {
forbiddenChildProperty->addWarning(
"QtQuick.Layouts", "Layout", "anchors",
"Detected anchors on an item that is managed by a layout. This is undefined "
@@ -273,6 +332,39 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
manager->registerElementPass(std::move(forbiddenChildProperty));
}
+ auto attachedPropertyType = std::make_unique<AttachedPropertyTypeValidatorPass>(manager);
+
+ if (hasQuick) {
+ attachedPropertyType->addWarning("Accessible", { { "QtQuick", "Item" } },
+ "Accessible must be attached to an Item");
+ attachedPropertyType->addWarning(
+ "LayoutMirroring", { { "QtQuick", "Item" }, { "QtQuick", "Window" } },
+ "LayoutDirection attached property only works with Items and Windows");
+ attachedPropertyType->addWarning("EnterKey", { { "QtQuick", "Item" } },
+ "EnterKey attached property only works with Items");
+ }
+
+ if (hasQuickLayouts) {
+ attachedPropertyType->addWarning("Layout", { { "QtQuick", "Item" } },
+ "Layout must be attached to Item elements");
+ }
+
+ if (hasQuickControls) {
+ attachedPropertyType->addWarning(
+ "ScrollBar", { { "QtQuick", "Flickable" }, { "QtQuick.Templates", "ScrollView" } },
+ "ScrollBar must be attached to a Flickable or ScrollView");
+ attachedPropertyType->addWarning("ScrollIndicator", { { "QtQuick", "Flickable" } },
+ "ScrollIndicator must be attached to a Flickable");
+ attachedPropertyType->addWarning("SplitView", { { "QtQuick", "Item" } },
+ "SplitView attached property only works with Items");
+ attachedPropertyType->addWarning("StackView", { { "QtQuick", "Item" } },
+ "StackView attached property only works with Items");
+ attachedPropertyType->addWarning("ToolTip", { { "QtQuick", "Item" } },
+ "ToolTip must be attached to an Item");
+ }
+
+ manager->registerElementPass(std::move(attachedPropertyType));
+
if (manager->hasImportedModule(u"QtQuick.Controls.macOS"_qs)
|| manager->hasImportedModule(u"QtQuick.Controls.Windows"_qs))
manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
diff --git a/src/plugins/qmllint/quick/quicklintplugin.h b/src/plugins/qmllint/quick/quicklintplugin.h
index 322c3a0332..a1c5218e7a 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.h
+++ b/src/plugins/qmllint/quick/quicklintplugin.h
@@ -66,6 +66,32 @@ private:
QHash<QQmlSA::Element, QVarLengthArray<Warning, 8>> m_types;
};
+class AttachedPropertyTypeValidatorPass : public QQmlSA::ElementPass
+{
+public:
+ struct TypeDescription
+ {
+ QString module;
+ QString name;
+ };
+
+ AttachedPropertyTypeValidatorPass(QQmlSA::PassManager *manager);
+
+ void addWarning(QAnyStringView attachedTypeName, QList<TypeDescription> allowedTypes,
+ QAnyStringView warning);
+
+ bool shouldRun(const QQmlSA::Element &element) override;
+ void run(const QQmlSA::Element &element) override;
+
+private:
+ struct Warning
+ {
+ QVarLengthArray<QQmlSA::Element, 4> allowedTypes;
+ QString message;
+ };
+ QHash<QString, Warning> m_attachedTypes;
+};
+
class ControlsNativeValidatorPass : public QQmlSA::ElementPass
{
public:
diff --git a/tests/auto/qml/qmllint/data/pluginQuick_attached.qml b/tests/auto/qml/qmllint/data/pluginQuick_attached.qml
new file mode 100644
index 0000000000..6474ff3ddb
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/pluginQuick_attached.qml
@@ -0,0 +1,19 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+QtObject {
+ // QtQuick
+ Accessible.name: "Foo"
+ LayoutMirroring.enabled: true
+ EnterKey.type: Qt.EnterKeyGo
+
+ // QtQuick.Layouts
+ Layout.minimumHeight: 3
+
+ // QtQuick.Templates
+ ScrollBar.vertical: ScrollBar {}
+ ScrollIndicator.vertical: ScrollIndicator {}
+ SplitView.fillWidth: true
+ StackView.visible: true
+ ToolTip.delay: 50
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 82f4deb0b3..7844e63d27 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1696,6 +1696,18 @@ void TestQmllint::quickPlugin()
u"Cannot specify x for items inside Flow. Flow will not function."_qs },
Message {
u"Cannot specify y for items inside Flow. Flow will not function."_qs } } });
+ runTest("pluginQuick_attached.qml",
+ Result {
+ { Message { u"ToolTip must be attached to an Item"_qs },
+ Message { u"SplitView attached property only works with Items"_qs },
+ Message { u"ScrollIndicator must be attached to a Flickable"_qs },
+ Message { u"ScrollBar must be attached to a Flickable or ScrollView"_qs },
+ Message { u"Accessible must be attached to an Item"_qs },
+ Message { u"EnterKey attached property only works with Items"_qs },
+ Message {
+ u"LayoutDirection attached property only works with Items and Windows"_qs },
+ Message { u"Layout must be attached to Item elements"_qs },
+ Message { u"StackView attached property only works with Items"_qs } } });
}
#endif