1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 472818 - Numerous improvements to HeuristicResolver

Notable improvements include:
  - better handling of typedefs and reference types
  - support for specialization of dependent lookup results
  - support for nested templates

Change-Id: Id7b20e40bf31507b4e1743a7ae5898b1a85ade17
This commit is contained in:
Nathan Ridge 2016-01-31 23:25:15 -05:00
parent 6df72650e4
commit 453e429d5c
3 changed files with 340 additions and 42 deletions

View file

@ -273,8 +273,59 @@ public class CPPTemplates {
return e.getProblem();
}
}
/**
* Instantiates an alias template with the given arguments.
*/
public static IBinding instantiateAliasTemplate(ICPPAliasTemplate aliasTemplate,
ICPPTemplateArgument[] args, IASTNode point) {
try {
args = addDefaultArguments(aliasTemplate, args, point);
if (args == null) {
return createProblem(aliasTemplate, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, point);
}
ICPPTemplateParameterMap parameterMap = createParameterMap(aliasTemplate, args, point);
if (parameterMap == null) {
return createProblem(aliasTemplate, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, point);
}
IType aliasedType = aliasTemplate.getType();
IBinding owner = aliasTemplate.getOwner();
return createAliasTemplaceInstance(aliasTemplate, args, parameterMap, aliasedType, owner, point);
} catch (DOMException e) {
return e.getProblem();
}
}
/**
* Instantiate a specialization of an alias template with the given arguments.
*
* TODO(nathanridge): The reason we have this method is that we (incorrectly) represent
* specializations of alias templates as alias template instances. A specialization of
* an alias template is an alias template, so it needs to be instantiated to produce
* an actual alias template instance. Actual alias template instances do not need to be
* instantiated.
*/
public static IBinding instantiateAliasTemplateInstance(ICPPAliasTemplateInstance aliasTemplateInstance,
ICPPTemplateArgument[] args, IASTNode point) {
ICPPAliasTemplate aliasTemplate = aliasTemplateInstance.getTemplateDefinition();
try {
args = addDefaultArguments(aliasTemplate, args, point);
if (args == null) {
return createProblem(aliasTemplate, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, point);
}
ICPPTemplateParameterMap parameterMap = createParameterMap(aliasTemplate, args, point);
if (parameterMap == null) {
return createProblem(aliasTemplate, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, point);
}
IType aliasedType = aliasTemplateInstance.getType();
IBinding owner = aliasTemplateInstance.getOwner();
return createAliasTemplaceInstance(aliasTemplate, args, parameterMap, aliasedType, owner, point);
} catch (DOMException e) {
return e.getProblem();
}
}
private static IBinding createProblem(ICPPPartiallySpecializable template, int id, IASTNode point) {
private static IBinding createProblem(ICPPTemplateDefinition template, int id, IASTNode point) {
return new ProblemBinding(point, id, template.getNameCharArray());
}
@ -717,35 +768,17 @@ public class CPPTemplates {
if (template instanceof ICPPAliasTemplate) {
ICPPAliasTemplate aliasTemplate = (ICPPAliasTemplate) template;
ICPPTemplateArgument[] args = createTemplateArgumentArray(id);
args = addDefaultArguments(aliasTemplate, args, id);
if (args == null) {
return new ProblemBinding(id, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, templateName.toCharArray());
}
CPPTemplateParameterMap parameterMap = createParameterMap(aliasTemplate, args, id);
if (parameterMap == null) {
return new ProblemBinding(id, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, templateName.toCharArray());
}
IType aliasedType = aliasTemplate.getType();
IBinding owner = template.getOwner();
return createAliasTemplaceInstance(aliasTemplate, args, parameterMap, aliasedType, owner, id);
return instantiateAliasTemplate(aliasTemplate, args, id);
}
// Alias template instance.
if (template instanceof ICPPAliasTemplateInstance) {
// TODO(nathanridge): Remove this branch once we properly represent
// specializations of alias templates (which will then implement
// ICPPAliasTemplate and be caught by the previous branch).
ICPPAliasTemplateInstance aliasTemplateInstance = (ICPPAliasTemplateInstance) template;
ICPPTemplateArgument[] args = createTemplateArgumentArray(id);
ICPPAliasTemplate aliasTemplate = aliasTemplateInstance.getTemplateDefinition();
args = addDefaultArguments(aliasTemplate, args, id);
if (args == null) {
return new ProblemBinding(id, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, templateName.toCharArray());
}
CPPTemplateParameterMap parameterMap = createParameterMap(aliasTemplate, args, id);
if (parameterMap == null) {
return new ProblemBinding(id, IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS, templateName.toCharArray());
}
IType aliasedType = aliasTemplateInstance.getType();
IBinding owner = aliasTemplateInstance.getOwner();
return createAliasTemplaceInstance(aliasTemplate, args, parameterMap, aliasedType, owner, id);
return instantiateAliasTemplateInstance(aliasTemplateInstance, args, id);
}
// Class or variable template.
@ -817,9 +850,9 @@ public class CPPTemplates {
return e.getProblem();
}
}
private static IBinding createAliasTemplaceInstance(ICPPAliasTemplate aliasTemplate,
ICPPTemplateArgument[] args, CPPTemplateParameterMap parameterMap, IType aliasedType,
ICPPTemplateArgument[] args, ICPPTemplateParameterMap parameterMap, IType aliasedType,
IBinding owner, IASTNode point) {
InstantiationContext context = createInstantiationContext(parameterMap, owner, point);
IType instantiatedType = instantiateType(aliasedType, context);

View file

@ -16,19 +16,30 @@ import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPDeferredClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMember;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMemberClass;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMemberClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
public class HeuristicResolver {
@ -40,7 +51,7 @@ public class HeuristicResolver {
*/
public static IScope findConcreteScopeForType(IType type, IASTNode point) {
if (type instanceof ICPPUnknownType) {
type = resolveUnknownType((ICPPUnknownType) type, point);
type = resolveUnknownType((ICPPUnknownType) type, point, SemanticUtil.TDEF | SemanticUtil.REF);
}
if (type instanceof ICompositeType) {
return ((ICompositeType) type).getCompositeScope();
@ -48,10 +59,103 @@ public class HeuristicResolver {
return null;
}
/**
* Helper function for lookInside().
* Specializes the given bindings in the given context.
*
* @param point the point of instantiation for name lookups
*/
private static IBinding[] specializeBindings(IBinding[] bindings, ICPPClassSpecialization context,
IASTNode point) {
IBinding[] result = new IBinding[bindings.length];
for (int i = 0; i < bindings.length; ++i) {
result[i] = context.specializeMember(bindings[i], point);
}
return result;
}
/**
* An extension of CPPDeferredClassInstance that implements ICPPClassSpecialization,
* allowing its members to be specialized via specializeMember().
*/
private static class CPPDependentClassInstance extends CPPDeferredClassInstance
implements ICPPClassSpecialization {
public CPPDependentClassInstance(ICPPDeferredClassInstance deferredInstance) {
super(deferredInstance.getClassTemplate(), deferredInstance.getTemplateArguments());
}
@Override
public ICPPClassType getSpecializedBinding() {
return (ICPPClassType) super.getSpecializedBinding();
}
// This overload of specializeMember() is all we're interested in.
// Everything else is unsupported.
@Override
public IBinding specializeMember(IBinding binding, IASTNode point) {
return CPPTemplates.createSpecialization(this, binding, point);
}
@Override
public IBinding specializeMember(IBinding binding) {
throw new UnsupportedOperationException();
}
@Override
public ICPPBase[] getBases(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPConstructor[] getConstructors(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPField[] getDeclaredFields(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPMethod[] getMethods(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPMethod[] getAllDeclaredMethods(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPMethod[] getDeclaredMethods(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public IBinding[] getFriends(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public IField[] getFields(IASTNode point) {
throw new UnsupportedOperationException();
}
@Override
public ICPPClassType[] getNestedClasses(IASTNode point) {
throw new UnsupportedOperationException();
}
}
/**
* Helper function for resolveUnknownType() and resolveUnknownBinding().
* Heuristically resolves the given unknown type and performs name lookup inside it.
*
* If name lookup is performed inside a template scope to approximate lookup
* in the scope of a dependent instantiation, the lookup results are
* specialized in the context of the dependent instantiation.
*
* @param ownerType the type to perform name lookup inside
* @param isPointerDeref true if 'ownerType' is a pointer type
* @param name the name to be looked up
@ -61,23 +165,50 @@ public class HeuristicResolver {
*/
private static IBinding[] lookInside(IType ownerType, boolean isPointerDeref, char[] name,
ICPPTemplateArgument[] templateArgs, IASTNode point) {
// The pointer type might be outside of the dependent type...
// If this is a pointer dereference, the pointer type might be outside of the dependent type.
ownerType = SemanticUtil.getSimplifiedType(ownerType);
if (isPointerDeref && ownerType instanceof IPointerType) {
ownerType = ((IPointerType) ownerType).getType();
isPointerDeref = false;
}
if (ownerType instanceof ICPPUnknownType) {
IType lookupType = resolveUnknownType((ICPPUnknownType) ownerType, point);
// ... or inside the dependent type.
// Here we have a loop similar to the one in resolveUnknownType(), but we stop when
// we get a result that's an ICPPClassSpecialization or an ICPPDeferredClassInstance,
// so we can use it to specialize the lookup results as appropriate.
IType lookupType;
ICPPClassSpecialization specializationContext = null;
while (true) {
if (ownerType instanceof ICPPClassSpecialization) {
specializationContext = (ICPPClassSpecialization) ownerType;
lookupType = specializationContext.getSpecializedBinding();
break;
} else if (ownerType instanceof ICPPDeferredClassInstance) {
specializationContext = new CPPDependentClassInstance(
(ICPPDeferredClassInstance) ownerType);
lookupType = specializationContext.getSpecializedBinding();
break;
}
IType resolvedType = resolveUnknownTypeOnce((ICPPUnknownType) ownerType, point);
resolvedType = SemanticUtil.getNestedType(resolvedType, SemanticUtil.TDEF | SemanticUtil.REF);
if (resolvedType == ownerType || !(resolvedType instanceof ICPPUnknownType)) {
lookupType = resolvedType;
break;
} else {
ownerType = resolvedType;
continue;
}
}
// If this is a pointer dereference, and the pointer type wasn't outside the
// dependent type, it might be inside the dependent type.
if (isPointerDeref) {
lookupType = SemanticUtil.getSimplifiedType(lookupType);
if (lookupType instanceof IPointerType) {
lookupType = ((IPointerType) lookupType).getType();
} else {
lookupType = null;
}
}
IScope lookupScope = null;
if (lookupType instanceof ICPPClassType) {
lookupScope = ((ICPPClassType) lookupType).getCompositeScope();
@ -91,6 +222,9 @@ public class HeuristicResolver {
CPPSemantics.lookup(lookup, lookupScope);
IBinding[] foundBindings = lookup.getFoundBindings();
if (foundBindings.length > 0) {
if (specializationContext != null) {
foundBindings = specializeBindings(foundBindings, specializationContext, point);
}
return foundBindings;
}
} catch (DOMException e) {
@ -102,7 +236,7 @@ public class HeuristicResolver {
/**
* Helper function for resolveUnknownType().
* Returns the type of a binding, or if the binding is a type, that type.
* Returns the type of a binding.
*/
private static IType typeForBinding(IBinding binding) {
if (binding instanceof IType) {
@ -118,16 +252,47 @@ public class HeuristicResolver {
}
/**
* Given an unknown type, heuristically tries to find a concrete type (i.e. not an unknown type)
* corresponding to it.
* Given an unknown type, heuristically tries to find a concrete type
* (i.e. not an unknown type) corresponding to it.
*
* Returns null if no heuristic resolution could be performed.
*
* Multiple rounds of resolution are performed, as the result of a single
* round may yield a type which is still dependent. Resolution stops when
* a concrete type is found, or the resolution of the last resolution
* round is the same as the result of the previous resolution round.
* In between each round, typedefs are unwrapped.
*
* @param point the point of instantiation for lookups
*/
private static IType resolveUnknownType(ICPPUnknownType type, IASTNode point) {
return resolveUnknownType(type, point, SemanticUtil.TDEF);
}
/**
* Similar to resolveUnknownType(type, point), but allows specifying
* things other than typedefs to unwrap between rounds of resolution
* (e.g. references).
*/
private static IType resolveUnknownType(ICPPUnknownType type, IASTNode point, int unwrapOptions) {
while (true) {
IType resolvedType = resolveUnknownTypeOnce(type, point);
resolvedType = SemanticUtil.getNestedType(resolvedType, unwrapOptions);
if (resolvedType != type && resolvedType instanceof ICPPUnknownType) {
type = (ICPPUnknownType) resolvedType;
continue;
}
return resolvedType;
}
}
/**
* Helper function for resolveUnknownType() which does one round of resolution.
*/
private static IType resolveUnknownTypeOnce(ICPPUnknownType type, IASTNode point) {
if (type instanceof ICPPDeferredClassInstance) {
return ((ICPPDeferredClassInstance) type).getClassTemplate();
ICPPDeferredClassInstance deferredInstance = (ICPPDeferredClassInstance) type;
return deferredInstance.getClassTemplate();
} else if (type instanceof TypeOfDependentExpression) {
ICPPEvaluation evaluation = ((TypeOfDependentExpression) type).getEvaluation();
if (evaluation instanceof EvalUnary) {
@ -138,7 +303,6 @@ public class HeuristicResolver {
IType argument = unary.getArgument().getType(point);
if (argument instanceof ICPPUnknownType) {
IType resolved = resolveUnknownType((ICPPUnknownType) argument, point);
resolved = SemanticUtil.getSimplifiedType(resolved);
if (resolved instanceof IPointerType) {
return ((IPointerType) resolved).getType();
}
@ -148,8 +312,9 @@ public class HeuristicResolver {
EvalID id = (EvalID) evaluation;
ICPPEvaluation fieldOwner = id.getFieldOwner();
if (fieldOwner != null) {
IBinding[] candidates = lookInside(fieldOwner.getType(point),
id.isPointerDeref(), id.getName(), id.getTemplateArgs(), point);
IType fieldOwnerType = fieldOwner.getType(point);
IBinding[] candidates = lookInside(fieldOwnerType, id.isPointerDeref(), id.getName(),
id.getTemplateArgs(), point);
if (candidates.length == 1) {
return typeForBinding(candidates[0]);
}
@ -166,11 +331,27 @@ public class HeuristicResolver {
// TODO(nathanridge): Handle more cases.
} else if (type instanceof ICPPUnknownMemberClass) {
ICPPUnknownMemberClass member = (ICPPUnknownMemberClass) type;
IBinding[] candidates = lookInside(member.getOwnerType(), false, member.getNameCharArray(),
null, point);
IType ownerType = member.getOwnerType();
IBinding[] candidates = lookInside(ownerType, false, member.getNameCharArray(), null, point);
if (candidates.length == 1) {
if (candidates[0] instanceof IType) {
return (IType) candidates[0];
IType result = (IType) candidates[0];
if (type instanceof ICPPUnknownMemberClassInstance) {
ICPPTemplateArgument[] args = ((ICPPUnknownMemberClassInstance) type).getArguments();
if (result instanceof ICPPClassTemplate) {
result = (IType) CPPTemplates.instantiate((ICPPClassTemplate) result, args, point);
} else if (result instanceof ICPPAliasTemplate) {
result = (IType) CPPTemplates.instantiateAliasTemplate((ICPPAliasTemplate) result,
args, point);
} else if (result instanceof ICPPAliasTemplateInstance) {
// TODO(nathanridge): Remove this branch once we properly represent
// specializations of alias templates (which will then implement
// ICPPAliasTemplate and be caught by the previous branch).
result = (IType) CPPTemplates.instantiateAliasTemplateInstance(
(ICPPAliasTemplateInstance) result, args, point);
}
}
return result;
}
}
}

View file

@ -1736,4 +1736,88 @@ public class CompletionTests extends AbstractContentAssistTest {
final String[] expected = {};
assertCompletionResults(fCursorOffset, expected, ID);
}
// template <typename T>
// struct vector {
// T& front();
// };
// template <class T>
// void foo(vector<vector<T>> a) {
// a.front()./*cursor*/
// }
public void testDependentScopes_bug472818a() throws Exception {
final String[] expected = { "vector<typename T>", "front(void)" };
assertCompletionResults(fCursorOffset, expected, ID);
}
// template <typename T>
// struct vector {
// T& front();
// };
// template <class T>
// void foo(vector<vector<vector<T>>> a) {
// a.front().front()./*cursor*/
// }
public void testDependentScopes_bug472818b() throws Exception {
final String[] expected = { "vector<typename T>", "front(void)" };
assertCompletionResults(fCursorOffset, expected, ID);
}
// // This is a simplification of the actual std::vector implementation
// // that ships with gcc 5.1's libstdc++.
// template <typename T>
// struct allocator {
// typedef T value_type;
// template <typename U>
// struct rebind {
// typedef allocator<U> other;
// };
// };
// template <typename Alloc, typename T>
// struct alloctr_rebind {
// typedef typename Alloc::template rebind<T>::other type;
// };
// template <typename Alloc>
// struct allocator_traits {
// typedef typename Alloc::value_type value_type;
// template <typename T>
// using rebind_alloc = typename alloctr_rebind<Alloc, T>::type;
// };
// template <typename Alloc>
// struct alloc_traits {
// typedef allocator_traits<Alloc> base_type;
// typedef typename base_type::value_type value_type;
// typedef value_type& reference;
// template <typename T>
// struct rebind {
// typedef typename base_type::template rebind_alloc<T> other;
// };
// };
// template <typename T, typename Alloc>
// struct vector_base {
// typedef typename alloc_traits<Alloc>::template rebind<T>::other allocator_type;
// };
// template <typename T, typename Alloc = allocator<T>>
// struct vector {
// typedef vector_base<T, Alloc> base_type;
// typedef typename base_type::allocator_type allocator_type;
// typedef alloc_traits<allocator_type> alloc_traits_type;
// typedef typename alloc_traits_type::reference reference;
// reference front();
// };
// template <class T>
// void foo(vector<vector<vector<T>>> a) {
// a.front().front()./*cursor*/
// }
public void testDependentScopes_bug472818c() throws Exception {
final String[] expected = {
"vector<typename T, typename Alloc = allocator<T>>",
"base_type",
"allocator_type",
"alloc_traits_type",
"reference",
"front(void)"
};
assertCompletionResults(fCursorOffset, expected, ID);
}
}