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:
parent
25a0992a63
commit
fd074ab029
4 changed files with 96 additions and 21 deletions
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue