1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-24 17:35:35 +02:00

Bug 425102 QObject::connect content assist still broken

The content assistant was treating all function calls in the same way.
Here is an example showing the two cases that should be handled:

    qobj.connect( qobj.func(), SIGNAL( sig() ), SLOT( slot() ) );

In this case sig() applies to the return type of qobj::func() and slot()
applies to qobj (the same instance that connect is called upon).

The previous implementation of the assistant was not making a
distinction between these two cases.

I've added another test case to confirm behaviour in this area.

Change-Id: I8f76a5d5ae7384ea5162c5d36abeebb4c79c394b
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/20848
Tested-by: Hudson CI
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2014-01-20 20:23:35 -05:00 committed by Doug Schaefer
parent 25a0992a63
commit fd074ab029
4 changed files with 96 additions and 21 deletions

View file

@ -25,6 +25,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
@ -146,8 +147,20 @@ public class ASTUtil {
}
public static ICPPClassType getReceiverType(IASTFunctionCallExpression fncall) {
// See the thread that starts at:
// http://dev.eclipse.org/mhonarc/lists/cdt-dev/msg26972.html
// If the expression is calling a member function then find the type of the
// receiver.
IASTExpression fnName = fncall.getFunctionNameExpression();
if (fnName instanceof ICPPASTFieldReference) {
ICPPASTFieldReference fieldRef = (ICPPASTFieldReference) fnName;
ICPPASTExpression receiver = fieldRef.getFieldOwner();
IType recvType = getBaseType(receiver);
if (recvType instanceof ICPPClassType)
return (ICPPClassType) recvType;
}
// Otherwise check for a call to implicit 'this'. See details in the thread that
// starts at http://dev.eclipse.org/mhonarc/lists/cdt-dev/msg26972.html
try {
for(IScope scope = CPPVisitor.getContainingScope(fncall); scope != null; scope = scope.getParent())
if (scope instanceof ICPPClassScope)

View file

@ -14,8 +14,8 @@ import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.qt.core.QtKeywords;
/**
@ -93,30 +93,32 @@ public class QtFunctionCallUtil {
* that will be used for this method. Returns null if the argument is not a Qt method call or
* if the associated node cannot be found.
*/
public static IASTNode getTypeNode(IASTFunctionCallExpression call, IASTInitializerClause[] args, int argIndex) {
public static IType getTargetType(IASTFunctionCallExpression call, IASTInitializerClause[] args, int argIndex) {
int sigExpIndex = getExpansionArgIndex(args, 0, SignalRegex);
if (argIndex == sigExpIndex)
return getSignalTargetNode(sigExpIndex, call, args);
return getSignalTargetType(sigExpIndex, call, args);
int methodExpIndex = getExpansionArgIndex(args, sigExpIndex + 1, MethodRegex);
if (argIndex == methodExpIndex)
return getMethodTargetNode(methodExpIndex, sigExpIndex, call, args);
return getMethodTargetType(methodExpIndex, sigExpIndex, call, args);
// Otherwise the given argument is not a SIGNAL or SLOT expansion.
return null;
}
private static IASTNode getSignalTargetNode(int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) {
private static IType getSignalTargetType(int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) {
// When the SIGNAL expansion is first, the type is based on the receiver of
// the function call. Otherwise the type is the previous argument.
return sigExpIndex == 0 ? call : args[sigExpIndex - 1];
return ASTUtil.getBaseType(sigExpIndex == 0 ? call : args[sigExpIndex - 1]);
}
private static IASTNode getMethodTargetNode(int methodExpIndex, int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) {
private static IType getMethodTargetType(int methodExpIndex, int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) {
// If the method is right after the signal, then the type is based on the receiver
// of the function call. Otherwise the method type is based on the parameter right
// before the expansion.
return (methodExpIndex == (sigExpIndex + 1)) ? call : args[methodExpIndex - 1];
if (methodExpIndex == (sigExpIndex + 1))
return ASTUtil.getReceiverType(call);
return ASTUtil.getBaseType(args[methodExpIndex - 1]);
}
private static int getExpansionArgIndex(IASTInitializerClause[] args, int begin, Pattern macroNameRegex) {

View file

@ -10,9 +10,13 @@ package org.eclipse.cdt.qt.tests;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.tests.ast2.AST2TestBase;
@ -20,9 +24,10 @@ import org.eclipse.cdt.internal.qt.core.ASTUtil;
public class ASTUtilTests extends AST2TestBase {
// class Type { public: void function() { } };
// class T
// {
// void callee() { }
// void callee() { Type instance; instance.function(); }
// void caller() { this->callee(); callee(); T::callee(); }
// };
// void T::callee() { this->caller(); caller(); T::caller(); }
@ -33,7 +38,7 @@ public class ASTUtilTests extends AST2TestBase {
// Find the callee function call.
ArrayList<IASTFunctionCallExpression> fnCalls = new ArrayList<IASTFunctionCallExpression>();
collectChildren(fnCalls, tu, IASTFunctionCallExpression.class);
assertEquals(6, fnCalls.size());
assertEquals(7, fnCalls.size());
assertNotNull(fnCalls.get(0));
assertNotNull(fnCalls.get(1));
@ -41,24 +46,80 @@ public class ASTUtilTests extends AST2TestBase {
assertNotNull(fnCalls.get(3));
assertNotNull(fnCalls.get(4));
assertNotNull(fnCalls.get(5));
assertNotNull(fnCalls.get(6));
ICPPClassType recvr0 = ASTUtil.getReceiverType(fnCalls.get(0));
// explicit type
IASTFunctionCallExpression call = fnCalls.get(0);
ICPPClassType recvr0 = ASTUtil.getReceiverType(call);
assertNotNull(recvr0);
assertEquals("Type", recvr0.getName());
// implicit this
ICPPClassType recvr1 = ASTUtil.getReceiverType(fnCalls.get(1));
ICPPClassType recvr2 = ASTUtil.getReceiverType(fnCalls.get(2));
ICPPClassType recvr3 = ASTUtil.getReceiverType(fnCalls.get(3));
ICPPClassType recvr4 = ASTUtil.getReceiverType(fnCalls.get(4));
ICPPClassType recvr5 = ASTUtil.getReceiverType(fnCalls.get(5));
ICPPClassType recvr6 = ASTUtil.getReceiverType(fnCalls.get(6));
assertNotNull(recvr0);
assertNotNull(recvr1);
assertNotNull(recvr2);
assertNotNull(recvr3);
assertNotNull(recvr4);
assertNotNull(recvr5);
assertSame(recvr0, recvr1);
assertNotNull(recvr6);
assertSame(recvr1, recvr2);
assertSame(recvr3, recvr4);
assertSame(recvr4, recvr5);
assertSame(recvr5, recvr6);
}
// class C1
// {
// public:
// void f( C1 * ) { }
// C1 * g() { return this; }
// };
// void h() { C1 c; c.f( c.g() ); }
public void testBaseTypeOfFunctionCall() throws Exception {
IASTTranslationUnit tu = parse();
assertNotNull(tu);
// Find the C1 type.
ArrayList<ICPPASTCompositeTypeSpecifier> specs = new ArrayList<ICPPASTCompositeTypeSpecifier>();
collectChildren(specs, tu, ICPPASTCompositeTypeSpecifier.class);
assertEquals(1, specs.size());
ICPPASTCompositeTypeSpecifier spec = specs.get(0);
assertNotNull(spec);
IASTName specName = spec.getName();
assertNotNull(specName);
assertEquals("C1", specName.getRawSignature());
// Find the function call expression "c.get()".
ArrayList<IASTFunctionCallExpression> fnCalls = new ArrayList<IASTFunctionCallExpression>();
collectChildren(fnCalls, tu, IASTFunctionCallExpression.class);
assertEquals(2, fnCalls.size());
IASTFunctionCallExpression c_f = fnCalls.get(0);
IASTFunctionCallExpression c_g = fnCalls.get(1);
assertNotNull(c_f);
assertNotNull(c_g);
IASTExpression nameExpr = c_f.getFunctionNameExpression();
assertNotNull(nameExpr);
assertEquals("c.f", nameExpr.getRawSignature());
IType recvType = ASTUtil.getReceiverType(c_f);
assertTrue(recvType instanceof ICPPClassType);
nameExpr = c_g.getFunctionNameExpression();
assertNotNull(nameExpr);
assertEquals("c.g", nameExpr.getRawSignature());
recvType = ASTUtil.getBaseType(c_g);
assertTrue(recvType instanceof ICPPClassType);
}
private IASTTranslationUnit parse() throws Exception {

View file

@ -185,14 +185,13 @@ public class QObjectConnectCompletion {
return fields;
}
private static Collection<QObjectConnectCompletion> getCompletionsFor(IASTNode targetNode, IASTInitializerClause arg) {
private static Collection<QObjectConnectCompletion> getCompletionsFor(IType targetType, IASTInitializerClause arg) {
IType targetType = ASTUtil.getBaseType(targetNode);
if (!(targetType instanceof ICPPClassType))
return null;
ICPPClassType cls = (ICPPClassType) targetType;
QtIndex qtIndex = QtIndex.getIndex(ASTUtil.getProject(targetNode));
QtIndex qtIndex = QtIndex.getIndex(ASTUtil.getProject(arg));
if (qtIndex == null)
return null;
@ -285,13 +284,13 @@ public class QObjectConnectCompletion {
int argIndex = args.length - 1;
// Find the type node that is used for this expansion.
IASTNode typeNode = QtFunctionCallUtil.getTypeNode(call, args, argIndex);
if (typeNode == null)
IType targetType = QtFunctionCallUtil.getTargetType(call, args, argIndex);
if (targetType == null)
return null;
// Returns completions for the given expansion using the given type as the
// source for Qt methods.
return getCompletionsFor(typeNode, args[argIndex]);
return getCompletionsFor(targetType, args[argIndex]);
}
return null;