1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +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.IASTNode;
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.ICPPASTCompletionContext;
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) {
IBinding[] bindings = CPPSemantics.findBindingsForContentAssist(n, isPrefix, namespaces);
if (fQualifierPos >= 0) {
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;
}
}
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]);
}
ICPPClassType classQualifier = getClassQualifier();
if (classQualifier != null) {
final boolean isDeclaration = getParent().getParent() instanceof IASTSimpleDeclaration;
List<IBinding> filtered = filterClassScopeBindings(classQualifier, bindings, isDeclaration);
if (isDeclaration && nameMatches(classQualifier.getNameCharArray(),
n.getLookupKey(), isPrefix)) {
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classQualifier, 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;
}
// 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) {
IASTNode parent= getParent();
if (parent instanceof IASTFieldReference) {
@ -364,18 +358,46 @@ public class CPPASTQualifiedName extends CPPASTNameBase
}
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,
final boolean isDeclaration) {
List<IBinding> filtered = new ArrayList<IBinding>();
final boolean canBeFieldAccess = canBeFieldAccess(classType);
final boolean allowNonstatic = canBeFieldAccess(classType) || isAddressOf();
final IBinding templateDefinition = (classType instanceof ICPPTemplateInstance)
? ((ICPPTemplateInstance) classType).getTemplateDefinition() : null;
for (final IBinding binding : bindings) {
if (binding instanceof IField) {
IField field = (IField) binding;
if (!canBeFieldAccess && !field.isStatic())
if (!allowNonstatic && !field.isStatic())
continue;
} else if (binding instanceof ICPPMethod) {
ICPPMethod method = (ICPPMethod) binding;
@ -383,7 +405,7 @@ public class CPPASTQualifiedName extends CPPASTNameBase
continue;
if (!isDeclaration) {
if (method.isDestructor() || method instanceof ICPPConstructor
|| (!canBeFieldAccess && !method.isStatic()))
|| (!allowNonstatic && !method.isStatic()))
continue;
}
} else if (binding instanceof IEnumerator || binding instanceof IEnumeration) {

View file

@ -903,6 +903,22 @@ public class CompletionTests extends AbstractContentAssistTest {
final String[] expected= { "Printer::" };
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 {
// 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.CImplicitTypedef;
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.CPPBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
@ -359,9 +360,9 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
} else if (binding instanceof ICPPAliasTemplate) {
handleAliasTemplate((ICPPAliasTemplate) binding, cContext, baseRelevance, proposals);
} else if (binding instanceof IFunction) {
handleFunction((IFunction) binding, cContext, baseRelevance, proposals);
handleFunction((IFunction) binding, astContext, cContext, baseRelevance, proposals);
} else if (binding instanceof IVariable) {
handleVariable((IVariable) binding, cContext, baseRelevance, proposals);
handleVariable((IVariable) binding, astContext, cContext, baseRelevance, proposals);
} else if (!cContext.isContextInformationStyle()) {
if (binding instanceof ITypedef) {
proposals.add(createProposal(name, name, getImage(binding),
@ -471,22 +472,22 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
}
private void handleClass(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) {
if (context.isContextInformationStyle() && context.isAfterOpeningParenthesis()) {
addProposalsForConstructors(classType, context, baseRelevance, proposals);
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
if (cContext.isContextInformationStyle() && cContext.isAfterOpeningParenthesis()) {
addProposalsForConstructors(classType, astContext, cContext, baseRelevance, proposals);
} else if (classType instanceof ICPPClassTemplate) {
addProposalForClassTemplate((ICPPClassTemplate) classType, context, baseRelevance, proposals);
addProposalForClassTemplate((ICPPClassTemplate) classType, cContext, baseRelevance, proposals);
} else {
int relevance = getClassTypeRelevance(classType);
if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) {
IASTName name= (IASTName)astContext;
if (name.getParent() instanceof IASTDeclarator) {
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),
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);
}
private void addProposalsForConstructors(ICPPClassType classType,
CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) {
private void addProposalsForConstructors(ICPPClassType classType, IASTCompletionContext astContext,
CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {
ICPPConstructor[] constructors = classType.getConstructors();
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;
}
private void handleFunction(IFunction function, CContentAssistInvocationContext context,
int baseRelevance, List<ICompletionProposal> proposals) {
// Returns whether a function name being completed could be a call that function.
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);
StringBuilder repStringBuff = new StringBuilder();
repStringBuff.append(function.getName());
boolean canBeCall = canBeCall(function, astContext, cContext);
repStringBuff.append('(');
StringBuilder dispArgs = new StringBuilder(); // For the dispArgString
@ -535,7 +557,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
String returnTypeStr = null;
IParameter[] params = function.getParameters();
if (params != null) {
final String parameterDelimiter = context.getFunctionParameterDelimiter();
final String parameterDelimiter = cContext.getFunctionParameterDelimiter();
for (int i = 0; i < params.length; ++i) {
IParameter param = params[i];
if (skipDefaultedParameter(param)) {
@ -602,26 +624,28 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
idStringBuff.append(')');
String idString = idStringBuff.toString();
// In a using declaration, emitting parentheses after the function
// name is useless, since the user will just have to delete them.
// Instead, emitting a semicolon is useful.
boolean inUsingDeclaration = context.isInUsingDirective();
if (inUsingDeclaration) {
boolean inUsingDeclaration = cContext.isInUsingDirective();
// If we can't be calling the function in this context, do not
// emit parentheses, since the user will just have to delete them.
if (!canBeCall) {
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(';');
}
} else {
repStringBuff.append(')');
}
} else {
repStringBuff.append(')');
}
String repString = repStringBuff.toString();
final int relevance = function instanceof ICPPMethod ?
RelevanceConstants.METHOD_TYPE_RELEVANCE : RelevanceConstants.FUNCTION_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString,
context.getCompletionNode().getLength(), image, baseRelevance + relevance, context);
if (!context.isContextInformationStyle()) {
cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
if (!cContext.isContextInformationStyle()) {
int cursorPosition = !inUsingDeclaration && hasArgs ?
repString.length() - 1 : repString.length();
proposal.setCursorPosition(cursorPosition);
@ -630,7 +654,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
if (contextDispargString != null && !inUsingDeclaration) {
CProposalContextInformation info =
new CProposalContextInformation(image, dispString, contextDispargString);
info.setContextInformationPosition(context.getContextInformationOffset());
info.setContextInformationPosition(cContext.getContextInformationOffset());
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
// be added.
if (function.getParameters() != null && function.getParameters().length != 0
&& isBeforeParameters(context)) {
proposals.add(ParameterGuessingProposal.createProposal(context, fAvailableElements, proposal,
&& isBeforeParameters(cContext)) {
proposals.add(ParameterGuessingProposal.createProposal(cContext, fAvailableElements, proposal,
function, fPrefix, fGuessArguments));
} else {
proposals.add(proposal);
@ -764,17 +788,18 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
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) {
if (context.isContextInformationStyle()) {
if (cContext.isContextInformationStyle()) {
IType t = variable.getType();
t= unwindTypedefs(t);
if (t instanceof ICPPClassType) {
ICPPClassType classType= (ICPPClassType) t;
IASTTranslationUnit ast = context.getCompletionNode().getTranslationUnit();
IASTTranslationUnit ast = cContext.getCompletionNode().getTranslationUnit();
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, ast);
for (ICPPConstructor constructor : constructors) {
handleFunction(constructor, context, baseRelevance, proposals);
handleFunction(constructor, astContext, cContext, baseRelevance, proposals);
}
}
return;
@ -807,7 +832,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
RelevanceConstants.FIELD_TYPE_RELEVANCE :
RelevanceConstants.VARIABLE_TYPE_RELEVANCE;
CCompletionProposal proposal = createProposal(repString, dispString, idString,
context.getCompletionNode().getLength(), image, baseRelevance + relevance, context);
cContext.getCompletionNode().getLength(), image, baseRelevance + relevance, cContext);
proposals.add(proposal);
}