CppEditor: Support implicit pointers in Qt5 connect conversion

Task-number: QTCREATORBUG-14104
Change-Id: I157dbcb0e06ed1bf49d2df01bdbad215e35a4c3a
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Orgad Shaneh
2015-03-05 22:19:41 +02:00
committed by Orgad Shaneh
parent 683540bee7
commit dbb9891f43
2 changed files with 89 additions and 5 deletions

View File

@@ -4543,6 +4543,12 @@ void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass_data()
"conne@ct(p.t, SIGNAL(sigFoo(int)), p.t, SLOT(setProp(int)));") "conne@ct(p.t, SIGNAL(sigFoo(int)), p.t, SLOT(setProp(int)));")
<< QByteArray("Pointer<TestClass> p;\n" << QByteArray("Pointer<TestClass> p;\n"
"connect(p.t, &TestClass::sigFoo, p.t, &TestClass::setProp);"); "connect(p.t, &TestClass::sigFoo, p.t, &TestClass::setProp);");
QTest::newRow("implicit-pointer")
<< QByteArray("Pointer<TestClass> p;\n"
"conne@ct(p, SIGNAL(sigFoo(int)), p, SLOT(setProp(int)));")
<< QByteArray("Pointer<TestClass> p;\n"
"connect(p.data(), &TestClass::sigFoo, p.data(), &TestClass::setProp);");
} }
void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass() void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass()
@@ -4555,6 +4561,8 @@ void CppEditorPlugin::test_quickfix_ConvertQt4Connect_connectWithinClass()
"struct Pointer\n" "struct Pointer\n"
"{\n" "{\n"
" T *t;\n" " T *t;\n"
" operator T*() const { return t; }\n"
" T *data() const { return t; }\n"
"};\n" "};\n"
"class QObject {};\n" "class QObject {};\n"
"class TestClass : public QObject\n" "class TestClass : public QObject\n"

View File

@@ -5578,10 +5578,75 @@ Symbol *skipForwardDeclarations(const QList<Symbol *> &symbols)
return 0; return 0;
} }
bool findRawAccessFunction(Class *klass, PointerType *pointerType, QString *objAccessFunction)
{
QList<Function *> 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, Class *senderOrReceiverClass(const CppQuickFixInterface &interface,
const CppRefactoringFilePtr &file, const CppRefactoringFilePtr &file,
const ExpressionAST *objectPointerAST, const ExpressionAST *objectPointerAST,
Scope *objectPointerScope) Scope *objectPointerScope,
QString *objAccessFunction)
{ {
const LookupContext &context = interface.context(); const LookupContext &context = interface.context();
@@ -5602,6 +5667,10 @@ Class *senderOrReceiverClass(const CppQuickFixInterface &interface,
QTC_ASSERT(objectPointerTypeBase, return 0); QTC_ASSERT(objectPointerTypeBase, return 0);
PointerType *objectPointerType = objectPointerTypeBase->asPointerType(); PointerType *objectPointerType = objectPointerTypeBase->asPointerType();
if (!objectPointerType) {
objectPointerType = determineConvertedType(objectPointerTypeBase->asNamedType(), context,
objectPointerScope, objAccessFunction);
}
QTC_ASSERT(objectPointerType, return 0); QTC_ASSERT(objectPointerType, return 0);
Type *objectTypeBase = objectPointerType->elementType().type(); // Dereference Type *objectTypeBase = objectPointerType->elementType().type(); // Dereference
@@ -5624,7 +5693,8 @@ bool findConnectReplacement(const CppQuickFixInterface &interface,
const ExpressionAST *objectPointerAST, const ExpressionAST *objectPointerAST,
const QtMethodAST *methodAST, const QtMethodAST *methodAST,
const CppRefactoringFilePtr &file, const CppRefactoringFilePtr &file,
QString *replacement) QString *replacement,
QString *objAccessFunction)
{ {
// Get name of method // Get name of method
if (!methodAST->declarator || !methodAST->declarator->core_declarator) if (!methodAST->declarator || !methodAST->declarator->core_declarator)
@@ -5640,7 +5710,8 @@ bool findConnectReplacement(const CppQuickFixInterface &interface,
// Lookup object pointer type // Lookup object pointer type
Scope *scope = file->scopeAt(methodAST->firstToken()); 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); QTC_ASSERT(objectClass, return false);
// Look up member function in call, including base class members. // 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(); const CppRefactoringFilePtr file = interface.currentFile();
QString newSignal; QString newSignal;
if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal)) QString senderAccessFunc;
if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal, &senderAccessFunc))
continue; continue;
QString newMethod; QString newMethod;
if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod)) QString receiverAccessFunc;
if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod, &receiverAccessFunc))
continue; continue;
ChangeSet changes; ChangeSet changes;
changes.replace(file->endOf(arg1), file->endOf(arg1), senderAccessFunc);
changes.replace(file->startOf(arg2), file->endOf(arg2), newSignal); changes.replace(file->startOf(arg2), file->endOf(arg2), newSignal);
if (!arg3) if (!arg3)
newMethod.prepend(QLatin1String("this, ")); newMethod.prepend(QLatin1String("this, "));
else
changes.replace(file->endOf(arg3), file->endOf(arg3), receiverAccessFunc);
changes.replace(file->startOf(arg4), file->endOf(arg4), newMethod); changes.replace(file->startOf(arg4), file->endOf(arg4), newMethod);
result.append(new ConvertQt4ConnectOperation(interface, changes)); result.append(new ConvertQt4ConnectOperation(interface, changes));