From 8848d58f29342b231e7ffb85067a16d8a53d56dc Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 2 Jun 2025 17:25:40 +0200 Subject: [PATCH] tst_QMetaObject: add a reproducer for overly eager QVector/QList replacement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kludge that was added to argumentTypesFromString() for Qt 6 to support merging QVector into QList—specifically, the replacement of QVector< with QList<—was unfortunately not removed before the Qt 6.0 release. As a kludge, it has unintended consequences, as Thiago pointed out in a comment on a related patch. This change adds a reproducer that demonstrates cases where the kludge causes correct code to fail incorrectly. We need this test to ensure that we do not silently change behavior when deprecating and eventually removing the kludge. Adapt the MyQList normalization test, which was carefully written to avoid hitting the kludge, to use template arguments. This will allow it to trigger the buggy code path and avoid confusing the reader with the mention of a non-template MyQList, given that MyQList is now a template. Reported-by: Thiago Macieira Pick-to: 6.8 6.5 Task-number: QTBUG-135572 Change-Id: I91d769d494489fe63dbbb67f849b78fc7aa39ec6 Reviewed-by: Ahmad Samir (cherry picked from commit a97ac8507e39a421f530f5ad612a7e4bb58f6718) Reviewed-by: Marc Mutz (cherry picked from commit fdc070c3b0bce2d7aa5c568cf45555a1697822d7) Reviewed-by: Qt Cherry-pick Bot --- .../kernel/qmetaobject/tst_qmetaobject.cpp | 70 +++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index b342c9e1a62..4017888d92b 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -14,6 +14,9 @@ #endif #include +#include +#include + Q_DECLARE_METATYPE(const QMetaObject *) #include "forwarddeclared.h" @@ -49,6 +52,28 @@ public: Q_INVOKABLE MyGadget() {} }; +template +class MyQList +{ + std::vector m_data; +public: + MyQList(std::initializer_list il) : m_data{il} {} + + const std::vector &data() const { return m_data; } + std::vector &data() { return m_data; } +}; + +template +class MyQVector +{ + std::deque m_data; +public: + MyQVector(std::initializer_list il) : m_data{il} {} + + const std::deque &data() const { return m_data; } + std::deque &data() { return m_data; } +}; + namespace MyNamespace { // Used in tst_QMetaObject::checkScope class MyClass : public QObject @@ -316,6 +341,7 @@ private slots: void normalizedType_data(); void normalizedType(); void customPropertyType(); + void customQVectorSuffix(); void keysToValue_data(); void keysToValue(); // Also keyToValue() void propertyNotify(); @@ -357,6 +383,8 @@ private slots: signals: void value6Changed(); void value7Changed(const QString &); + void myQListChanged(const MyQList &); + void myQVectorChanged(const MyQVector &); // needs different template arg from MyQList! }; void tst_QMetaObject::stdSet() @@ -2304,8 +2332,11 @@ void tst_QMetaObject::normalizedSignature_data() QTest::newRow("const13") << "void foo(const Foo&)" << "void foo(Foo)"; QTest::newRow("const14") << "void foo(Fooconst&)" << "void foo(Foo)"; QTest::newRow("QVector") << "void foo(QVector)" << "void foo(QList)"; - QTest::newRow("QVector1") << "void foo(const Template)" - << "void foo(Template)"; + QTest::newRow("QVector1") << "void foo(const Template const>)" + << "void foo(Template>)"; + QTest::newRow("MyQVector") << "void foo(MyQVector)" << "void foo(MyQVector)"; + QTest::newRow("MyQVector1") << "void foo(const Template const>)" + << "void foo(Template>)"; QTest::newRow("refref") << "const char* foo(const X &&,X const &&, const X* &&) && " << "const char*foo(const X&&,const X&&,const X*&&)&&"; @@ -2428,6 +2459,22 @@ void tst_QMetaObject::customPropertyType() QCOMPARE(prop.metaType().id(), QMetaType::QVariantList); } +void tst_QMetaObject::customQVectorSuffix() +{ + QObject ctx; + QVERIFY(connect(this, SIGNAL(myQListChanged(MyQList)), + &ctx, SLOT(deleteLater()))); // just some compatible slot... + + // QMetaObject internally does s/QVector QList kludge getting in the way", Continue); + QTest::ignoreMessage(QtWarningMsg, + QRegularExpression(R"(.*QObject::connect: No such signal )" + R"(tst_QMetaObject.*::myQVectorChanged\(MyQVector\).*)"_L1)); + QVERIFY(connect(this, SIGNAL(myQVectorChanged(MyQVector)), + &ctx, SLOT(deleteLater()))); // just some compatible slot... +} + void tst_QMetaObject::keysToValue_data() { QTest::addColumn("object"); @@ -2802,10 +2849,11 @@ void tst_QMetaObject::indexOfMethod_data() QTest::addColumn("object"); QTest::addColumn("name"); QTest::addColumn("isSignal"); + QTest::addColumn("found"); - auto row = [this] (const char *fun, bool sig) { + auto row = [this] (const char *fun, bool sig, bool found = true) { QObject *o = this; - QTest::addRow("%s", fun) << o << QByteArray(fun) << sig; + QTest::addRow("%s", fun) << o << QByteArray(fun) << sig << found; }; row("indexOfMethod_data()", false); @@ -2814,6 +2862,9 @@ void tst_QMetaObject::indexOfMethod_data() row("value7Changed(QString)", true); row("destroyed()", true); row("destroyed(QObject*)", true); + row("myQListChanged(MyQList)", true); + row("myQListChanged(MyQVector)", true, false); + row("myQVectorChanged(MyQVector)", true); } void tst_QMetaObject::indexOfMethod() @@ -2821,9 +2872,16 @@ void tst_QMetaObject::indexOfMethod() QFETCH(QObject *, object); QFETCH(QByteArray, name); QFETCH(bool, isSignal); + QFETCH(const bool, found); + QEXPECT_FAIL("myQListChanged(MyQVector)", "Qt 6 QVector -> QList kludge getting in the way", Abort); + QEXPECT_FAIL("myQVectorChanged(MyQVector)", "Qt 6 QVector -> QList kludge getting in the way", Abort); int idx = object->metaObject()->indexOfMethod(name); - QVERIFY(idx >= 0); - QCOMPARE(object->metaObject()->method(idx).methodSignature(), name); + if (found) + QVERIFY(idx >= 0); + else + QVERIFY(idx < 0); + if (found) + QCOMPARE(object->metaObject()->method(idx).methodSignature(), name); QCOMPARE(object->metaObject()->indexOfSlot(name), isSignal ? -1 : idx); QCOMPARE(object->metaObject()->indexOfSignal(name), !isSignal ? -1 : idx); }