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:
parent
917eecca42
commit
1628d11e7e
3 changed files with 125 additions and 62 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue