QObject: Handle pointers to incomplete types in auto-connections

The code for the QMetaMethod queuedConnectionTypes overload relied on
QMetaMethod::parameterMetaType and QMetaType::flags() to detect whether
we're dealing with a pointer (and then use the VoidStar metatype instead).
However, if the type was incomplete when the slot was defined, and the
type was not registered when connect was called, we would not find a
metatype for the argument.
However, in that case we might still be able to handle the method, by
checking whether the type name of the arguments ends with a "*".
This patch does that, fixing a regression from 5.15.

Pick-to: 6.5 6.4 6.2
Fixes: QTBUG-108537
Change-Id: I54cc48a3343444480ab9094fe1ebaaa5aa75cee0
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Fabian Kosmale 2023-02-13 17:27:12 +01:00
parent da5dc20625
commit 9483fcd68a
2 changed files with 39 additions and 0 deletions

View File

@ -84,6 +84,8 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
typeIds[i] = QMetaType::VoidStar; typeIds[i] = QMetaType::VoidStar;
else else
typeIds[i] = metaType.id(); typeIds[i] = metaType.id();
if (!typeIds[i] && method.parameterTypeName(i).endsWith('*'))
typeIds[i] = QMetaType::VoidStar;
if (!typeIds[i]) { if (!typeIds[i]) {
const QByteArray typeName = method.parameterTypeName(i); const QByteArray typeName = method.parameterTypeName(i);
qCWarning(lcConnect, qCWarning(lcConnect,

View File

@ -58,6 +58,7 @@ private slots:
void connectNotify_connectSlotsByName(); void connectNotify_connectSlotsByName();
void connectDisconnectNotify_shadowing(); void connectDisconnectNotify_shadowing();
void connectReferenceToIncompleteTypes(); void connectReferenceToIncompleteTypes();
void connectAutoQueuedIncomplete();
void emitInDefinedOrder(); void emitInDefinedOrder();
void customTypes(); void customTypes();
void streamCustomTypes(); void streamCustomTypes();
@ -893,6 +894,42 @@ void tst_QObject::connectReferenceToIncompleteTypes() {
QVERIFY(connection); QVERIFY(connection);
} }
struct Incomplete2;
class QObjectWithIncomplete2 : public QObject {
Q_OBJECT
public:
QObjectWithIncomplete2(QObject *parent=nullptr) : QObject(parent) {}
signals:
void signalWithIncomplete(Incomplete2 *ptr);
public slots:
void slotWithIncomplete(Incomplete2 *) { calledSlot = true; }
void run() { Q_EMIT signalWithIncomplete(nullptr); }
public:
bool calledSlot = false;
};
void tst_QObject::connectAutoQueuedIncomplete()
{
auto objectWithIncomplete1 = new QObjectWithIncomplete2();
auto objectWithIncomplete2 = new QObjectWithIncomplete2();
auto t = new QThread(this);
auto cleanup = qScopeGuard([&](){
t->quit();
QVERIFY(t->wait());
delete objectWithIncomplete1;
delete objectWithIncomplete2;
});
t->start();
objectWithIncomplete2->moveToThread(t);
connect(objectWithIncomplete2, &QObjectWithIncomplete2::signalWithIncomplete,
objectWithIncomplete1, &QObjectWithIncomplete2::slotWithIncomplete);
QMetaObject::invokeMethod(objectWithIncomplete2, "run", Qt::QueuedConnection);
QTRY_VERIFY(objectWithIncomplete1->calledSlot);
}
static void connectDisconnectNotifyTestSlot() {} static void connectDisconnectNotifyTestSlot() {}
void tst_QObject::connectDisconnectNotifyPMF() void tst_QObject::connectDisconnectNotifyPMF()