1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

Bug 402498. Do not rely on promiscuous binding resolution for adding

includes.

Change-Id: Idace39cc9ff9b781bf0de2d0017533548fb69e83
This commit is contained in:
Sergey Prigogin 2015-02-15 22:41:54 -08:00 committed by Gerrit Code Review @ Eclipse.org
parent 0221ee20b1
commit 52c80c124e
3 changed files with 170 additions and 22 deletions

View file

@ -507,6 +507,7 @@ public class IncludeOrganizerTest extends IncludesTestBase {
//class C {};
//source.cpp
//#include "h2.h"
//A a;
//B* b;
//C* c;

View file

@ -698,18 +698,20 @@ public class BindingClassifier {
if (isPartOfExternalMacroDefinition(functionNameExpression))
return PROCESS_CONTINUE;
IASTInitializerClause[] arguments = functionCallExpression.getArguments();
IBinding binding = getBindingOfExpression(functionNameExpression);
if (binding != null) {
if (binding instanceof IFunction) {
defineForFunctionCall((IFunction) binding, functionCallExpression.getArguments());
} else if (binding instanceof ICPPMember) {
try {
IType memberType = ((ICPPMember) binding).getType();
defineIndirectTypes(memberType);
} catch (DOMException e) {
if (binding instanceof IProblemBinding) {
IBinding[] candidates = ((IProblemBinding) binding).getCandidateBindings();
if (candidates.length != 0) {
for (IBinding candidate : candidates) {
defineBindingForFunctionCall(candidate, arguments);
}
} else {
defineBinding(binding);
}
} else if (binding instanceof ITypedef) {
defineBinding(binding);
} else {
defineBindingForFunctionCall(binding, arguments);
}
}
if (functionCallExpression instanceof IASTImplicitNameOwner) {
@ -717,7 +719,7 @@ public class BindingClassifier {
for (IASTName name : implicitNames) {
binding = name.resolveBinding();
if (binding instanceof IFunction) {
defineForFunctionCall((IFunction) binding, functionCallExpression.getArguments());
defineForFunctionCall((IFunction) binding, arguments);
}
}
}
@ -786,6 +788,20 @@ public class BindingClassifier {
return PROCESS_CONTINUE;
}
protected void defineBindingForFunctionCall(IBinding binding, IASTInitializerClause[] arguments) {
if (binding instanceof IFunction) {
defineForFunctionCall((IFunction) binding, arguments);
} else if (binding instanceof ICPPMember) {
try {
IType memberType = ((ICPPMember) binding).getType();
defineIndirectTypes(memberType);
} catch (DOMException e) {
}
} else if (binding instanceof ITypedef) {
defineBinding(binding);
}
}
@Override
public int leave(IASTName name) {
if (isPartOfExternalMacroDefinition(name))
@ -1085,16 +1101,7 @@ public class BindingClassifier {
}
private void addRequiredBindings(IBinding binding, Deque<IBinding> newBindings) {
if (binding instanceof ICPPMethod) {
newBindings.add(binding); // Include the method in case we need its inline definition.
if (binding instanceof ICPPConstructor)
newBindings.add(binding.getOwner());
} else if (binding instanceof IType) {
// Remove type qualifiers.
IBinding b = getTypeBinding((IType) binding);
if (b != null)
newBindings.add(b);
} else if (binding instanceof IProblemBinding) {
if (binding instanceof IProblemBinding) {
IProblemBinding problemBinding = (IProblemBinding) binding;
IBinding[] candidateBindings = problemBinding.getCandidateBindings();
@ -1113,6 +1120,15 @@ public class BindingClassifier {
} catch (CoreException e) {
}
}
} else if (binding instanceof ICPPMethod) {
newBindings.add(binding); // Include the method in case we need its inline definition.
if (binding instanceof ICPPConstructor)
newBindings.add(binding.getOwner());
} else if (binding instanceof IType) {
// Remove type qualifiers.
IBinding b = getTypeBinding((IType) binding);
if (b != null)
newBindings.add(b);
} else {
newBindings.add(binding);
}
@ -1175,7 +1191,9 @@ public class BindingClassifier {
*/
private boolean canForwardDeclare(IBinding binding) {
boolean canDeclare = false;
if (binding instanceof ICPPUsingDeclaration) {
if (binding instanceof IProblemBinding && ((IProblemBinding) binding).getCandidateBindings().length != 0) {
return true; // Return true to consider delegates later on.
} if (binding instanceof ICPPUsingDeclaration) {
return true; // Return true to consider delegates later on.
} if (binding instanceof ICompositeType) {
canDeclare = fPreferences.forwardDeclareCompositeTypes;

View file

@ -40,6 +40,8 @@ import org.eclipse.text.edits.MultiTextEdit;
import com.ibm.icu.text.Collator;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -51,15 +53,20 @@ import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
import org.eclipse.cdt.core.index.IIndex;
@ -76,6 +83,7 @@ import org.eclipse.cdt.ui.IFunctionSummary;
import org.eclipse.cdt.ui.IRequiredInclude;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.IASTInternalScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
@ -182,7 +190,13 @@ public class IncludeCreator {
final ArrayList<IncludeCandidate> candidates = new ArrayList<>(candidatesMap.values());
if (candidates.size() > 1) {
IncludeCandidate candidate = fAmbiguityResolver.selectElement(candidates);
// First, try to resolve the ambiguity by comparing the namespaces of the
// candidate bindings to the namespace in which the source name occurs.
IncludeCandidate candidate = selectCandidateByNamespace(name, candidates);
// If that doesn't disambiguate, fall back to the ambiguity resolver
// provided by the user of this class.
if (candidate == null)
candidate = fAmbiguityResolver.selectElement(candidates);
if (candidate == null)
return rootEdit;
candidates.clear();
@ -226,6 +240,69 @@ public class IncludeCreator {
return createEdit(requiredIncludes, usingDeclarations, ast, selection);
}
private ICPPNamespaceScope getContainingNamespaceScope(IASTNode node) {
IScope scope = CPPVisitor.getContainingScope(node);
while (scope != null) {
if (scope instanceof ICPPNamespaceScope && !(scope instanceof ICPPBlockScope)) {
return (ICPPNamespaceScope) scope;
}
try {
scope = scope.getParent();
} catch (DOMException e) {
return null;
}
}
return null;
}
private ICPPNamespace getContainingNamespace(IASTName name) {
ICPPNamespaceScope scope = getContainingNamespaceScope(name);
// TODO(nathanridge): Move this ICPPNamespaceScope -> ICPPNamespace
// mapping code to a utility class.
if (scope instanceof ICPPNamespace) {
return (ICPPNamespace) scope;
}
if (scope instanceof IASTInternalScope) {
IASTNode node = ((IASTInternalScope) scope).getPhysicalNode();
if (node instanceof ICPPASTNamespaceDefinition) {
IBinding namespace = ((ICPPASTNamespaceDefinition) node).getName().resolveBinding();
if (namespace instanceof ICPPNamespace) {
return (ICPPNamespace) namespace;
}
}
}
return null;
}
private ICPPNamespace getContainingNamespace(IBinding binding) {
while (binding != null) {
if (binding instanceof ICPPNamespace) {
return (ICPPNamespace) binding;
}
binding = binding.getOwner();
}
return null;
}
private IncludeCandidate selectCandidateByNamespace(IASTName sourceName, ArrayList<IncludeCandidate> candidates) {
// If one of the candidates is in the same namespace as the source name,
// and the others aren't, prefer that candidate.
ICPPNamespace sourceNamespace = getContainingNamespace(sourceName);
if (sourceNamespace == null) {
return null;
}
IncludeCandidate winner = null;
for (IncludeCandidate candidate : candidates) {
if (SemanticUtil.isSameNamespace(sourceNamespace, getContainingNamespace(candidate.binding))) {
if (winner != null) {
return null; // ambiguous between 'winner' and 'candidate'
}
winner = candidate;
}
}
return winner;
}
private MultiTextEdit createEdit(List<IncludeInfo> includes,
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
ITextSelection selection) {
@ -497,11 +574,63 @@ public class IncludeCreator {
return name.getLastName().toString().equals(usingChain.get(usingChain.size() - 1 - qualifiers.length));
}
private ArrayList<String> getUsingChainForProblemBinding(IProblemBinding binding) {
ArrayList<String> chain = new ArrayList<>(4);
chain.add(binding.getName());
IASTNode node = binding.getASTNode();
if (node.getParent() instanceof ICPPASTQualifiedName) {
// If the ProblemBinding is for a name inside the qualified name,
// use the chain of preceding segments in the qualifier as the
// using chain.
ICPPASTQualifiedName qualifiedName = (ICPPASTQualifiedName) node.getParent();
ICPPASTNameSpecifier[] qualifier = qualifiedName.getQualifier();
int i = qualifier.length;
if (node != qualifiedName.getLastName()) {
while (--i >= 0) {
if (qualifier[i] == node) {
break;
}
}
}
while (--i >= 0) {
chain.add(qualifier[i].resolveBinding().getName());
}
} else {
// Otherwise, fall back to the chain of namespaces that physically
// contain the name.
ICPPNamespaceScope namespace = getContainingNamespaceScope(node);
while (namespace != null) {
IName namespaceName = namespace.getScopeName();
if (namespaceName != null) {
chain.add(new String(namespaceName.getSimpleID()));
}
try {
IScope parent = namespace.getParent();
if (parent instanceof ICPPNamespaceScope) {
namespace = (ICPPNamespaceScope) parent;
} else {
break;
}
} catch (DOMException e) {
break;
}
}
}
return chain;
}
/**
* Returns components of the qualified name in reverse order.
* For ns1::ns2::Name, e.g., it returns [Name, ns2, ns1].
*/
private ArrayList<String> getUsingChain(IBinding binding) {
// For ProblemBindings, getOwner() is very heuristic and doesn't
// produce the chain of owner bindings we want here, so we
// handle it specially.
if (binding instanceof IProblemBinding) {
return getUsingChainForProblemBinding((IProblemBinding) binding);
}
ArrayList<String> chain = new ArrayList<>(4);
for (; binding != null; binding = binding.getOwner()) {
String name = binding.getName();