summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2021-11-17 14:08:11 -0800
committerThiago Macieira <thiago.macieira@intel.com>2021-11-27 21:38:22 -0800
commit0e72a846d379ba02ff80ecac2526640a05b872b6 (patch)
tree1b64de80427755d2c2a335558e55d549949e740c /tests/auto/corelib/kernel/qobject/tst_qobject.cpp
parentb41356658e522f8fbc1061c4f4c76f0ba17b6acd (diff)
QObject: Q_ASSERT the object type before calling a PMF
The old-syle signal-slot syntax had the advantage of not delivering signals to slots in derived classes after that derived class's destructor had finished running (because we called via the virtual qt_metacall). The new syntax made no checks, so a conversion from the old to the new syntax may introduce crashes or other data corruptions at runtime if the destructor had completed. This commit introduces a Q_ASSERT to print the class name that the object is not any more. Since this is in inline code, this should get enabled for users' debug modes and does not therefore depend on Qt being built in debug mode. It required some Private classes to be adapted to the new form, by exposing the public q_func() in the public: part. Pick-to: 6.2 Fixes: QTBUG-33908 Change-Id: Iccb47e5527544b6fbd75fffd16b874cdc08c1f3e Reviewed-by: Marc Mutz <marc.mutz@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Diffstat (limited to 'tests/auto/corelib/kernel/qobject/tst_qobject.cpp')
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp71
1 files changed, 69 insertions, 2 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index 9c27824e36..2e34099e5b 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -1,7 +1,8 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
+** Copyright (C) 2020 Olivier Goffart <ogoffart@woboq.com>
+** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -169,6 +170,7 @@ private slots:
void disconnectDisconnects();
void singleShotConnection();
void objectNameBinding();
+ void emitToDestroyedClass();
};
struct QObjectCreatedOnShutdown
@@ -5946,8 +5948,8 @@ public:
};
class ConnectToPrivateSlotPrivate : public QObjectPrivate {
- Q_DECLARE_PUBLIC(ConnectToPrivateSlot)
public:
+ Q_DECLARE_PUBLIC(ConnectToPrivateSlot)
int receivedCount;
QVariant receivedValue;
@@ -8140,6 +8142,71 @@ void tst_QObject::objectNameBinding()
"objectName");
}
+namespace EmitToDestroyedClass {
+static int assertionCallCount = 0;
+static int wouldHaveAssertedCount = 0;
+struct WouldAssert : std::exception {};
+class Base : public QObject
+{
+ Q_OBJECT
+public:
+ ~Base()
+ {
+ try {
+ emit theSignal();
+ } catch (const WouldAssert &) {
+ ++wouldHaveAssertedCount;
+ }
+ }
+
+signals:
+ void theSignal();
+};
+
+class Derived : public Base
+{
+ Q_OBJECT
+public:
+ ~Derived() { }
+
+public slots:
+ void doNothing() {}
+};
+} // namespace EmitToDestroyedClass
+
+QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+template<> void assertObjectType<EmitToDestroyedClass::Derived>(QObject *o)
+{
+ // override the assertion so we don't assert and so something does happen
+ // when assertions are disabled. By throwing, we also prevent the UB from
+ // happening.
+ using namespace EmitToDestroyedClass;
+ ++assertionCallCount;
+ if (!qobject_cast<Derived *>(o))
+ throw WouldAssert();
+}
+}
+QT_END_NAMESPACE
+
+void tst_QObject::emitToDestroyedClass()
+{
+ using namespace EmitToDestroyedClass;
+ std::unique_ptr ptr = std::make_unique<Derived>();
+ QObject::connect(ptr.get(), &Base::theSignal, ptr.get(), &Derived::doNothing);
+ QCOMPARE(assertionCallCount, 0);
+ QCOMPARE(wouldHaveAssertedCount, 0);
+
+ // confirm our replacement function did get called
+ emit ptr->theSignal();
+ QCOMPARE(assertionCallCount, 1);
+ QCOMPARE(wouldHaveAssertedCount, 0);
+
+ ptr.reset();
+ QCOMPARE(assertionCallCount, 2);
+ QCOMPARE(wouldHaveAssertedCount, 1);
+}
+
// Test for QtPrivate::HasQ_OBJECT_Macro
static_assert(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value);
static_assert(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value);