aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <[email protected]>2025-04-24 16:30:23 +0200
committerSami Shalayel <[email protected]>2025-04-29 09:46:49 +0200
commita8b0a8aa97cc9215f6f1317d9dbbebabaf3cb791 (patch)
tree0c0cc8e7d88d9180128ee2fad838709f2360b9d0
parentb8300fb5277610d59724a9087fc89f60c166a386 (diff)
qmllint: warn about duplicate property bindings
Warn about duplicate property bindings for object and script bindings. Remove a TODO about doing this task that seemed to be at the wrong place. Fixes: QTBUG-118102 Change-Id: I46696b696f6c7e0c83e36077998d6118b14498ad Reviewed-by: Olivier De Cannière <[email protected]>
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp32
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp46
2 files changed, 75 insertions, 3 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 70555ef4a4..72dac04ccd 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -600,6 +600,9 @@ QVector<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiA
void QQmlJSImportVisitor::setAllBindings()
{
+ using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
+ QHash<Key, QQmlJS::SourceLocation> m_foundBindings;
+
for (auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
// ensure the scope is resolved. If not, produce a warning.
const QQmlJSScope::Ptr type = it->owner;
@@ -607,8 +610,32 @@ void QQmlJSImportVisitor::setAllBindings()
continue;
auto binding = it->create();
- if (binding.isValid())
- type->addOwnPropertyBinding(binding, it->specifier);
+ if (!binding.isValid())
+ continue;
+ type->addOwnPropertyBinding(binding, it->specifier);
+
+ // we already warn about duplicate interceptors in processPropertyBindingObjects()
+ if (binding.hasInterceptor())
+ continue;
+ const QString propertyName = binding.propertyName();
+
+ // list can be bound multiple times
+ if (type->property(propertyName).isList())
+ continue;
+
+ const Key key = std::make_pair(type, propertyName);
+ auto sourceLocationIt = m_foundBindings.constFind(key);
+ if (sourceLocationIt == m_foundBindings.constEnd()) {
+ m_foundBindings.insert(key, binding.sourceLocation());
+ continue;
+ }
+
+ const QQmlJS::SourceLocation location = binding.sourceLocation();
+ m_logger->log("Duplicate binding on property '%1'"_L1.arg(propertyName),
+ qmlDuplicatePropertyBinding, location);
+ m_logger->log("Note: previous binding on '%1' here"_L1.arg(propertyName),
+ qmlDuplicatePropertyBinding, *sourceLocationIt, true, true, {}, {},
+ location.startLine);
}
}
@@ -883,7 +910,6 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
qmlIncompatibleType, objectBinding.location);
}
} else {
- // TODO: Warn here if binding.hasValue() is true
if (foundValueSources.contains(uniqueBindingId)) {
m_logger->log(
QStringLiteral("Cannot combine value source and binding on property \"%1\"")
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 0f9c3e0a32..bb7d985d03 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1401,6 +1401,48 @@ void TestQmllint::dirtyQmlSnippet_data()
{ "Enum keys should start with an uppercase"_L1, 1, 35 } } }
<< defaultOptions;
+ QTest::newRow("duplicateObjectBinding")
+ << u"property Item i; i: Item {} i: Item {}"_s
+ << Result{ { { "Duplicate binding on property 'i'"_L1, 1, 29 },
+ { "Note: previous binding on 'i' here"_L1, 1, 18 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateObjectBinding2")
+ << u"property Item i: Item {} i: Item {}"_s
+ << Result{ { { "Duplicate binding on property 'i'"_L1, 1, 26 },
+ { "Note: previous binding on 'i' here"_L1, 1, 15 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateBinding")
+ << u"property int i; i: 42; i: 43;"_s
+ << Result{ { { "Duplicate binding on property 'i'"_L1, 1, 27 },
+ { "Note: previous binding on 'i' here"_L1, 1, 20 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateBinding2")
+ << u"property int i: 42; i: 43;"_s
+ << Result{ { { "Duplicate binding on property 'i'"_L1, 1, 24 },
+ { "Note: previous binding on 'i' here"_L1, 1, 17 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateGrouped")
+ << u"Text { font.pixelSize: 5; font.pixelSize: 10; }"_s
+ << Result{ { { "Duplicate binding on property 'pixelSize'"_L1, 1, 43 },
+ { "Note: previous binding on 'pixelSize' here"_L1, 1, 24 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateGrouped2")
+ << u"Text { font.pixelSize: 5; font { pixelSize: 10 } }"_s
+ << Result{ { { "Duplicate binding on property 'pixelSize'"_L1, 1, 45 },
+ { "Note: previous binding on 'pixelSize' here"_L1, 1, 24 } } }
+ << defaultOptions;
+
+ QTest::newRow("duplicateGrouped3")
+ << u"Text { font { pixelSize: 5; pixelSize: 10 } }"_s
+ << Result{ { { "Duplicate binding on property 'pixelSize'"_L1, 1, 40 },
+ { "Note: previous binding on 'pixelSize' here"_L1, 1, 26 } } }
+ << defaultOptions;
+
QTest::newRow("color-name") << u"property color myColor: \"lbue\""_s
<< Result{ { { "Invalid color \"lbue\""_L1, 1, 25 } },
{},
@@ -1464,6 +1506,10 @@ void TestQmllint::cleanQmlSnippet_data()
QTest::newRow("testSnippet") << u"property int qwer: 123"_s << defaultOptions;
QTest::newRow("enum") << u"enum Hello { World, Kitty, DlroW }"_s << defaultOptions;
+ QTest::newRow("duplicateList") << u"Item {} Item {}"_s << defaultOptions;
+ QTest::newRow("duplicateList2")
+ << u"property list<Item> myList; myList: Item {} myList: Item {}"_s << defaultOptions;
+
QTest::newRow("color-name") << u"property color myColor: \"blue\""_s << defaultOptions;
QTest::newRow("color-name2") << u"property color myColor\nmyColor: \"green\""_s
<< defaultOptions;