QMetaObject: deprecate the Qt 6 QVector -> QList porting kludge
The argumentTypesFromString() function is clearly documented not to perform any normalization, yet in typical Qt 6.0 porting rush, it did, and this kludge was never removed. Do it now; it's in the way of porting QArgumentType from QBA to QBAV, and it's causing correct code to incorrectly fail. This, however, changes the behavior of QMetaObject::indexOf*(), because they don't fall back to normalization (indeed, these functions are used as isNormalized checks, e.g. in connect()). So we can't remove the kludge just yet, but we can drag it out of the fast path and re-try with QVector replaced by QList when nothing was found using the original signature. This way, we only pessimize unported users (and calls that would have failed for other reasons, by scanning for "QVector<" in the signature). Add a qWarning() that we'll remove this behavior going forward. It does, however, fix the bug that signals and slots that contain types that match, but are not, "QVector<", fail to be found by the machinery: [ChangeLog][QtCore][QMetaObject/QObject] Fixed a bug that caused signals and slots with argument types matching "QVector<" (e.g. "MyQVector<int>" or "NotQt::QVector<int>") to not be found in QObject::connect() or QMetaObject::indexOfMethod(). [ChangeLog][Deprecation Notices][QMetaObject] The indexOf{Constructor,Slot,Signal,Method}() functions are documented to require input according to QMetaObject::normalizedSignature(), but accepted a QList declared as QVector. This was an internal porting aid and is being deprecated now. Watch out for runtime warnings about this. QObject::connect() and QMetaObject::invokeMethod() are unaffected, as they fall back to normalizeSignature() automatically. No change in tst_bench_qobject connect performance, which is unsurprising, as the benchmark doesn't use a QVector alias. Amends 03326a2fec416405b437089874f6439e937bbada. Task-number: QTBUG-135572 Pick-to: 6.10 Change-Id: I7fd9293bba5d2b57b4452e55499ffbf360bc6123 Reviewed-by: Ahmad Samir <a.samirh78@gmail.com> Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
parent
5aa1bc4621
commit
afdf37ad8f
@ -26,6 +26,8 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
@ -741,6 +743,31 @@ inline int QMetaObjectPrivate::indexOfMethodRelative(const QMetaObject **baseObj
|
|||||||
\sa constructor(), constructorCount(), normalizedSignature()
|
\sa constructor(), constructorCount(), normalizedSignature()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if QT_DEPRECATED_SINCE(6, 10)
|
||||||
|
Q_DECL_COLD_FUNCTION
|
||||||
|
static int compat_indexOf(const char *what, const char *sig, const QMetaObject *mo,
|
||||||
|
int (*indexOf)(const QMetaObject *, const char *))
|
||||||
|
{
|
||||||
|
const QByteArray normalized = QByteArray(sig).replace("QVector<", "QList<");
|
||||||
|
const int i = indexOf(mo, normalized.data());
|
||||||
|
if (i >= 0) {
|
||||||
|
qWarning(R"(QMetaObject::indexOf%s: argument "%s" is not normalized, because it contains "QVector<". )"
|
||||||
|
R"(Earlier versions of Qt 6 incorrectly normalized QVector< to QList<, silently. )"
|
||||||
|
R"(This behavior is deprecated as of 6.10, and will be removed in a future version of Qt.)",
|
||||||
|
what, sig);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INDEXOF_COMPAT(what, arg) \
|
||||||
|
do { \
|
||||||
|
if (i < 0 && Q_UNLIKELY(std::strstr(arg, "QVector<"))) \
|
||||||
|
i = compat_indexOf(#what, arg, this, &indexOf ## what ## _helper); \
|
||||||
|
} while (false)
|
||||||
|
#else
|
||||||
|
#define INDEXOF_COMPAT(what, arg)
|
||||||
|
#endif // QT_DEPRECATED_SINCE(6, 10)
|
||||||
|
|
||||||
static int indexOfConstructor_helper(const QMetaObject *mo, const char *constructor)
|
static int indexOfConstructor_helper(const QMetaObject *mo, const char *constructor)
|
||||||
{
|
{
|
||||||
QArgumentTypeArray types;
|
QArgumentTypeArray types;
|
||||||
@ -752,6 +779,7 @@ int QMetaObject::indexOfConstructor(const char *constructor) const
|
|||||||
{
|
{
|
||||||
Q_ASSERT(priv(d.data)->revision >= 7);
|
Q_ASSERT(priv(d.data)->revision >= 7);
|
||||||
int i = indexOfConstructor_helper(this, constructor);
|
int i = indexOfConstructor_helper(this, constructor);
|
||||||
|
INDEXOF_COMPAT(Constructor, constructor);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,6 +810,7 @@ int QMetaObject::indexOfMethod(const char *method) const
|
|||||||
{
|
{
|
||||||
const QMetaObject *m = this;
|
const QMetaObject *m = this;
|
||||||
int i = indexOfMethod_helper(m, method);
|
int i = indexOfMethod_helper(m, method);
|
||||||
|
INDEXOF_COMPAT(Method, method);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,7 +833,6 @@ static void argumentTypesFromString(const char *str, const char *end,
|
|||||||
++str;
|
++str;
|
||||||
}
|
}
|
||||||
QByteArray argType(begin, str - begin);
|
QByteArray argType(begin, str - begin);
|
||||||
argType.replace("QVector<", "QList<");
|
|
||||||
types += QArgumentType(std::move(argType));
|
types += QArgumentType(std::move(argType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -856,6 +884,7 @@ int QMetaObject::indexOfSignal(const char *signal) const
|
|||||||
{
|
{
|
||||||
const QMetaObject *m = this;
|
const QMetaObject *m = this;
|
||||||
int i = indexOfSignal_helper(m, signal);
|
int i = indexOfSignal_helper(m, signal);
|
||||||
|
INDEXOF_COMPAT(Signal, signal);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,9 +941,12 @@ int QMetaObject::indexOfSlot(const char *slot) const
|
|||||||
{
|
{
|
||||||
const QMetaObject *m = this;
|
const QMetaObject *m = this;
|
||||||
int i = indexOfSlot_helper(m, slot);
|
int i = indexOfSlot_helper(m, slot);
|
||||||
|
INDEXOF_COMPAT(Slot, slot);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef INDEXOF_COMPAT
|
||||||
|
|
||||||
// same as indexOfSignalRelative but for slots.
|
// same as indexOfSignalRelative but for slots.
|
||||||
int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m,
|
int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m,
|
||||||
QByteArrayView name, int argc,
|
QByteArrayView name, int argc,
|
||||||
|
@ -40,6 +40,17 @@ Q_DECLARE_METATYPE(const QMetaObject *)
|
|||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
#if QT_DEPRECATED_SINCE(6, 10)
|
||||||
|
static void eatIndexOfNonNormalizedWarning()
|
||||||
|
{
|
||||||
|
static const QRegularExpression rx(R"(QMetaObject::indexOf(Constructor|Method|Signal|Slot): )"
|
||||||
|
R"(argument ".+" is not normalized, )"
|
||||||
|
R"(because it contains "QVector<"\.)"_L1);
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, rx);
|
||||||
|
}
|
||||||
|
#define NORMALIZES_QVECTOR_QLIST
|
||||||
|
#endif
|
||||||
|
|
||||||
struct MyStruct
|
struct MyStruct
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -2470,12 +2481,8 @@ void tst_QMetaObject::customQVectorSuffix()
|
|||||||
QVERIFY(connect(this, SIGNAL(myQListChanged(MyQList<int>)),
|
QVERIFY(connect(this, SIGNAL(myQListChanged(MyQList<int>)),
|
||||||
&ctx, SLOT(deleteLater()))); // just some compatible slot...
|
&ctx, SLOT(deleteLater()))); // just some compatible slot...
|
||||||
|
|
||||||
// QMetaObject internally does s/QVector</QList</ indiscriminently, so the
|
// QMetaObject used to internally s/QVector</QList</ indiscriminently, so the
|
||||||
// existing signal is not found:
|
// existing signal was not found:
|
||||||
QEXPECT_FAIL("", "Qt 6 QVector -> QList kludge getting in the way", Continue);
|
|
||||||
QTest::ignoreMessage(QtWarningMsg,
|
|
||||||
QRegularExpression(R"(.*QObject::connect: No such signal )"
|
|
||||||
R"(tst_QMetaObject.*::myQVectorChanged\(MyQVector<double>\).*)"_L1));
|
|
||||||
QVERIFY(connect(this, SIGNAL(myQVectorChanged(MyQVector<double>)),
|
QVERIFY(connect(this, SIGNAL(myQVectorChanged(MyQVector<double>)),
|
||||||
&ctx, SLOT(deleteLater()))); // just some compatible slot...
|
&ctx, SLOT(deleteLater()))); // just some compatible slot...
|
||||||
}
|
}
|
||||||
@ -2759,12 +2766,18 @@ void tst_QMetaObject::metaMethod()
|
|||||||
QCOMPARE(obj.slotResult, QString("sl5:12345"));
|
QCOMPARE(obj.slotResult, QString("sl5:12345"));
|
||||||
|
|
||||||
// check Qt 6 QVector/QList alias:
|
// check Qt 6 QVector/QList alias:
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13v(QVector<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13v(QVector<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#endif
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13v(QList<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13v(QList<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QVector<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QVector<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#endif
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
|
QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
|
||||||
@ -2781,14 +2794,20 @@ void tst_QMetaObject::metaMethod()
|
|||||||
|
|
||||||
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QObject*,QList<int>)");
|
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QObject*,QList<int>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QObject*,QVector<int>)");
|
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QObject*,QVector<int>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#endif
|
||||||
QCOMPARE(QtTestObject::staticMetaObject.constructor(index).methodSignature(),
|
QCOMPARE(QtTestObject::staticMetaObject.constructor(index).methodSignature(),
|
||||||
"QtTestObject(QObject*,QList<int>)");
|
"QtTestObject(QObject*,QList<int>)");
|
||||||
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QList<int>,QObject*)");
|
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QList<int>,QObject*)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QVector<int>,QObject*)");
|
index = QtTestObject::staticMetaObject.indexOfConstructor("QtTestObject(QVector<int>,QObject*)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#endif
|
||||||
QCOMPARE(QtTestObject::staticMetaObject.constructor(index).methodSignature(),
|
QCOMPARE(QtTestObject::staticMetaObject.constructor(index).methodSignature(),
|
||||||
"QtTestObject(QList<int>,QObject*)");
|
"QtTestObject(QList<int>,QObject*)");
|
||||||
}
|
}
|
||||||
@ -2832,8 +2851,11 @@ void tst_QMetaObject::metaMethodNoMacro()
|
|||||||
QCOMPARE(obj.slotResult, QString("sl5:12345"));
|
QCOMPARE(obj.slotResult, QString("sl5:12345"));
|
||||||
|
|
||||||
// check Qt 6 QVector/QList alias:
|
// check Qt 6 QVector/QList alias:
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QVector<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QVector<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
|
#endif
|
||||||
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
|
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
|
||||||
QVERIFY(index > 0);
|
QVERIFY(index > 0);
|
||||||
QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
|
QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
|
||||||
@ -2878,8 +2900,11 @@ void tst_QMetaObject::indexOfMethod()
|
|||||||
QFETCH(QByteArray, name);
|
QFETCH(QByteArray, name);
|
||||||
QFETCH(bool, isSignal);
|
QFETCH(bool, isSignal);
|
||||||
QFETCH(const bool, found);
|
QFETCH(const bool, found);
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
QEXPECT_FAIL("myQListChanged(MyQVector<int>)", "Qt 6 QVector -> QList kludge getting in the way", Abort);
|
QEXPECT_FAIL("myQListChanged(MyQVector<int>)", "Qt 6 QVector -> QList kludge getting in the way", Abort);
|
||||||
QEXPECT_FAIL("myQVectorChanged(MyQVector<double>)", "Qt 6 QVector -> QList kludge getting in the way", Abort);
|
if (qstrcmp(QTest::currentDataTag(), "myQListChanged(MyQVector<int>)") == 0)
|
||||||
|
eatIndexOfNonNormalizedWarning();
|
||||||
|
#endif
|
||||||
int idx = object->metaObject()->indexOfMethod(name);
|
int idx = object->metaObject()->indexOfMethod(name);
|
||||||
if (found)
|
if (found)
|
||||||
QVERIFY(idx >= 0);
|
QVERIFY(idx >= 0);
|
||||||
@ -3333,5 +3358,9 @@ void tst_QMetaObject::connectByMetaMethodToFreeFunction()
|
|||||||
QCOMPARE(emit o.sig1(u"foo"_s), u"foofoo"_s);
|
QCOMPARE(emit o.sig1(u"foo"_s), u"foofoo"_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef NORMALIZES_QVECTOR_QLIST
|
||||||
|
# undef NORMALIZES_QVECTOR_QLIST
|
||||||
|
#endif
|
||||||
|
|
||||||
QTEST_MAIN(tst_QMetaObject)
|
QTEST_MAIN(tst_QMetaObject)
|
||||||
#include "tst_qmetaobject.moc"
|
#include "tst_qmetaobject.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user