From daa56f1b4e784d6497ae4fdeaa664f3a1a7b5143 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 11 Jun 2014 11:15:59 +0200 Subject: [PATCH] Suggest candidates when non-existent method passed to invokeMethod(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMetaObject::invokeMethod: No such method Object::someMethod(SomeType) becomes: QMetaObject::invokeMethod: No such method Object::someMethod(SomeType) Candidates are: someMethod(SomeOtherType) someMethod(YetAnotherType) Change-Id: I3566bca64423e2f8150d0d544fb4e35a5262b19e Reviewed-by: Jędrzej Nowacki Reviewed-by: Olivier Goffart --- src/corelib/kernel/qmetaobject.cpp | 26 +++++++++++++++++-- .../kernel/qmetaobject/tst_qmetaobject.cpp | 12 ++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index accefb1ec4..cb63718680 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1338,6 +1338,27 @@ QByteArray QMetaObject::normalizedSignature(const char *method) enum { MaximumParamCount = 11 }; // up to 10 arguments + 1 return value +/*! + Returns the signatures of all methods whose name matches \a nonExistentMember, + or an empty QByteArray if there are no matches. +*/ +static inline QByteArray findMethodCandidates(const QMetaObject *metaObject, const char *nonExistentMember) +{ + QByteArray candidateMessage; + // Prevent full string comparison in every iteration. + const QByteArray memberByteArray = nonExistentMember; + for (int i = 0; i < metaObject->methodCount(); ++i) { + const QMetaMethod method = metaObject->method(i); + if (method.name() == memberByteArray) + candidateMessage.append(" " + method.methodSignature() + '\n'); + } + if (!candidateMessage.isEmpty()) { + candidateMessage.prepend("\nCandidates are:\n"); + candidateMessage.chop(1); + } + return candidateMessage; +} + /*! Invokes the \a member (a signal or a slot name) on the object \a obj. Returns \c true if the member could be invoked. Returns \c false @@ -1455,8 +1476,9 @@ bool QMetaObject::invokeMethod(QObject *obj, } if (idx < 0 || idx >= meta->methodCount()) { - qWarning("QMetaObject::invokeMethod: No such method %s::%s", - meta->className(), sig.constData()); + // This method doesn't belong to us; print out a nice warning with candidates. + qWarning("QMetaObject::invokeMethod: No such method %s::%s%s", + meta->className(), sig.constData(), findMethodCandidates(meta, member).constData()); return false; } QMetaMethod method = meta->method(idx); diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 09084578ab..a920ea7a01 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -561,9 +561,11 @@ void tst_QMetaObject::invokeMetaMember() QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist")); QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Q_ARG(QString, "arg"))); - QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)"); + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n" + "Candidates are:\n sl3(QString,QString,QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", Q_ARG(QString, "arg"))); - QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)"); + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)\n" + "Candidates are:\n sl1(QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(QString, "arg"), Q_ARG(QString, "arg"), Q_ARG(QString, "arg"))); //should not have changed since last test. @@ -747,9 +749,11 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember() QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist", Qt::BlockingQueuedConnection)); QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg"))); - QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)"); + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n" + "Candidates are:\n sl3(QString,QString,QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg"))); - QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)"); + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)\n" + "Candidates are:\n sl1(QString)"); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg"), Q_ARG(QString, "arg"), Q_ARG(QString, "arg"))); //should not have changed since last test.