aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/qmllint/quick/plugin.json5
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.cpp53
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.h12
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp4
-rw-r--r--src/qmlcompiler/qqmlsa.cpp10
-rw-r--r--src/qmlcompiler/qqmlsa_p.h4
-rw-r--r--tests/auto/qml/qmllint/data/customParser.qml4
-rw-r--r--tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml19
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp13
9 files changed, 121 insertions, 3 deletions
diff --git a/src/plugins/qmllint/quick/plugin.json b/src/plugins/qmllint/quick/plugin.json
index 442a693b7b..527e6002b4 100644
--- a/src/plugins/qmllint/quick/plugin.json
+++ b/src/plugins/qmllint/quick/plugin.json
@@ -29,6 +29,11 @@
"name": "unexpected-var-type",
"settingsName": "UnexpectedVarType",
"description": "Warn about incompatible types being set on var properties"
+ },
+ {
+ "name": "property-changes-parsed",
+ "settingsName": "PropertyChangesParsed",
+ "description": "Warn about legacy PropertyChanges that rely on the custom parser"
}
]
}
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp
index 3b89635748..f040d57849 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.cpp
+++ b/src/plugins/qmllint/quick/quicklintplugin.cpp
@@ -4,6 +4,7 @@
#include "quicklintplugin.h"
#include <QtQmlCompiler/private/qqmljslogger_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
QT_BEGIN_NAMESPACE
@@ -14,6 +15,7 @@ static constexpr LoggerWarningId quickAttachedPropertyType { "Quick.attached-pro
static constexpr LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" };
static constexpr LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" };
static constexpr LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" };
+static constexpr LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" };
ForbiddenChildrenPropertyValidatorPass::ForbiddenChildrenPropertyValidatorPass(
QQmlSA::PassManager *manager)
@@ -520,6 +522,7 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
if (hasQuick) {
manager->registerElementPass(std::make_unique<AnchorsValidatorPass>(manager));
+ manager->registerElementPass(std::make_unique<PropertyChangesValidatorPass>(manager));
auto forbiddenChildProperty =
std::make_unique<ForbiddenChildrenPropertyValidatorPass>(manager);
@@ -636,6 +639,56 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
}
+PropertyChangesValidatorPass::PropertyChangesValidatorPass(QQmlSA::PassManager *manager)
+ : QQmlSA::ElementPass(manager)
+{
+ m_propertyChanges = resolveType("QtQuick", "PropertyChanges");
+}
+
+bool PropertyChangesValidatorPass::shouldRun(const QQmlSA::Element &element)
+{
+ return !m_propertyChanges.isNull() && element->inherits(m_propertyChanges);
+}
+
+void PropertyChangesValidatorPass::run(const QQmlSA::Element &element)
+{
+ const QMultiHash<QString, QQmlJSMetaPropertyBinding> bindings = element->ownPropertyBindings();
+
+ const auto target = bindings.find(u"target"_s);
+ if (target == bindings.end())
+ return;
+
+ QString targetId = u"<id>"_s;
+ const QString targetBinding = sourceCode(target->sourceLocation());
+ const QQmlSA::Element targetElement = resolveId(targetBinding, element);
+ if (!targetElement.isNull())
+ targetId = targetBinding;
+
+ for (auto it = bindings.begin(), end = bindings.end(); it != end; ++it) {
+ const QString name = it->propertyName();
+ if (element->hasProperty(name))
+ continue;
+
+ const QQmlJS::SourceLocation bindingLocation = it->sourceLocation();
+ if (!targetElement->hasProperty(name)) {
+ emitWarning(
+ "Unknown property \"%1\" in PropertyChanges."_L1.arg(name),
+ quickPropertyChangesParsed, bindingLocation);
+ continue;
+ }
+
+ QString binding = sourceCode(bindingLocation);
+ if (binding.length() > 16)
+ binding = binding.left(13) + "..."_L1;
+
+ emitWarning(
+ "Property \"%1\" is custom-parsed in PropertyChanges. "
+ "You should phrase this binding as \"%2.%1: %3\""_L1
+ .arg(name, targetId, binding),
+ quickPropertyChangesParsed, bindingLocation);
+ }
+}
+
QT_END_NAMESPACE
#include "moc_quicklintplugin.cpp"
diff --git a/src/plugins/qmllint/quick/quicklintplugin.h b/src/plugins/qmllint/quick/quicklintplugin.h
index be368f0880..9db2fff5cf 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.h
+++ b/src/plugins/qmllint/quick/quicklintplugin.h
@@ -139,6 +139,18 @@ private:
QMultiHash<QString, QQmlSA::Element> m_expectedPropertyTypes;
};
+class PropertyChangesValidatorPass : public QQmlSA::ElementPass
+{
+public:
+ PropertyChangesValidatorPass(QQmlSA::PassManager *manager);
+
+ bool shouldRun(const QQmlSA::Element &element) override;
+ void run(const QQmlSA::Element &element) override;
+
+private:
+ QQmlSA::Element m_propertyChanges;
+};
+
QT_END_NAMESPACE
#endif // QUICKLINTPLUGIN_H
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 48c4bcbeef..0f79034bc6 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1773,7 +1773,9 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name,
}
auto expr = exprStatement->expression;
- QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
+ QQmlJSMetaPropertyBinding binding(
+ combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
+ name);
bool isUndefinedBinding = false;
diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp
index 0b6e88ef7c..9dde74ad86 100644
--- a/src/qmlcompiler/qqmlsa.cpp
+++ b/src/qmlcompiler/qqmlsa.cpp
@@ -49,6 +49,16 @@ Element GenericPass::resolveLiteralType(const QQmlJSMetaPropertyBinding &binding
return binding.literalType(d->manager->m_typeResolver);
}
+Element GenericPass::resolveId(QAnyStringView id, const Element &context)
+{
+ return d->manager->m_typeResolver->scopeForId(id.toString(), context);
+}
+
+QString GenericPass::sourceCode(QQmlJS::SourceLocation location)
+{
+ return d->manager->m_visitor->logger()->code().mid(location.offset, location.length);
+}
+
/*!
* \brief PassManager::registerElementPass registers ElementPass
with the pass manager.
diff --git a/src/qmlcompiler/qqmlsa_p.h b/src/qmlcompiler/qqmlsa_p.h
index 67b6995b54..e43ba9ed4e 100644
--- a/src/qmlcompiler/qqmlsa_p.h
+++ b/src/qmlcompiler/qqmlsa_p.h
@@ -50,6 +50,10 @@ public:
QQmlJS::SourceLocation srcLocation = QQmlJS::SourceLocation());
Element resolveType(QAnyStringView moduleName, QAnyStringView typeName); // #### TODO: revisions
Element resolveLiteralType(const QQmlJSMetaPropertyBinding &binding);
+ Element resolveId(QAnyStringView id, const Element &context);
+
+ QString sourceCode(QQmlJS::SourceLocation location);
+
private:
std::unique_ptr<GenericPassPrivate> d; // PIMPL might be overkill
diff --git a/tests/auto/qml/qmllint/data/customParser.qml b/tests/auto/qml/qmllint/data/customParser.qml
index a83ae7e823..324ca20953 100644
--- a/tests/auto/qml/qmllint/data/customParser.qml
+++ b/tests/auto/qml/qmllint/data/customParser.qml
@@ -7,11 +7,11 @@ Rectangle {
states: [
State {
name: "red_color"
- PropertyChanges { target: root; color: "red" }
+ PropertyChanges { root.color: "red" }
},
State {
name: "blue_color"
- PropertyChanges { target: root; color: "blue" }
+ PropertyChanges { root.color: "blue" }
}
]
}
diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml
new file mode 100644
index 0000000000..94873463f1
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Window {
+ Item {
+ id: foo
+ property color myColor: 'black'
+
+ states: [
+ State {
+ PropertyChanges {
+ target: foo
+ myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16)
+ notThere: "a"
+ }
+ }
+ ]
+
+ }
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index a5e906ce9a..498a3fea84 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1885,6 +1885,19 @@ void TestQmllint::quickPlugin()
runTest("pluginQuick_attachedClean.qml", Result::clean());
runTest("pluginQuick_attachedIgnore.qml", Result::clean());
runTest("pluginQuick_noCrashOnUneresolved.qml", Result {}); // we don't care about the specific warnings
+
+ runTest("pluginQuick_propertyChangesParsed.qml",
+ Result { {
+ Message {
+ u"Property \"myColor\" is custom-parsed in PropertyChanges. "
+ "You should phrase this binding as \"foo.myColor: Qt.rgba(0.5, ...\""_s,
+ 12, 30
+ },
+ Message {
+ u"Unknown property \"notThere\" in PropertyChanges."_s,
+ 13, 31
+ }
+ } });
}
#endif