diff options
author | Sami Shalayel <[email protected]> | 2025-04-24 16:30:23 +0200 |
---|---|---|
committer | Sami Shalayel <[email protected]> | 2025-04-29 09:46:49 +0200 |
commit | a8b0a8aa97cc9215f6f1317d9dbbebabaf3cb791 (patch) | |
tree | 0c0cc8e7d88d9180128ee2fad838709f2360b9d0 | |
parent | b8300fb5277610d59724a9087fc89f60c166a386 (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.cpp | 32 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 46 |
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; |