From dbb9891f43291e3ffd015f3cc84d9389d01e170e Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Thu, 5 Mar 2015 22:19:41 +0200 Subject: [PATCH] CppEditor: Support implicit pointers in Qt5 connect conversion Task-number: QTCREATORBUG-14104 Change-Id: I157dbcb0e06ed1bf49d2df01bdbad215e35a4c3a Reviewed-by: Nikolai Kosjar --- src/plugins/cppeditor/cppquickfix_test.cpp | 8 ++ src/plugins/cppeditor/cppquickfixes.cpp | 86 ++++++++++++++++++++-- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index c2fd582d4d8..4a203153d36 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -4543,6 +4543,12 @@ void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass_data() "conne@ct(p.t, SIGNAL(sigFoo(int)), p.t, SLOT(setProp(int)));") << QByteArray("Pointer p;\n" "connect(p.t, &TestClass::sigFoo, p.t, &TestClass::setProp);"); + + QTest::newRow("implicit-pointer") + << QByteArray("Pointer p;\n" + "conne@ct(p, SIGNAL(sigFoo(int)), p, SLOT(setProp(int)));") + << QByteArray("Pointer p;\n" + "connect(p.data(), &TestClass::sigFoo, p.data(), &TestClass::setProp);"); } void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass() @@ -4555,6 +4561,8 @@ void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass() "struct Pointer\n" "{\n" " T *t;\n" + " operator T*() const { return t; }\n" + " T *data() const { return t; }\n" "};\n" "class QObject {};\n" "class TestClass : public QObject\n" diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 3c252082d18..d71303eb25e 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -5578,10 +5578,75 @@ Symbol *skipForwardDeclarations(const QList &symbols) return 0; } +bool findRawAccessFunction(Class *klass, PointerType *pointerType, QString *objAccessFunction) +{ + QList candidates; + for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) { + if (Function *func = (*it)->asFunction()) { + const Name *funcName = func->name(); + if (!funcName->isOperatorNameId() + && !funcName->isConversionNameId() + && func->returnType().type() == pointerType + && func->isConst() + && func->argumentCount() == 0) { + candidates << func; + } + } + } + const Name *funcName = 0; + switch (candidates.size()) { + case 0: + return false; + case 1: + funcName = candidates.first()->name(); + break; + default: + // Multiple candidates - prefer a function named data + foreach (Function *func, candidates) { + if (!strcmp(func->name()->identifier()->chars(), "data")) { + funcName = func->name(); + break; + } + } + if (!funcName) + funcName = candidates.first()->name(); + } + const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview(); + *objAccessFunction = QLatin1Char('.') + oo.prettyName(funcName) + QLatin1String("()"); + return true; +} + +PointerType *determineConvertedType(NamedType *namedType, const LookupContext &context, + Scope *scope, QString *objAccessFunction) +{ + if (!namedType) + return 0; + if (ClassOrNamespace *binding = context.lookupType(namedType->name(), scope)) { + if (Symbol *objectClassSymbol = skipForwardDeclarations(binding->symbols())) { + if (Class *klass = objectClassSymbol->asClass()) { + for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) { + if (Function *func = (*it)->asFunction()) { + if (const ConversionNameId *conversionName = + func->name()->asConversionNameId()) { + if (PointerType *type = conversionName->type()->asPointerType()) { + if (findRawAccessFunction(klass, type, objAccessFunction)) + return type; + } + } + } + } + } + } + } + + return 0; +} + Class *senderOrReceiverClass(const CppQuickFixInterface &interface, const CppRefactoringFilePtr &file, const ExpressionAST *objectPointerAST, - Scope *objectPointerScope) + Scope *objectPointerScope, + QString *objAccessFunction) { const LookupContext &context = interface.context(); @@ -5602,6 +5667,10 @@ Class *senderOrReceiverClass(const CppQuickFixInterface &interface, QTC_ASSERT(objectPointerTypeBase, return 0); PointerType *objectPointerType = objectPointerTypeBase->asPointerType(); + if (!objectPointerType) { + objectPointerType = determineConvertedType(objectPointerTypeBase->asNamedType(), context, + objectPointerScope, objAccessFunction); + } QTC_ASSERT(objectPointerType, return 0); Type *objectTypeBase = objectPointerType->elementType().type(); // Dereference @@ -5624,7 +5693,8 @@ bool findConnectReplacement(const CppQuickFixInterface &interface, const ExpressionAST *objectPointerAST, const QtMethodAST *methodAST, const CppRefactoringFilePtr &file, - QString *replacement) + QString *replacement, + QString *objAccessFunction) { // Get name of method if (!methodAST->declarator || !methodAST->declarator->core_declarator) @@ -5640,7 +5710,8 @@ bool findConnectReplacement(const CppQuickFixInterface &interface, // Lookup object pointer type Scope *scope = file->scopeAt(methodAST->firstToken()); - Class *objectClass = senderOrReceiverClass(interface, file, objectPointerAST, scope); + Class *objectClass = senderOrReceiverClass(interface, file, objectPointerAST, scope, + objAccessFunction); QTC_ASSERT(objectClass, return false); // Look up member function in call, including base class members. @@ -5762,17 +5833,22 @@ void ConvertQt4Connect::match(const CppQuickFixInterface &interface, QuickFixOpe const CppRefactoringFilePtr file = interface.currentFile(); QString newSignal; - if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal)) + QString senderAccessFunc; + if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal, &senderAccessFunc)) continue; QString newMethod; - if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod)) + QString receiverAccessFunc; + if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod, &receiverAccessFunc)) continue; ChangeSet changes; + changes.replace(file->endOf(arg1), file->endOf(arg1), senderAccessFunc); changes.replace(file->startOf(arg2), file->endOf(arg2), newSignal); if (!arg3) newMethod.prepend(QLatin1String("this, ")); + else + changes.replace(file->endOf(arg3), file->endOf(arg3), receiverAccessFunc); changes.replace(file->startOf(arg4), file->endOf(arg4), newMethod); result.append(new ConvertQt4ConnectOperation(interface, changes));