1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-25 09:55:29 +02:00

Bug 395562 - Completion of static members after class qualifier

Change-Id: I0142547adae9cca8245dfeead065f45ff30a878e
This commit is contained in:
Nathan Ridge 2016-12-12 15:02:57 -05:00
parent 917eecca42
commit 1628d11e7e
3 changed files with 125 additions and 62 deletions

View file

@ -25,6 +25,7 @@ import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner; import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICPPASTCompletionContext; import org.eclipse.cdt.core.dom.ast.ICPPASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IEnumerator; import org.eclipse.cdt.core.dom.ast.IEnumerator;
@ -309,39 +310,32 @@ public class CPPASTQualifiedName extends CPPASTNameBase
public IBinding[] findBindings(IASTName n, boolean isPrefix, String[] namespaces) { public IBinding[] findBindings(IASTName n, boolean isPrefix, String[] namespaces) {
IBinding[] bindings = CPPSemantics.findBindingsForContentAssist(n, isPrefix, namespaces); IBinding[] bindings = CPPSemantics.findBindingsForContentAssist(n, isPrefix, namespaces);
if (fQualifierPos >= 0) { ICPPClassType classQualifier = getClassQualifier();
IBinding binding = fQualifier[fQualifierPos].resolveBinding(); if (classQualifier != null) {
final boolean isDeclaration = getParent().getParent() instanceof IASTSimpleDeclaration;
while (binding instanceof ITypedef) { List<IBinding> filtered = filterClassScopeBindings(classQualifier, bindings, isDeclaration);
ITypedef typedef = (ITypedef) binding; if (isDeclaration && nameMatches(classQualifier.getNameCharArray(),
IType type = typedef.getType(); n.getLookupKey(), isPrefix)) {
if (type instanceof IBinding) { ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classQualifier, n);
binding = (IBinding) type; for (int i = 0; i < constructors.length; i++) {
} else { if (!constructors[i].isImplicit()) {
binding = null; filtered.add(constructors[i]);
}
}
if (binding instanceof ICPPClassType) {
ICPPClassType classType = (ICPPClassType) binding;
final boolean isDeclaration = getParent().getParent() instanceof IASTSimpleDeclaration;
List<IBinding> filtered = filterClassScopeBindings(classType, bindings, isDeclaration);
if (isDeclaration && nameMatches(classType.getNameCharArray(),
n.getLookupKey(), isPrefix)) {
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, n);
for (int i = 0; i < constructors.length; i++) {
if (!constructors[i].isImplicit()) {
filtered.add(constructors[i]);
}
} }
} }
return filtered.toArray(new IBinding[filtered.size()]);
} }
return filtered.toArray(new IBinding[filtered.size()]);
} }
return bindings; return bindings;
} }
// Are we taking the address of a qualified name?
private boolean isAddressOf() {
return getParent() instanceof IASTIdExpression
&& getParent().getParent() instanceof IASTUnaryExpression
&& ((IASTUnaryExpression) getParent().getParent()).getOperator() == IASTUnaryExpression.op_amper;
}
private boolean canBeFieldAccess(ICPPClassType baseClass) { private boolean canBeFieldAccess(ICPPClassType baseClass) {
IASTNode parent= getParent(); IASTNode parent= getParent();
if (parent instanceof IASTFieldReference) { if (parent instanceof IASTFieldReference) {
@ -364,18 +358,46 @@ public class CPPASTQualifiedName extends CPPASTNameBase
} }
return false; return false;
} }
private ICPPClassType getClassQualifier() {
if (fQualifierPos < 0) {
return null;
}
IBinding binding = fQualifier[fQualifierPos].resolveBinding();
while (binding instanceof ITypedef) {
ITypedef typedef = (ITypedef) binding;
IType type = typedef.getType();
if (type instanceof IBinding) {
binding = (IBinding) type;
} else {
binding = null;
}
}
return binding instanceof ICPPClassType ? (ICPPClassType) binding : null;
}
public static boolean canBeFieldAccess(CPPASTQualifiedName qname) {
ICPPClassType classQualifier = qname.getClassQualifier();
if (classQualifier == null) {
return true;
}
return qname.canBeFieldAccess(classQualifier);
}
private List<IBinding> filterClassScopeBindings(ICPPClassType classType, IBinding[] bindings, private List<IBinding> filterClassScopeBindings(ICPPClassType classType, IBinding[] bindings,
final boolean isDeclaration) { final boolean isDeclaration) {
List<IBinding> filtered = new ArrayList<IBinding>(); List<IBinding> filtered = new ArrayList<IBinding>();
final boolean canBeFieldAccess = canBeFieldAccess(classType); final boolean allowNonstatic = canBeFieldAccess(classType) || isAddressOf();
final IBinding templateDefinition = (classType instanceof ICPPTemplateInstance) final IBinding templateDefinition = (classType instanceof ICPPTemplateInstance)
? ((ICPPTemplateInstance) classType).getTemplateDefinition() : null; ? ((ICPPTemplateInstance) classType).getTemplateDefinition() : null;
for (final IBinding binding : bindings) { for (final IBinding binding : bindings) {
if (binding instanceof IField) { if (binding instanceof IField) {
IField field = (IField) binding; IField field = (IField) binding;
if (!canBeFieldAccess && !field.isStatic()) if (!allowNonstatic && !field.isStatic())
continue; continue;
} else if (binding instanceof ICPPMethod) { } else if (binding instanceof ICPPMethod) {
ICPPMethod method = (ICPPMethod) binding; ICPPMethod method = (ICPPMethod) binding;
@ -383,7 +405,7 @@ public class CPPASTQualifiedName extends CPPASTNameBase
continue; continue;
if (!isDeclaration) { if (!isDeclaration) {
if (method.isDestructor() || method instanceof ICPPConstructor if (method.isDestructor() || method instanceof ICPPConstructor
|| (!canBeFieldAccess && !method.isStatic())) || (!allowNonstatic && !method.isStatic()))
continue; continue;
} }
} else if (binding instanceof IEnumerator || binding instanceof IEnumeration) { } else if (binding instanceof IEnumerator || binding instanceof IEnumeration) {

View file

@ -903,6 +903,22 @@ public class CompletionTests extends AbstractContentAssistTest {
final String[] expected= { "Printer::" }; final String[] expected= { "Printer::" };
assertMinimumCompletionResults(fCursorOffset, expected, REPLACEMENT); assertMinimumCompletionResults(fCursorOffset, expected, REPLACEMENT);
} }
// struct S {
// void method();
// int datamem;
// };
//
// template <typename F>
// void f(F);
//
// int main() {
// f(&S::/*cursor*/);
// }
public void testAddressOfClassQualifiedNonstaticMember_395562() throws Exception {
final String[] expected = { "method", "datamem" };
assertMinimumCompletionResults(fCursorOffset, expected, REPLACEMENT);
}
// typedef struct { // typedef struct {
// int sx; // int sx;

View file

@ -102,6 +102,7 @@ import org.eclipse.cdt.internal.core.dom.parser.c.CBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitFunction; import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitTypedef; import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitTypedef;
import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor; import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinParameter; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinParameter;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinVariable; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
@ -359,9 +360,9 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
} else if (binding instanceof ICPPAliasTemplate) { } else if (binding instanceof ICPPAliasTemplate) {
handleAliasTemplate((ICPPAliasTemplate) binding, cContext, baseRelevance, proposals); handleAliasTemplate((ICPPAliasTemplate) binding, cContext, baseRelevance, proposals);
} else if (binding instanceof IFunction) { } else if (binding instanceof IFunction) {
handleFunction((IFunction) binding, cContext, baseRelevance, proposals); handleFunction((IFunction) binding, astContext, cContext, baseRelevance, proposals);
} else if (binding instanceof IVariable) { } else if (binding instanceof IVariable) {
handleVariable((IVariable) binding, cContext, baseRelevance, proposals); handleVariable((IVariable) binding, astContext, cContext, baseRelevance, proposals);
} else if (!cContext.isContextInformationStyle()) { } else if (!cContext.isContextInformationStyle()) {
if (binding instanceof ITypedef) { if (binding instanceof ITypedef) {
proposals.add(createProposal(name, name, getImage(binding), proposals.add(createProposal(name, name, getImage(binding),
@ -471,22 +472,22 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
} }
private void handleClass(ICPPClassType classType, IASTCompletionContext astContext, private void handleClass(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) { CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
if (context.isContextInformationStyle() && context.isAfterOpeningParenthesis()) { if (cContext.isContextInformationStyle() && cContext.isAfterOpeningParenthesis()) {
addProposalsForConstructors(classType, context, baseRelevance, proposals); addProposalsForConstructors(classType, astContext, cContext, baseRelevance, proposals);
} else if (classType instanceof ICPPClassTemplate) { } else if (classType instanceof ICPPClassTemplate) {
addProposalForClassTemplate((ICPPClassTemplate) classType, context, baseRelevance, proposals); addProposalForClassTemplate((ICPPClassTemplate) classType, cContext, baseRelevance, proposals);
} else { } else {
int relevance = getClassTypeRelevance(classType); int relevance = getClassTypeRelevance(classType);
if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) { if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) {
IASTName name= (IASTName)astContext; IASTName name= (IASTName)astContext;
if (name.getParent() instanceof IASTDeclarator) { if (name.getParent() instanceof IASTDeclarator) {
proposals.add(createProposal(classType.getName() + "::", classType.getName(), //$NON-NLS-1$ proposals.add(createProposal(classType.getName() + "::", classType.getName(), //$NON-NLS-1$
getImage(classType), baseRelevance + relevance, context)); getImage(classType), baseRelevance + relevance, cContext));
} }
} }
proposals.add(createProposal(classType.getName(), classType.getName(), getImage(classType), proposals.add(createProposal(classType.getName(), classType.getName(), getImage(classType),
baseRelevance + RelevanceConstants.CLASS_TYPE_RELEVANCE, context)); baseRelevance + RelevanceConstants.CLASS_TYPE_RELEVANCE, cContext));
} }
} }
@ -496,11 +497,11 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
baseRelevance + RelevanceConstants.TYPEDEF_TYPE_RELEVANCE, proposals); baseRelevance + RelevanceConstants.TYPEDEF_TYPE_RELEVANCE, proposals);
} }
private void addProposalsForConstructors(ICPPClassType classType, private void addProposalsForConstructors(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) { CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
ICPPConstructor[] constructors = classType.getConstructors(); ICPPConstructor[] constructors = classType.getConstructors();
for (ICPPConstructor constructor : constructors) { for (ICPPConstructor constructor : constructors) {
handleFunction(constructor, context, baseRelevance, proposals); handleFunction(constructor, astContext, cContext, baseRelevance, proposals);
} }
} }
@ -520,13 +521,34 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
return relevance; return relevance;
} }
private void handleFunction(IFunction function, CContentAssistInvocationContext context, // Returns whether a function name being completed could be a call that function.
int baseRelevance, List<ICompletionProposal> proposals) { private boolean canBeCall(IFunction function, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext) {
// Can't have a call in a using-directive.
if (cContext.isInUsingDirective()) {
return false;
}
// Otherwise, it can be call unless the function is a nonstatic method,
// and we are not inside the class's scope.
if (astContext instanceof CPPASTQualifiedName) {
CPPASTQualifiedName qname = (CPPASTQualifiedName) astContext;
if (!function.isStatic() && !CPPASTQualifiedName.canBeFieldAccess(qname)) {
return false;
}
}
return true;
}
private void handleFunction(IFunction function, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
Image image = getImage(function); Image image = getImage(function);
StringBuilder repStringBuff = new StringBuilder(); StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(function.getName()); repStringBuff.append(function.getName());
boolean canBeCall = canBeCall(function, astContext, cContext);
repStringBuff.append('('); repStringBuff.append('(');
StringBuilder dispArgs = new StringBuilder(); // For the dispArgString StringBuilder dispArgs = new StringBuilder(); // For the dispArgString
@ -535,7 +557,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
String returnTypeStr = null; String returnTypeStr = null;
IParameter[] params = function.getParameters(); IParameter[] params = function.getParameters();
if (params != null) { if (params != null) {
final String parameterDelimiter = context.getFunctionParameterDelimiter(); final String parameterDelimiter = cContext.getFunctionParameterDelimiter();
for (int i = 0; i < params.length; ++i) { for (int i = 0; i < params.length; ++i) {
IParameter param = params[i]; IParameter param = params[i];
if (skipDefaultedParameter(param)) { if (skipDefaultedParameter(param)) {
@ -602,26 +624,28 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
idStringBuff.append(')'); idStringBuff.append(')');
String idString = idStringBuff.toString(); String idString = idStringBuff.toString();
// In a using declaration, emitting parentheses after the function boolean inUsingDeclaration = cContext.isInUsingDirective();
// name is useless, since the user will just have to delete them.
// Instead, emitting a semicolon is useful. // If we can't be calling the function in this context, do not
boolean inUsingDeclaration = context.isInUsingDirective(); // emit parentheses, since the user will just have to delete them.
if (inUsingDeclaration) { if (!canBeCall) {
repStringBuff.setLength(repStringBuff.length() - 1); // Remove opening parenthesis repStringBuff.setLength(repStringBuff.length() - 1); // Remove opening parenthesis
if (!context.isFollowedBySemicolon()) {
// In a using declaration, emitting a semicolon instead is useful.
if (inUsingDeclaration && !cContext.isFollowedBySemicolon()) {
repStringBuff.append(';'); repStringBuff.append(';');
} }
} else { } else {
repStringBuff.append(')'); repStringBuff.append(')');
} }
String repString = repStringBuff.toString(); String repString = repStringBuff.toString();
final int relevance = function instanceof ICPPMethod ? final int relevance = function instanceof ICPPMethod ?
RelevanceConstants.METHOD_TYPE_RELEVANCE : RelevanceConstants.FUNCTION_TYPE_RELEVANCE; RelevanceConstants.METHOD_TYPE_RELEVANCE : RelevanceConstants.FUNCTION_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString, CCompletionProposal proposal = createProposal(repString, dispString, idString,
context.getCompletionNode().getLength(), image, baseRelevance + relevance, context); cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
if (!context.isContextInformationStyle()) { if (!cContext.isContextInformationStyle()) {
int cursorPosition = !inUsingDeclaration && hasArgs ? int cursorPosition = !inUsingDeclaration && hasArgs ?
repString.length() - 1 : repString.length(); repString.length() - 1 : repString.length();
proposal.setCursorPosition(cursorPosition); proposal.setCursorPosition(cursorPosition);
@ -630,7 +654,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
if (contextDispargString != null && !inUsingDeclaration) { if (contextDispargString != null && !inUsingDeclaration) {
CProposalContextInformation info = CProposalContextInformation info =
new CProposalContextInformation(image, dispString, contextDispargString); new CProposalContextInformation(image, dispString, contextDispargString);
info.setContextInformationPosition(context.getContextInformationOffset()); info.setContextInformationPosition(cContext.getContextInformationOffset());
proposal.setContextInformation(info); proposal.setContextInformation(info);
} }
@ -638,8 +662,8 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
// assist is invoked before typing any parameters. Otherwise, the normal parameter hint proposal will // assist is invoked before typing any parameters. Otherwise, the normal parameter hint proposal will
// be added. // be added.
if (function.getParameters() != null && function.getParameters().length != 0 if (function.getParameters() != null && function.getParameters().length != 0
&& isBeforeParameters(context)) { && isBeforeParameters(cContext)) {
proposals.add(ParameterGuessingProposal.createProposal(context, fAvailableElements, proposal, proposals.add(ParameterGuessingProposal.createProposal(cContext, fAvailableElements, proposal,
function, fPrefix, fGuessArguments)); function, fPrefix, fGuessArguments));
} else { } else {
proposals.add(proposal); proposals.add(proposal);
@ -764,17 +788,18 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
return !isDisplayDefaultedParameters() && param instanceof ICPPParameter && ((ICPPParameter)param).hasDefaultValue(); return !isDisplayDefaultedParameters() && param instanceof ICPPParameter && ((ICPPParameter)param).hasDefaultValue();
} }
private void handleVariable(IVariable variable, CContentAssistInvocationContext context, private void handleVariable(IVariable variable, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext,
int baseRelevance, List<ICompletionProposal> proposals) { int baseRelevance, List<ICompletionProposal> proposals) {
if (context.isContextInformationStyle()) { if (cContext.isContextInformationStyle()) {
IType t = variable.getType(); IType t = variable.getType();
t= unwindTypedefs(t); t= unwindTypedefs(t);
if (t instanceof ICPPClassType) { if (t instanceof ICPPClassType) {
ICPPClassType classType= (ICPPClassType) t; ICPPClassType classType= (ICPPClassType) t;
IASTTranslationUnit ast = context.getCompletionNode().getTranslationUnit(); IASTTranslationUnit ast = cContext.getCompletionNode().getTranslationUnit();
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, ast); ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, ast);
for (ICPPConstructor constructor : constructors) { for (ICPPConstructor constructor : constructors) {
handleFunction(constructor, context, baseRelevance, proposals); handleFunction(constructor, astContext, cContext, baseRelevance, proposals);
} }
} }
return; return;
@ -807,7 +832,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
RelevanceConstants.FIELD_TYPE_RELEVANCE : RelevanceConstants.FIELD_TYPE_RELEVANCE :
RelevanceConstants.VARIABLE_TYPE_RELEVANCE; RelevanceConstants.VARIABLE_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString, CCompletionProposal proposal = createProposal(repString, dispString, idString,
context.getCompletionNode().getLength(), image, baseRelevance + relevance, context); cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
proposals.add(proposal); proposals.add(proposal);
} }