mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-31 12:55:40 +02:00
Bug 402498. Do not rely on promiscuous binding resolution for adding
includes. Change-Id: Idace39cc9ff9b781bf0de2d0017533548fb69e83
This commit is contained in:
parent
0221ee20b1
commit
52c80c124e
3 changed files with 170 additions and 22 deletions
|
@ -507,6 +507,7 @@ public class IncludeOrganizerTest extends IncludesTestBase {
|
||||||
//class C {};
|
//class C {};
|
||||||
|
|
||||||
//source.cpp
|
//source.cpp
|
||||||
|
//#include "h2.h"
|
||||||
//A a;
|
//A a;
|
||||||
//B* b;
|
//B* b;
|
||||||
//C* c;
|
//C* c;
|
||||||
|
|
|
@ -698,18 +698,20 @@ public class BindingClassifier {
|
||||||
if (isPartOfExternalMacroDefinition(functionNameExpression))
|
if (isPartOfExternalMacroDefinition(functionNameExpression))
|
||||||
return PROCESS_CONTINUE;
|
return PROCESS_CONTINUE;
|
||||||
|
|
||||||
|
IASTInitializerClause[] arguments = functionCallExpression.getArguments();
|
||||||
IBinding binding = getBindingOfExpression(functionNameExpression);
|
IBinding binding = getBindingOfExpression(functionNameExpression);
|
||||||
if (binding != null) {
|
if (binding != null) {
|
||||||
if (binding instanceof IFunction) {
|
if (binding instanceof IProblemBinding) {
|
||||||
defineForFunctionCall((IFunction) binding, functionCallExpression.getArguments());
|
IBinding[] candidates = ((IProblemBinding) binding).getCandidateBindings();
|
||||||
} else if (binding instanceof ICPPMember) {
|
if (candidates.length != 0) {
|
||||||
try {
|
for (IBinding candidate : candidates) {
|
||||||
IType memberType = ((ICPPMember) binding).getType();
|
defineBindingForFunctionCall(candidate, arguments);
|
||||||
defineIndirectTypes(memberType);
|
}
|
||||||
} catch (DOMException e) {
|
} else {
|
||||||
|
defineBinding(binding);
|
||||||
}
|
}
|
||||||
} else if (binding instanceof ITypedef) {
|
} else {
|
||||||
defineBinding(binding);
|
defineBindingForFunctionCall(binding, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (functionCallExpression instanceof IASTImplicitNameOwner) {
|
if (functionCallExpression instanceof IASTImplicitNameOwner) {
|
||||||
|
@ -717,7 +719,7 @@ public class BindingClassifier {
|
||||||
for (IASTName name : implicitNames) {
|
for (IASTName name : implicitNames) {
|
||||||
binding = name.resolveBinding();
|
binding = name.resolveBinding();
|
||||||
if (binding instanceof IFunction) {
|
if (binding instanceof IFunction) {
|
||||||
defineForFunctionCall((IFunction) binding, functionCallExpression.getArguments());
|
defineForFunctionCall((IFunction) binding, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,6 +788,20 @@ public class BindingClassifier {
|
||||||
return PROCESS_CONTINUE;
|
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
|
@Override
|
||||||
public int leave(IASTName name) {
|
public int leave(IASTName name) {
|
||||||
if (isPartOfExternalMacroDefinition(name))
|
if (isPartOfExternalMacroDefinition(name))
|
||||||
|
@ -1085,16 +1101,7 @@ public class BindingClassifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRequiredBindings(IBinding binding, Deque<IBinding> newBindings) {
|
private void addRequiredBindings(IBinding binding, Deque<IBinding> newBindings) {
|
||||||
if (binding instanceof ICPPMethod) {
|
if (binding instanceof IProblemBinding) {
|
||||||
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) {
|
|
||||||
IProblemBinding problemBinding = (IProblemBinding) binding;
|
IProblemBinding problemBinding = (IProblemBinding) binding;
|
||||||
|
|
||||||
IBinding[] candidateBindings = problemBinding.getCandidateBindings();
|
IBinding[] candidateBindings = problemBinding.getCandidateBindings();
|
||||||
|
@ -1113,6 +1120,15 @@ public class BindingClassifier {
|
||||||
} catch (CoreException e) {
|
} 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 {
|
} else {
|
||||||
newBindings.add(binding);
|
newBindings.add(binding);
|
||||||
}
|
}
|
||||||
|
@ -1175,7 +1191,9 @@ public class BindingClassifier {
|
||||||
*/
|
*/
|
||||||
private boolean canForwardDeclare(IBinding binding) {
|
private boolean canForwardDeclare(IBinding binding) {
|
||||||
boolean canDeclare = false;
|
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.
|
return true; // Return true to consider delegates later on.
|
||||||
} if (binding instanceof ICompositeType) {
|
} if (binding instanceof ICompositeType) {
|
||||||
canDeclare = fPreferences.forwardDeclareCompositeTypes;
|
canDeclare = fPreferences.forwardDeclareCompositeTypes;
|
||||||
|
|
|
@ -40,6 +40,8 @@ import org.eclipse.text.edits.MultiTextEdit;
|
||||||
import com.ibm.icu.text.Collator;
|
import com.ibm.icu.text.Collator;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
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.IASTDeclaration;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
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.IEnumeration;
|
||||||
import org.eclipse.cdt.core.dom.ast.IEnumerator;
|
import org.eclipse.cdt.core.dom.ast.IEnumerator;
|
||||||
import org.eclipse.cdt.core.dom.ast.IFunction;
|
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.IType;
|
||||||
import org.eclipse.cdt.core.dom.ast.ITypedef;
|
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.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.ICPPASTQualifiedName;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
|
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.ICPPASTUsingDirective;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
|
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.ICPPConstructor;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
|
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.ICPPSpecialization;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
|
||||||
import org.eclipse.cdt.core.index.IIndex;
|
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.IRequiredInclude;
|
||||||
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
|
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.CPPVisitor;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
|
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());
|
final ArrayList<IncludeCandidate> candidates = new ArrayList<>(candidatesMap.values());
|
||||||
if (candidates.size() > 1) {
|
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)
|
if (candidate == null)
|
||||||
return rootEdit;
|
return rootEdit;
|
||||||
candidates.clear();
|
candidates.clear();
|
||||||
|
@ -226,6 +240,69 @@ public class IncludeCreator {
|
||||||
return createEdit(requiredIncludes, usingDeclarations, ast, selection);
|
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,
|
private MultiTextEdit createEdit(List<IncludeInfo> includes,
|
||||||
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
|
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
|
||||||
ITextSelection selection) {
|
ITextSelection selection) {
|
||||||
|
@ -497,11 +574,63 @@ public class IncludeCreator {
|
||||||
return name.getLastName().toString().equals(usingChain.get(usingChain.size() - 1 - qualifiers.length));
|
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.
|
* Returns components of the qualified name in reverse order.
|
||||||
* For ns1::ns2::Name, e.g., it returns [Name, ns2, ns1].
|
* For ns1::ns2::Name, e.g., it returns [Name, ns2, ns1].
|
||||||
*/
|
*/
|
||||||
private ArrayList<String> getUsingChain(IBinding binding) {
|
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);
|
ArrayList<String> chain = new ArrayList<>(4);
|
||||||
for (; binding != null; binding = binding.getOwner()) {
|
for (; binding != null; binding = binding.getOwner()) {
|
||||||
String name = binding.getName();
|
String name = binding.getName();
|
||||||
|
|
Loading…
Add table
Reference in a new issue