diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java index 34c729dfab2..a1181be180f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java @@ -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 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 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 filterClassScopeBindings(ICPPClassType classType, IBinding[] bindings, final boolean isDeclaration) { List filtered = new ArrayList(); - 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) { diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java index 8a3b515e188..e09d03ff10a 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java @@ -903,6 +903,22 @@ public class CompletionTests extends AbstractContentAssistTest { final String[] expected= { "Printer::" }; assertMinimumCompletionResults(fCursorOffset, expected, REPLACEMENT); } + + // struct S { + // void method(); + // int datamem; + // }; + // + // template + // 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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java index e204e810873..8c0c7604951 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java @@ -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 proposals) { - if (context.isContextInformationStyle() && context.isAfterOpeningParenthesis()) { - addProposalsForConstructors(classType, context, baseRelevance, proposals); + CContentAssistInvocationContext cContext, int baseRelevance, List 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 proposals) { + private void addProposalsForConstructors(ICPPClassType classType, IASTCompletionContext astContext, + CContentAssistInvocationContext cContext, int baseRelevance, List 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 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 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 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); }