| /******************************************************************************* |
| * Copyright (c) 2015 Nathan Ridge. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Nathan Ridge - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.cdt.core.dom.ast.DOMException; |
| import org.eclipse.cdt.core.dom.ast.IASTNode; |
| 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.IQualifierType; |
| 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.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.core.dom.ast.cpp.ICPPTemplateInstance; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; |
| import org.eclipse.cdt.core.parser.util.CharArrayUtils; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPDeferredClassInstance; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType; |
| 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; |
| |
| /** |
| * The purpose of this class is to perform heuristic binding resolution |
| * in contexts where the results of ordinary binding resolution (whose |
| * approach to templates is "defer actual resolution until template |
| * arguments become available") are undesirable. |
| * |
| * Usually, this comes up in cases where the user is trying to invoke |
| * certain editor functionality inside a template. |
| * |
| * For example, consider the following code: |
| * |
| * struct Cat { |
| * void meow(); |
| * }; |
| * |
| * template <typename T> |
| * struct B { |
| * Cat foo(); |
| * }; |
| * |
| * template <typename T> |
| * void foo(B<T> a) { |
| * a.foo(). |
| * } |
| * |
| * and suppose content assist is invoked after the "a.foo().". |
| * To determine what completions to provide in that context, we try |
| * to determine the type of 'a.foo()', and then look to see what |
| * members are inside that type. |
| * |
| * However, because we're in a template, the type of 'a.foo()' is |
| * a deferred / unknown type (in this case, a TypeOfDependentExpression), |
| * so we don't know what members it has. |
| * |
| * HeuristicResolver maps that unknown type to a concrete type |
| * (in this case, 'Cat') by applying the following heuristic: |
| * whenever name lookup is deferred because the lookup scope is |
| * the scope of a dependent template instantiation, assume the |
| * instantiation uses the primary template (as opposed to a partial |
| * or explicit specialization), and perform the lookup in the |
| * primary template scope. This heuristic gives the right answer |
| * in many cases, including this one. |
| * |
| * HeuristicResolver can handle some more complex situations as well, |
| * such as metafunction calls, typedefs, and nested templates. See |
| * CompletionTests.testDependentScopes_bug472818c for a test case |
| * that pushes it to its limit. |
| * |
| * However, due to the nature of its heuristic, it cannot handle |
| * cases where the correct answer requires selecting a specialization |
| * rather than the primary template. Bug 487700 is on file for |
| * implementing more advanced heuristics that could deal with this. |
| */ |
| public class HeuristicResolver { |
| /** |
| * Given a dependent type, heuristically tries to find a concrete scope (i.e. not an unknown scope) |
| * for it. |
| */ |
| public static IScope findConcreteScopeForType(IType type) { |
| if (type instanceof ICPPUnknownType) { |
| type = resolveUnknownType((ICPPUnknownType) type, SemanticUtil.TDEF | SemanticUtil.REF | SemanticUtil.CVTYPE); |
| } |
| type = SemanticUtil.getNestedType(type, SemanticUtil.PTR); |
| if (type instanceof ICompositeType) { |
| return ((ICompositeType) type).getCompositeScope(); |
| } else if (type instanceof ICPPEnumeration) { |
| return ((ICPPEnumeration) type).asScope(); |
| } |
| return null; |
| } |
| |
| /** |
| * Helper function for lookInside(). |
| * Specializes the given bindings in the given context. |
| */ |
| private static IBinding[] specializeBindings(IBinding[] bindings, ICPPClassSpecialization context) { |
| IBinding[] result = new IBinding[bindings.length]; |
| for (int i = 0; i < bindings.length; ++i) { |
| result[i] = context.specializeMember(bindings[i]); |
| } |
| 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(chooseTemplateForDeferredInstance(deferredInstance), |
| deferredInstance.getTemplateArguments()); |
| } |
| |
| @Override |
| public ICPPClassType getSpecializedBinding() { |
| return (ICPPClassType) super.getSpecializedBinding(); |
| } |
| |
| @Override |
| @Deprecated |
| public IBinding specializeMember(IBinding binding, IASTNode point) { |
| return specializeMember(binding); |
| } |
| |
| // This overload of specializeMember() is all we're interested in. |
| // Everything else is unsupported. |
| @Override |
| public IBinding specializeMember(IBinding binding) { |
| return CPPTemplates.createSpecialization(this, binding); |
| } |
| |
| @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(); |
| } |
| |
| @Override |
| public ICPPUsingDeclaration[] getUsingDeclarations(IASTNode point) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * Represents a lookup of a name in a primary template scope. |
| * The set of such lookups during a heuristic resolution operation is |
| * tracked, to avoid infinite recursion. |
| */ |
| private static class HeuristicLookup { |
| public IScope scope; |
| public char[] name; |
| |
| public HeuristicLookup(IScope scope, char[] name) { |
| this.scope = scope; |
| this.name = name; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (!(other instanceof HeuristicLookup)) { |
| return false; |
| } |
| HeuristicLookup otherLookup = (HeuristicLookup) other; |
| return scope == otherLookup.scope |
| && CharArrayUtils.equals(name, otherLookup.name); |
| } |
| |
| @Override |
| public int hashCode() { |
| return scope.hashCode() * (29 + name.hashCode()); |
| } |
| } |
| |
| /** |
| * 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 |
| * @param templateArgs template arguments following the name, if any |
| * @param lookupSet the set of lookups performed so far; lookups during this call are added to this |
| * @param point point of instantiation for name lookups |
| * @return results of the name lookup |
| */ |
| private static IBinding[] lookInside(IType ownerType, boolean isPointerDeref, char[] name, |
| ICPPTemplateArgument[] templateArgs, Set<HeuristicLookup> lookupSet) { |
| // 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 IQualifierType) { |
| ownerType = ((IQualifierType) ownerType).getType(); |
| } |
| |
| IType lookupType = ownerType; |
| ICPPClassSpecialization specializationContext = null; |
| if (lookupType instanceof ICPPUnknownType) { |
| // 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. |
| while (true) { |
| if (lookupType instanceof ICPPClassSpecialization) { |
| specializationContext = (ICPPClassSpecialization) lookupType; |
| lookupType = specializationContext.getSpecializedBinding(); |
| break; |
| } else if (lookupType instanceof ICPPDeferredClassInstance) { |
| specializationContext = new CPPDependentClassInstance( |
| (ICPPDeferredClassInstance) lookupType); |
| lookupType = specializationContext.getSpecializedBinding(); |
| break; |
| } |
| IType resolvedType = resolveUnknownTypeOnce((ICPPUnknownType) lookupType, lookupSet); |
| resolvedType = SemanticUtil.getNestedType(resolvedType, |
| SemanticUtil.TDEF | SemanticUtil.REF | SemanticUtil.CVTYPE); |
| |
| // 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) { |
| if (resolvedType instanceof IPointerType) { |
| isPointerDeref = false; |
| resolvedType = ((IPointerType) resolvedType).getType(); |
| } else { |
| resolvedType = null; |
| } |
| } |
| |
| resolvedType = SemanticUtil.getNestedType(resolvedType, |
| SemanticUtil.CVTYPE | SemanticUtil.TDEF); |
| |
| if (resolvedType == lookupType || !(resolvedType instanceof ICPPUnknownType)) { |
| lookupType = resolvedType; |
| break; |
| } else { |
| lookupType = resolvedType; |
| continue; |
| } |
| } |
| |
| } |
| |
| IScope lookupScope = null; |
| if (lookupType instanceof ICPPClassType) { |
| lookupScope = ((ICPPClassType) lookupType).getCompositeScope(); |
| } else if (lookupType instanceof ICPPEnumeration) { |
| lookupScope = ((ICPPEnumeration) lookupType).asScope(); |
| } |
| if (lookupScope != null) { |
| HeuristicLookup entry = new HeuristicLookup(lookupScope, name); |
| if (lookupSet.add(entry)) { |
| LookupData lookup = new LookupData(name, templateArgs, CPPSemantics.getCurrentLookupPoint()); |
| lookup.fHeuristicBaseLookup = true; |
| try { |
| CPPSemantics.lookup(lookup, lookupScope); |
| IBinding[] foundBindings = lookup.getFoundBindings(); |
| if (foundBindings.length > 0) { |
| if (specializationContext != null) { |
| foundBindings = specializeBindings(foundBindings, specializationContext); |
| } |
| return foundBindings; |
| } |
| } catch (DOMException e) { |
| } |
| } |
| } |
| return IBinding.EMPTY_BINDING_ARRAY; |
| } |
| |
| /** |
| * Helper function for resolveUnknownType(). |
| * Returns the type of a binding. |
| */ |
| private static IType typeForBinding(IBinding binding) { |
| if (binding instanceof IType) { |
| return (IType) binding; |
| } else if (binding instanceof IVariable) { |
| return ((IVariable) binding).getType(); |
| } else if (binding instanceof IEnumerator) { |
| return ((IEnumerator) binding).getType(); |
| } else if (binding instanceof IFunction) { |
| return ((IFunction) binding).getType(); |
| } |
| return null; |
| } |
| |
| /** |
| * 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. |
| */ |
| public static IType resolveUnknownType(ICPPUnknownType type) { |
| return resolveUnknownType(type, SemanticUtil.TDEF | SemanticUtil.CVTYPE); |
| } |
| |
| /** |
| * 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, int unwrapOptions) { |
| while (true) { |
| Set<HeuristicLookup> lookupSet = new HashSet<>(); |
| IType resolvedType = resolveUnknownTypeOnce(type, lookupSet); |
| resolvedType = SemanticUtil.getNestedType(resolvedType, unwrapOptions); |
| |
| if (resolvedType != type && resolvedType instanceof ICPPUnknownType) { |
| type = (ICPPUnknownType) resolvedType; |
| continue; |
| } |
| return resolvedType; |
| } |
| } |
| |
| /** |
| * Heuristically choose between the primary template and any partial specializations |
| * for a deferred template instance. |
| */ |
| private static ICPPClassTemplate chooseTemplateForDeferredInstance(ICPPDeferredClassInstance instance) { |
| ICPPClassTemplate template = instance.getClassTemplate(); |
| if (!instance.isExplicitSpecialization()) { |
| try { |
| IBinding partial = CPPTemplates.selectSpecialization(template, |
| instance.getTemplateArguments(), false); |
| if (partial != null && partial instanceof ICPPTemplateInstance) |
| return (ICPPClassTemplate) ((ICPPTemplateInstance) partial).getTemplateDefinition(); |
| } catch (DOMException e) { |
| } |
| } |
| return template; |
| } |
| |
| private static IType resolveEvalType(ICPPEvaluation evaluation, Set<HeuristicLookup> lookupSet) { |
| if (evaluation instanceof EvalUnary) { |
| EvalUnary unary = (EvalUnary) evaluation; |
| // Handle the common case of a dependent type representing the |
| // result of dereferencing another dependent type. |
| if (unary.getOperator() == IASTUnaryExpression.op_star) { |
| IType argument = unary.getArgument().getType(); |
| if (argument instanceof ICPPUnknownType) { |
| IType resolved = resolveUnknownType((ICPPUnknownType) argument); |
| if (resolved instanceof IPointerType) { |
| return ((IPointerType) resolved).getType(); |
| } |
| } |
| } |
| } else if (evaluation instanceof EvalID) { |
| EvalID id = (EvalID) evaluation; |
| ICPPEvaluation fieldOwner = id.getFieldOwner(); |
| if (fieldOwner != null) { |
| IType fieldOwnerType = fieldOwner.getType(); |
| IBinding[] candidates = lookInside(fieldOwnerType, id.isPointerDeref(), id.getName(), |
| id.getTemplateArgs(), lookupSet); |
| if (candidates.length > 0) { |
| // If there is more than one candidate, for now just |
| // choose the first one. A better thing to do would |
| // be to perform heuristic overload resolution (TODO). |
| return typeForBinding(candidates[0]); |
| } |
| } |
| } else if (evaluation instanceof EvalFunctionCall) { |
| EvalFunctionCall evalFunctionCall = (EvalFunctionCall) evaluation; |
| ICPPEvaluation function = evalFunctionCall.getArguments()[0]; |
| IType functionType = function.getType(); |
| if (functionType instanceof ICPPUnknownType) { |
| functionType = resolveUnknownType((ICPPUnknownType) functionType); |
| } |
| return ExpressionTypes.typeFromFunctionCall(functionType); |
| } else if (evaluation instanceof EvalMemberAccess) { |
| IBinding member = ((EvalMemberAccess) evaluation).getMember(); |
| // Presumably the type will be unknown. That's fine, it will be |
| // resolved during subsequent resolution rounds. |
| return typeForBinding(member); |
| } else if (evaluation instanceof EvalTypeId) { |
| EvalTypeId evalTypeId = (EvalTypeId) evaluation; |
| IType result = evalTypeId.getInputType(); |
| if (evalTypeId.representsNewExpression()) { |
| result = new CPPPointerType(result); |
| } |
| return result; |
| } else if (evaluation instanceof EvalBinding) { |
| return evaluation.getType(); |
| } |
| // TODO(nathanridge): Handle more cases. |
| return null; |
| } |
| |
| /** |
| * Helper function for {@link #resolveUnknownType} which does one round of resolution. |
| */ |
| private static IType resolveUnknownTypeOnce(ICPPUnknownType type, Set<HeuristicLookup> lookupSet) { |
| if (type instanceof ICPPDeferredClassInstance) { |
| ICPPDeferredClassInstance deferredInstance = (ICPPDeferredClassInstance) type; |
| return chooseTemplateForDeferredInstance(deferredInstance); |
| } else if (type instanceof TypeOfDependentExpression) { |
| ICPPEvaluation evaluation = ((TypeOfDependentExpression) type).getEvaluation(); |
| return resolveEvalType(evaluation, lookupSet); |
| } else if (type instanceof ICPPUnknownMemberClass) { |
| ICPPUnknownMemberClass member = (ICPPUnknownMemberClass) type; |
| IType ownerType = member.getOwnerType(); |
| IBinding[] candidates = lookInside(ownerType, false, member.getNameCharArray(), null, lookupSet); |
| if (candidates.length == 1) { |
| if (candidates[0] instanceof IType) { |
| 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); |
| } else if (result instanceof ICPPAliasTemplate) { |
| result = (IType) CPPTemplates.instantiateAliasTemplate((ICPPAliasTemplate) result, |
| args); |
| } |
| } |
| return result; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Given an unknown binding, heuristically tries to find concrete bindings (i.e. not unknown bindings) |
| * corresponding to it. |
| * |
| * Returns an empty array if no heuristic resolution could be performed. |
| */ |
| public static IBinding[] resolveUnknownBinding(ICPPUnknownBinding binding) { |
| if (binding instanceof ICPPDeferredClassInstance) { |
| return new IBinding[] { chooseTemplateForDeferredInstance((ICPPDeferredClassInstance) binding) }; |
| } else if (binding instanceof ICPPUnknownMember) { |
| Set<HeuristicLookup> lookupSet = new HashSet<>(); |
| return lookInside(((ICPPUnknownMember) binding).getOwnerType(), false, |
| binding.getNameCharArray(), null, lookupSet); |
| } else if (binding instanceof ICPPUnknownType) { |
| IType resolved = resolveUnknownType((ICPPUnknownType) binding); |
| if (resolved != binding && resolved instanceof IBinding) { |
| return new IBinding[] { (IBinding) resolved }; |
| } |
| } |
| return IBinding.EMPTY_BINDING_ARRAY; |
| } |
| } |