blob: 4cf03a3d0e745ef10c1d5f1512bc844e8e8a8929 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2015 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Markus Schorn (Wind River Systems)
* Bryan Wilkinson (QNX)
* Sergey Prigogin (Google)
* Andrew Ferguson (Symbian)
* Anton Gorenkov
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.core.parser.util.ArrayUtil.appendAt;
import static org.eclipse.cdt.core.parser.util.ArrayUtil.trim;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
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.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
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.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.core.parser.util.ObjectSet;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
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.core.runtime.CoreException;
/**
* Holds common implementation of methods for ICPPClassType implementations that have
* a corresponding textual definition in the source code.
*
* @see CPPClassType
* @see CPPClassTemplate
*/
public class ClassTypeHelper {
public static IBinding[] getFriends(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getFriends();
IASTNode[] declarations= host.getDeclarations();
IASTNode node = (declarations != null && declarations.length != 0) ? declarations[0] : null;
return new IBinding[] { new ProblemBinding(node, IProblemBinding.SEMANTIC_DEFINITION_NOT_FOUND, host.getNameCharArray()) };
}
}
ObjectSet<IBinding> resultSet = new ObjectSet<>(2);
IASTDeclaration[] members = host.getCompositeTypeSpecifier().getMembers();
for (IASTDeclaration decl : members) {
while (decl instanceof ICPPASTTemplateDeclaration) {
decl = ((ICPPASTTemplateDeclaration) decl).getDeclaration();
}
if (decl instanceof IASTSimpleDeclaration) {
ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) ((IASTSimpleDeclaration) decl).getDeclSpecifier();
if (declSpec.isFriend()) {
IASTDeclarator[] dtors = ((IASTSimpleDeclaration) decl).getDeclarators();
if (dtors.length == 0) {
if (declSpec instanceof ICPPASTElaboratedTypeSpecifier) {
resultSet.put(((ICPPASTElaboratedTypeSpecifier) declSpec).getName().resolveBinding());
} else if (declSpec instanceof ICPPASTNamedTypeSpecifier) {
resultSet.put(((ICPPASTNamedTypeSpecifier) declSpec).getName().resolveBinding());
}
} else {
for (IASTDeclarator dtor : dtors) {
if (dtor == null) break;
dtor= ASTQueries.findInnermostDeclarator(dtor);
resultSet.put(dtor.getName().resolveBinding());
}
}
}
} else if (decl instanceof IASTFunctionDefinition) {
ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) ((IASTFunctionDefinition) decl).getDeclSpecifier();
if (declSpec.isFriend()) {
IASTDeclarator dtor = ((IASTFunctionDefinition) decl).getDeclarator();
dtor= ASTQueries.findInnermostDeclarator(dtor);
resultSet.put(dtor.getName().resolveBinding());
}
}
}
return resultSet.keyArray(IBinding.class);
}
/**
* Checks if a binding is a friend of a class. Only classes and functions can be friends of a class.
* A class is considered a friend of itself.
*
* @param binding a binding.
* @param classType a class.
* @return {@code true} if {@code binding} is a friend of {@code classType}.
*/
public static boolean isFriend(IBinding binding, ICPPClassType classType) {
IType type;
if (binding instanceof ICPPClassType) {
type = (IType) binding;
if (type.isSameType(classType)) {
return true;
}
for (IBinding friend : classType.getFriends()) {
if (friend instanceof ICPPClassType && type.isSameType((IType) friend)) {
return true;
}
}
} else if (binding instanceof ICPPFunction) {
type = ((ICPPFunction) binding).getType();
char[] name = binding.getNameCharArray();
for (IBinding friend : classType.getFriends()) {
if (friend instanceof ICPPFunction &&
CharArrayUtils.equals(name, friend.getNameCharArray()) &&
SemanticUtil.haveSameOwner(binding, friend) &&
type.isSameType(((ICPPFunction) friend).getType())) {
return true;
}
}
}
return false;
}
/**
* A host maybe backed up with a definition from the index.
*/
private static ICPPClassType getBackupDefinition(ICPPInternalClassTypeMixinHost host) {
ICPPClassScope scope = host.getCompositeScope();
if (scope != null) {
ICPPClassType b = scope.getClassType();
if (!(b instanceof ICPPInternalClassTypeMixinHost))
return b;
}
return null;
}
public static ICPPBase[] getBases(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getBases();
return ICPPBase.NO_BASES_BECAUSE_TYPE_IS_INCOMPLETE;
}
}
ICPPASTBaseSpecifier[] baseSpecifiers = host.getCompositeTypeSpecifier().getBaseSpecifiers();
if (baseSpecifiers.length == 0)
return ICPPBase.EMPTY_BASE_ARRAY;
ICPPBase[] bases = new ICPPBase[baseSpecifiers.length];
for (int i = 0; i < baseSpecifiers.length; i++) {
bases[i] = new CPPBaseClause(baseSpecifiers[i]);
}
return bases;
}
public static ICPPField[] getDeclaredFields(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getDeclaredFields();
return ICPPField.EMPTY_CPPFIELD_ARRAY;
}
}
IBinding binding = null;
ICPPField[] result = ICPPField.EMPTY_CPPFIELD_ARRAY;
int resultSize = 0;
IASTDeclaration[] decls = host.getCompositeTypeSpecifier().getMembers();
for (IASTDeclaration decl : decls) {
if (decl instanceof IASTSimpleDeclaration) {
IASTDeclarator[] dtors = ((IASTSimpleDeclaration) decl).getDeclarators();
for (IASTDeclarator dtor : dtors) {
binding = ASTQueries.findInnermostDeclarator(dtor).getName().resolveBinding();
if (binding instanceof ICPPField)
result = ArrayUtil.appendAt(result, resultSize++, (ICPPField) binding);
}
} else if (decl instanceof ICPPASTUsingDeclaration) {
IASTName n = ((ICPPASTUsingDeclaration) decl).getName();
binding = n.resolveBinding();
if (binding instanceof ICPPUsingDeclaration) {
IBinding[] bs = ((ICPPUsingDeclaration) binding).getDelegates();
for (IBinding element : bs) {
if (element instanceof ICPPField)
result = ArrayUtil.appendAt(result, resultSize++, (ICPPField) element);
}
} else if (binding instanceof ICPPField) {
result = ArrayUtil.appendAt(result, resultSize++, (ICPPField) binding);
}
}
}
return ArrayUtil.trim(result, resultSize);
}
/**
* Returns all direct and indirect base classes.
*
* @param classType a class
* @return An array of base classes in arbitrary order.
*/
public static ICPPClassType[] getAllBases(ICPPClassType classType) {
Set<ICPPClassType> result= new HashSet<>();
result.add(classType);
getAllBases(classType, result);
result.remove(classType);
return result.toArray(new ICPPClassType[result.size()]);
}
private static void getAllBases(ICPPClassType classType, Set<ICPPClassType> result) {
ICPPBase[] bases= classType.getBases();
for (ICPPBase base : bases) {
IBinding b= base.getBaseClass();
if (b instanceof ICPPClassType) {
final ICPPClassType baseClass = (ICPPClassType) b;
if (result.add(baseClass)) {
getAllBases(baseClass, result);
}
}
}
}
/**
* Returns all (direct or indirect) virtual base classes of {@code classType}.
*/
public static ICPPClassType[] getVirtualBases(ICPPClassType classType) {
Set<ICPPClassType> virtualBases = new HashSet<>();
Set<ICPPClassType> nonvirtualBases = new HashSet<>();
nonvirtualBases.add(classType);
getVirtualBases(classType, virtualBases, nonvirtualBases);
return virtualBases.toArray(new ICPPClassType[virtualBases.size()]);
}
/**
* Helper function for {@link #getVirtualBases(ICPPClassType)}.
*/
private static void getVirtualBases(ICPPClassType classType, Set<ICPPClassType> virtualBases,
Set<ICPPClassType> nonvirtualBases) {
ICPPBase[] bases = classType.getBases();
for (ICPPBase base : bases) {
IBinding b = base.getBaseClass();
if (b instanceof ICPPClassType) {
final ICPPClassType baseClass = (ICPPClassType) b;
if (base.isVirtual()) {
if (virtualBases.add(baseClass)) {
getVirtualBases(baseClass, virtualBases, nonvirtualBases);
}
} else {
// A non-virtual base could have virtual bases in its hierarchy.
if (nonvirtualBases.add(baseClass)) {
getVirtualBases(baseClass, virtualBases, nonvirtualBases);
}
}
}
}
}
/**
* Checks inheritance relationship between two classes.
*
* @return {@code true} if {@code subclass} is a subclass of {@code superclass}.
*/
public static boolean isSubclass(ICPPClassType subclass, ICPPClassType superclass) {
ICPPBase[] bases= subclass.getBases();
for (ICPPBase base : bases) {
IBinding b= base.getBaseClass();
if (b instanceof ICPPClassType) {
ICPPClassType baseClass = (ICPPClassType) b;
if (baseClass.isSameType(superclass)) {
return true;
}
if (isSubclass(baseClass, superclass)) {
return true;
}
}
}
return false;
}
public static ICPPMethod[] getAllDeclaredMethods(ICPPClassType ct) {
ICPPMethod[] methods= ct.getDeclaredMethods();
ICPPClassType[] bases= getAllBases(ct);
for (ICPPClassType base : bases) {
methods = ArrayUtil.addAll(ICPPMethod.class, methods, base.getDeclaredMethods());
}
return ArrayUtil.trim(ICPPMethod.class, methods);
}
public static ICPPMethod[] getMethods(ICPPClassType ct) {
ObjectSet<ICPPMethod> set = getOwnMethods(ct);
ICPPClassType[] bases= getAllBases(ct);
for (ICPPClassType base : bases) {
set.addAll(base.getDeclaredMethods());
set.addAll(getImplicitMethods(base));
}
return set.keyArray(ICPPMethod.class);
}
/**
* Returns methods either declared by the given class or generated by the compiler. Does not
* include methods declared in base classes.
*/
public static ObjectSet<ICPPMethod> getOwnMethods(ICPPClassType classType) {
ObjectSet<ICPPMethod> set= new ObjectSet<>(4);
set.addAll(classType.getDeclaredMethods());
set.addAll(getImplicitMethods(classType));
return set;
}
public static ICPPMethod[] getImplicitMethods(ICPPClassType classType) {
return getImplicitMethods(classType.getCompositeScope());
}
public static ICPPMethod[] getImplicitMethods(IScope scope) {
if (scope instanceof ICPPClassScope) {
return ((ICPPClassScope) scope).getImplicitMethods();
}
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
}
public static ICPPMethod[] getDeclaredMethods(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getDeclaredMethods();
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
}
}
IBinding binding = null;
ICPPMethod[] result = ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
int resultSize = 0;
IASTDeclaration[] decls = host.getCompositeTypeSpecifier().getMembers();
for (IASTDeclaration decl : decls) {
while (decl instanceof ICPPASTTemplateDeclaration)
decl = ((ICPPASTTemplateDeclaration) decl).getDeclaration();
if (decl instanceof IASTSimpleDeclaration) {
final IASTSimpleDeclaration sdecl = (IASTSimpleDeclaration) decl;
if (!((ICPPASTDeclSpecifier) sdecl.getDeclSpecifier()).isFriend()) {
IASTDeclarator[] dtors = sdecl.getDeclarators();
for (IASTDeclarator dtor : dtors) {
binding = ASTQueries.findInnermostDeclarator(dtor).getName().resolveBinding();
if (binding instanceof ICPPMethod)
result = ArrayUtil.appendAt(result, resultSize++, (ICPPMethod) binding);
}
}
} else if (decl instanceof IASTFunctionDefinition) {
final IASTFunctionDefinition fdef = (IASTFunctionDefinition) decl;
if (!((ICPPASTDeclSpecifier) fdef.getDeclSpecifier()).isFriend()) {
IASTDeclarator dtor = fdef.getDeclarator();
dtor = ASTQueries.findInnermostDeclarator(dtor);
binding = dtor.getName().resolveBinding();
if (binding instanceof ICPPMethod) {
result = ArrayUtil.appendAt(result, resultSize++, (ICPPMethod) binding);
}
}
} else if (decl instanceof ICPPASTUsingDeclaration) {
IASTName n = ((ICPPASTUsingDeclaration) decl).getName();
binding = n.resolveBinding();
if (binding instanceof ICPPUsingDeclaration) {
IBinding[] bs = ((ICPPUsingDeclaration) binding).getDelegates();
for (IBinding element : bs) {
if (element instanceof ICPPMethod)
result = ArrayUtil.appendAt(result, resultSize++, (ICPPMethod) element);
}
} else if (binding instanceof ICPPMethod) {
result = ArrayUtil.appendAt(result, resultSize++, (ICPPMethod) binding);
}
}
}
return ArrayUtil.trim(result, resultSize);
}
/**
* @see ICPPClassType#getConstructors()
*/
public static ICPPConstructor[] getConstructors(ICPPInternalClassTypeMixinHost host) {
ICPPClassScope scope = host.getCompositeScope();
if (scope == null) {
return ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
}
ICPPConstructor[] constructors = scope.getConstructors();
return getAllConstructors(host, constructors);
}
/**
* Returns all constructors for a given class type. The returned constructors include the explicitly
* declared, the implicit, and the inherited ones.
*
* @param classType the class to get the constructors for
* @param declaredAndImplicitConstructors the declared and implicit constructors of the class
* @return an array of all class constructors
*/
public static ICPPConstructor[] getAllConstructors(ICPPClassType classType,
ICPPConstructor[] declaredAndImplicitConstructors) {
IType[][] paramTypes = new IType[declaredAndImplicitConstructors.length][];
for (int i = 0; i < declaredAndImplicitConstructors.length; i++) {
ICPPConstructor ctor = declaredAndImplicitConstructors[i];
paramTypes[i] = ctor.getType().getParameterTypes();
}
ICPPConstructor[] inheritedConstructors = getInheritedConstructors(
(ICPPClassScope) classType.getCompositeScope(), classType.getBases(), paramTypes);
return ArrayUtil.addAll(declaredAndImplicitConstructors, inheritedConstructors);
}
/**
* Returns inherited constructors for a given class scope.
*
* @param scope the composite scope of the class to get the constructors for
* @param bases the base class relationships of the class
* @param existingConstructorParamTypes parameter types of the declared and the implicit constructors
* @return an array of all inherited constructors
*/
public static ICPPConstructor[] getInheritedConstructors(ICPPClassScope scope, ICPPBase[] bases,
IType[][] existingConstructorParamTypes) {
ICPPConstructor[] inheritedConstructors = ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
int n = 0;
for (ICPPBase base : bases) {
if (!base.isInheritedConstructorsSource())
continue;
IBinding baseType = base.getBaseClass();
if (!(baseType instanceof ICPPClassType))
continue;
ICPPClassType baseClass = (ICPPClassType) baseType;
ICPPConstructor[] ctors = baseClass.getConstructors();
for (ICPPConstructor ctor : ctors) {
if (canBeInherited(ctor, baseClass, existingConstructorParamTypes))
inheritedConstructors = appendAt(inheritedConstructors, n++, ctor);
}
}
return trim(inheritedConstructors, n);
}
private static boolean canBeInherited(ICPPConstructor ctor, ICPPClassType baseClass,
IType[][] existingConstructorParamTypes) {
ICPPParameter[] params = ctor.getParameters();
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0136r1.html
// 7.3.3-4 [Note] If a constructor or assignment operator brought from a base class into a derived
// class has the signature of a copy/move constructor or assignment operator for the derived class
// (12.8), the using-declaration does not by itself suppress the implicit declaration of the derived
// class member; the member from the base class is hidden or overridden by the implicitly-declared
// copy/move constructor or assignment operator of the derived class, as described below.
for (int k = Math.max(ctor.getRequiredArgumentCount(), 1); k <= params.length; k++) {
if (k == 1 && isReferenceToClass(params[0].getType(), baseClass)) {
continue; // Skip the copy constructor.
}
if (findMatchingSignature(params, k, existingConstructorParamTypes) < 0) {
return true;
}
}
return false;
}
private static boolean isReferenceToClass(IType type, IType classType) {
type= SemanticUtil.getNestedType(type, TDEF);
if (type instanceof ICPPReferenceType && !((ICPPReferenceType) type).isRValueReference()) {
type= SemanticUtil.getNestedType(type, TDEF|REF|CVTYPE);
return classType.isSameType(type);
}
return false;
}
private static int findMatchingSignature(ICPPParameter[] params, int numParams, IType[][] paramTypes) {
for (int i = 0; i < paramTypes.length; i++) {
if (doParameterTypesMatch(params, numParams, paramTypes[i]))
return i;
}
return -1;
}
private static boolean doParameterTypesMatch(ICPPParameter[] params, int numParams, IType[] types) {
if (numParams != types.length)
return false;
for (int i = 0; i < numParams; i++) {
if (!params[i].getType().isSameType(types[i]))
return false;
}
return true;
}
public static ICPPClassType[] getNestedClasses(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getNestedClasses();
return ICPPClassType.EMPTY_CLASS_ARRAY;
}
}
ICPPClassType[] result = ICPPClassType.EMPTY_CLASS_ARRAY;
int resultSize = 0;
IASTDeclaration[] decls = host.getCompositeTypeSpecifier().getMembers();
for (IASTDeclaration decl : decls) {
while (decl instanceof ICPPASTTemplateDeclaration)
decl = ((ICPPASTTemplateDeclaration) decl).getDeclaration();
if (decl instanceof IASTSimpleDeclaration) {
IBinding binding = null;
IASTDeclSpecifier declSpec = ((IASTSimpleDeclaration) decl).getDeclSpecifier();
if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
binding = ((ICPPASTCompositeTypeSpecifier) declSpec).getName().resolveBinding();
} else if (declSpec instanceof ICPPASTElaboratedTypeSpecifier &&
((IASTSimpleDeclaration) decl).getDeclarators().length == 0) {
binding = ((ICPPASTElaboratedTypeSpecifier) declSpec).getName().resolveBinding();
}
if (binding instanceof ICPPClassType)
result = ArrayUtil.appendAt(result, resultSize++, (ICPPClassType) binding);
}
}
return ArrayUtil.trim(result, resultSize);
}
public static ICPPUsingDeclaration[] getUsingDeclarations(ICPPInternalClassTypeMixinHost host) {
if (host.getDefinition() == null) {
host.checkForDefinition();
if (host.getDefinition() == null) {
ICPPClassType backup= getBackupDefinition(host);
if (backup != null)
return backup.getUsingDeclarations();
return ICPPUsingDeclaration.EMPTY_USING_DECL_ARRAY;
}
}
ICPPUsingDeclaration[] result = ICPPUsingDeclaration.EMPTY_USING_DECL_ARRAY;
int resultSize = 0;
IASTDeclaration[] decls = host.getCompositeTypeSpecifier().getMembers();
for (IASTDeclaration decl : decls) {
if (decl instanceof ICPPASTUsingDeclaration) {
IBinding binding = ((ICPPASTUsingDeclaration) decl).getName().resolveBinding();
if (binding instanceof ICPPUsingDeclaration) {
result = ArrayUtil.appendAt(result, resultSize++, (ICPPUsingDeclaration) binding);
}
}
}
return ArrayUtil.trim(result, resultSize);
}
public static ICPPField[] getFields(ICPPClassType ct) {
ICPPField[] fields = ct.getDeclaredFields();
ICPPClassType[] bases = getAllBases(ct);
for (ICPPClassType base : bases) {
fields = ArrayUtil.addAll(ICPPField.class, fields, base.getDeclaredFields());
}
return ArrayUtil.trim(ICPPField.class, fields);
}
public static IField findField(ICPPClassType ct, String name) {
IBinding[] bindings = CPPSemantics.findBindings(ct.getCompositeScope(), name, true);
IField field = null;
for (IBinding binding : bindings) {
if (binding instanceof IField) {
if (field == null) {
field = (IField) binding;
} else {
IASTNode[] decls= ASTInternal.getDeclarationsOfBinding(ct);
IASTNode node= (decls != null && decls.length > 0) ? decls[0] : null;
return new CPPField.CPPFieldProblem(ct, node, IProblemBinding.SEMANTIC_AMBIGUOUS_LOOKUP, name.toCharArray());
}
}
}
return field;
}
/**
* Returns whether {@code method} is virtual. This is the case if it is declared to be virtual
* or overrides another virtual method.
*/
public static boolean isVirtual(ICPPMethod m) {
if (m instanceof ICPPConstructor)
return false;
if (m.isVirtual())
return true;
final char[] mname= m.getNameCharArray();
final ICPPClassType mcl= m.getClassOwner();
if (mcl != null) {
final ICPPFunctionType mft= m.getType();
ICPPMethod[] allMethods= ClassTypeHelper.getMethods(mcl);
for (ICPPMethod method : allMethods) {
if (CharArrayUtils.equals(mname, method.getNameCharArray()) && functionTypesAllowOverride(mft, method.getType())) {
if (method.isVirtual()) {
return true;
}
}
}
}
return false;
}
/**
* Checks if the function types are consistent enough to be considered overrides.
*/
private static boolean functionTypesAllowOverride(ICPPFunctionType a, ICPPFunctionType b) {
if (a.isConst() != b.isConst() || a.isVolatile() != b.isVolatile() || a.takesVarArgs() != b.takesVarArgs()) {
return false;
}
IType[] paramsA = a.getParameterTypes();
IType[] paramsB = b.getParameterTypes();
if (paramsA.length == 1 && paramsB.length == 0) {
if (!SemanticUtil.isVoidType(paramsA[0]))
return false;
} else if (paramsB.length == 1 && paramsA.length == 0) {
if (!SemanticUtil.isVoidType(paramsB[0]))
return false;
} else if (paramsA.length != paramsB.length) {
return false;
} else {
for (int i = 0; i < paramsA.length; i++) {
if (paramsA[i] == null || ! paramsA[i].isSameType(paramsB[i]))
return false;
}
}
return true;
}
/**
* Returns {@code true} if {@code source} overrides {@code target}.
*/
public static boolean isOverrider(ICPPMethod source, ICPPMethod target) {
if (source instanceof ICPPConstructor || target instanceof ICPPConstructor)
return false;
if (!isVirtual(target))
return false;
if (!functionTypesAllowOverride(source.getType(), target.getType()))
return false;
final ICPPClassType sourceClass= source.getClassOwner();
final ICPPClassType targetClass= target.getClassOwner();
if (sourceClass == null || targetClass == null)
return false;
ICPPClassType[] bases= getAllBases(sourceClass);
for (ICPPClassType base : bases) {
if (base.isSameType(targetClass))
return true;
}
return false;
}
/**
* Returns all methods that are overridden by the given {@code method}.
*/
public static ICPPMethod[] findOverridden(ICPPMethod method) {
if (method instanceof ICPPConstructor)
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
final char[] mname= method.getNameCharArray();
final ICPPClassType mcl= method.getClassOwner();
if (mcl == null)
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
final ArrayList<ICPPMethod> result= new ArrayList<>();
final HashMap<ICPPClassType, Boolean> virtualInClass= new HashMap<>();
final ICPPFunctionType methodType= method.getType();
virtualInClass.put(mcl, method.isVirtual());
ICPPBase[] bases= mcl.getBases();
for (ICPPBase base : bases) {
IBinding b= base.getBaseClass();
if (b instanceof ICPPClassType) {
findOverridden((ICPPClassType) b, mname, methodType, virtualInClass,
result, CPPSemantics.MAX_INHERITANCE_DEPTH);
}
}
// List is filled from most derived up to here, reverse it.
Collections.reverse(result);
return result.toArray(new ICPPMethod[result.size()]);
}
/**
* Searches for overridden methods starting in {@code classType}. The map {@code virtualInClass}
* contains a mapping of classes that have been visited to the information whether they
* (or a base-class) contain an overridden method.
*
* @return whether {@code classType} contains an overridden method.
*/
private static boolean findOverridden(ICPPClassType classType, char[] methodName,
ICPPFunctionType methodType, Map<ICPPClassType, Boolean> virtualInClass,
List<ICPPMethod> result, int maxdepth) {
// Prevent recursion due to a hierarchy of unbounded depth, e.g. A<I> deriving from A<I - 1>.
if (maxdepth <= 0)
return false;
Boolean visitedBefore= virtualInClass.get(classType);
if (visitedBefore != null)
return visitedBefore;
ICPPMethod[] methods= classType.getDeclaredMethods();
ICPPMethod candidate= null;
boolean hasOverridden= false;
for (ICPPMethod method : methods) {
if (methodName[0] == '~' && method.isDestructor()
|| (CharArrayUtils.equals(methodName, method.getNameCharArray())
&& functionTypesAllowOverride(methodType, method.getType()))) {
candidate= method;
hasOverridden= method.isVirtual();
break;
}
}
// Prevent recursion due to a class inheriting (directly or indirectly) from itself.
virtualInClass.put(classType, hasOverridden);
ICPPBase[] bases= classType.getBases();
for (ICPPBase base : bases) {
IBinding b= base.getBaseClass();
if (b instanceof ICPPClassType) {
if (findOverridden((ICPPClassType) b, methodName, methodType, virtualInClass,
result, maxdepth - 1)) {
hasOverridden= true;
}
}
}
if (hasOverridden) {
// The candidate is virtual.
if (candidate != null)
result.add(candidate);
virtualInClass.put(classType, hasOverridden);
}
return hasOverridden;
}
/**
* Returns all methods found in the index, that override the given {@code method}.
*/
public static ICPPMethod[] findOverriders(IIndex index, ICPPMethod method)
throws CoreException {
if (!isVirtual(method))
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
final ICPPClassType mcl= method.getClassOwner();
if (mcl == null)
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
ICPPClassType[] subclasses= getSubClasses(index, mcl);
return findOverriders(subclasses, method);
}
/**
* Returns all methods belonging to the given set of classes that override the given {@code method}.
*/
public static ICPPMethod[] findOverriders(ICPPClassType[] subclasses, ICPPMethod method) {
final char[] mname= method.getNameCharArray();
final ICPPFunctionType mft= method.getType();
final ArrayList<ICPPMethod> result= new ArrayList<>();
for (ICPPClassType subClass : subclasses) {
ICPPMethod[] methods= subClass.getDeclaredMethods();
for (ICPPMethod candidate : methods) {
if (CharArrayUtils.equals(mname, candidate.getNameCharArray()) &&
functionTypesAllowOverride(mft, candidate.getType())) {
result.add(candidate);
}
}
}
return result.toArray(new ICPPMethod[result.size()]);
}
private static ICPPClassType[] getSubClasses(IIndex index, ICPPClassType mcl) throws CoreException {
Deque<ICPPBinding> result= new ArrayDeque<>();
HashSet<String> handled= new HashSet<>();
getSubClasses(index, mcl, result, handled);
result.removeFirst();
return result.toArray(new ICPPClassType[result.size()]);
}
private static void getSubClasses(IIndex index, ICPPBinding classOrTypedef,
Collection<ICPPBinding> result, HashSet<String> handled) throws CoreException {
if (!(classOrTypedef instanceof IType))
return;
final String key = ASTTypeUtil.getType((IType) classOrTypedef, true);
if (!handled.add(key)) {
return;
}
if (classOrTypedef instanceof ICPPClassType) {
result.add(classOrTypedef);
}
// TODO(nathanridge): Also find subclasses referenced via decltype-specifiers rather than names.
IIndexName[] names= index.findNames(classOrTypedef, IIndex.FIND_REFERENCES | IIndex.FIND_DEFINITIONS);
for (IIndexName indexName : names) {
if (indexName.isBaseSpecifier()) {
IIndexName subClassDef= indexName.getEnclosingDefinition();
if (subClassDef != null) {
IBinding subClass= index.findBinding(subClassDef);
if (subClass instanceof ICPPBinding) {
getSubClasses(index, (ICPPBinding) subClass, result, handled);
}
}
}
}
}
public enum MethodKind {
DEFAULT_CTOR,
COPY_CTOR,
MOVE_CTOR,
COPY_ASSIGNMENT_OP,
MOVE_ASSIGNMENT_OP,
DTOR,
OTHER
}
public static MethodKind getMethodKind(ICPPClassType classType, ICPPMethod method) {
if (method instanceof ICPPConstructor) {
final List<IType> params= getTypesOfRequiredParameters(method);
if (params.isEmpty())
return MethodKind.DEFAULT_CTOR;
if (params.size() == 1) {
IType t= SemanticUtil.getNestedType(params.get(0), SemanticUtil.TDEF);
if (SemanticUtil.isVoidType(t))
return MethodKind.DEFAULT_CTOR;
ICPPReferenceType refToClass = getRefToClass(classType, t);
if (refToClass != null)
return refToClass.isRValueReference() ? MethodKind.MOVE_CTOR : MethodKind.COPY_CTOR;
}
return MethodKind.OTHER;
}
if (method.isDestructor())
return MethodKind.DTOR;
if (CharArrayUtils.equals(method.getNameCharArray(), OverloadableOperator.ASSIGN.toCharArray())) {
final List<IType> params= getTypesOfRequiredParameters(method);
if (params.size() == 1) {
IType t= params.get(0);
ICPPReferenceType refToClass = getRefToClass(classType, t);
if (refToClass != null)
return refToClass.isRValueReference() ? MethodKind.MOVE_ASSIGNMENT_OP : MethodKind.COPY_ASSIGNMENT_OP;
}
return MethodKind.OTHER;
}
return MethodKind.OTHER;
}
/**
* Returns types of method parameters that don't have defaults.
*/
private static List<IType> getTypesOfRequiredParameters(ICPPMethod method) {
ICPPParameter[] parameters = method.getParameters();
if (parameters.length == 0)
return Collections.emptyList();
List<IType> types = new ArrayList<>(parameters.length);
for (ICPPParameter parameter : parameters) {
if (!parameter.hasDefaultValue() && !parameter.isParameterPack())
types.add(parameter.getType());
}
return types;
}
/**
* For implicit methods the exception specification is inherited, search it.
*/
public static IType[] getInheritedExceptionSpecification(ICPPMethod implicitMethod) {
// See 15.4.13
ICPPClassType owner= implicitMethod.getClassOwner();
if (owner == null || owner.getBases().length == 0)
return null;
// We use a list as types aren't comparable, and can have duplicates (15.4.6)
MethodKind kind= getMethodKind(owner, implicitMethod);
if (kind == MethodKind.OTHER)
return null;
List<IType> inheritedTypeids = new ArrayList<>();
ICPPClassType[] bases= getAllBases(owner);
for (ICPPClassType base : bases) {
if (!(base instanceof ICPPDeferredClassInstance)) {
ICPPMethod baseMethod= getMethodInClass(base, kind);
if (baseMethod != null) {
IType[] baseExceptionSpec= baseMethod.getExceptionSpecification();
if (baseExceptionSpec == null)
return null;
for (IType baseTypeId : baseMethod.getExceptionSpecification()) {
inheritedTypeids.add(baseTypeId);
}
}
}
}
return inheritedTypeids.toArray(new IType[inheritedTypeids.size()]);
}
/**
* If {@code type} is a, possibly qualified, reference type referring to {@code classType},
* returns that reference type. Otherwise returns {@code null}.
*/
private static ICPPReferenceType getRefToClass(ICPPClassType classType, IType type) {
while (type instanceof ITypedef) {
type= ((ITypedef) type).getType();
}
if (type instanceof ICPPReferenceType) {
ICPPReferenceType refType = (ICPPReferenceType) type;
type= refType.getType();
while (type instanceof ITypedef) {
type= ((ITypedef) type).getType();
}
if (type instanceof IQualifierType) {
type= ((IQualifierType) type).getType();
if (classType.isSameType(type))
return refType;
}
}
return null;
}
public static ICPPMethod getMethodInClass(ICPPClassType ct, MethodKind kind) {
switch (kind) {
case DEFAULT_CTOR:
case COPY_CTOR:
case MOVE_CTOR:
for (ICPPConstructor ctor : ct.getConstructors()) {
if (!ctor.isImplicit() && getMethodKind(ct, ctor) == kind)
return ctor;
}
return null;
case COPY_ASSIGNMENT_OP:
case MOVE_ASSIGNMENT_OP:
for (ICPPMethod method : ct.getDeclaredMethods()) {
if (method instanceof ICPPConstructor)
continue;
if (getMethodKind(ct, method) == kind)
return method;
}
return null;
case DTOR:
for (ICPPMethod method : ct.getDeclaredMethods()) {
if (method.isDestructor())
return method;
}
return null;
case OTHER:
break;
}
return null;
}
/**
* Returns the visibility for a given {@code member} in the {@code host}.
* Throws an IllegalArgumentException if {@code member} is not a member of {@code host}
*
* @param classType The class to get the member's visibility specifier of.
* @return the visibility of the {@code member}.
*/
public static int getVisibility(ICPPInternalClassTypeMixinHost classType, IBinding member) {
if (classType.getDefinition() == null) {
classType.checkForDefinition();
if (classType.getDefinition() == null) {
ICPPClassType backup = getBackupDefinition(classType);
if (backup != null) {
return backup.getVisibility(member);
}
if (classType instanceof ICPPClassSpecialization) {
// A class instance doesn't have a definition. Delegate to the class template.
ICPPClassType specialized = ((ICPPClassSpecialization) classType).getSpecializedBinding();
if (!specialized.equals(member.getOwner())) {
if (!(member instanceof ICPPSpecialization))
throw invalidMember(specialized, member);
member = ((ICPPSpecialization) member).getSpecializedBinding();
}
return specialized.getVisibility(member);
}
return ICPPClassType.v_public; // Fallback visibility
}
}
// The concept of visibility does not apply to a lambda, which can end
// up having a class as its owner if they are used in the initializer
// of a field or a member function parameter.
if (member instanceof CPPClosureType) {
return ICPPClassType.v_public;
}
ICPPASTCompositeTypeSpecifier classDeclSpec = classType.getCompositeTypeSpecifier();
int visibility = getVisibility(classDeclSpec, member);
if (visibility >= 0)
return visibility;
ICPPMethod[] implicitMethods = getImplicitMethods(classType);
for (ICPPMethod implicitMethod : implicitMethods) {
if (member.equals(implicitMethod)) {
return ICPPClassType.v_public;
}
}
// It's possible that we haven't found the member because the class was illegally redefined
// and the member belongs to another definition. Try to search the definition containing
// the member.
if (member instanceof ICPPInternalBinding) {
IASTNode node = ((ICPPInternalBinding) member).getDefinition();
if (node != null) {
IASTName ownerName = CPPVisitor.findDeclarationOwnerDefinition(node, false);
if (ownerName != null && !ownerName.equals(classDeclSpec.getName()) &&
ownerName.getPropertyInParent() == ICPPASTCompositeTypeSpecifier.TYPE_NAME) {
classDeclSpec = (ICPPASTCompositeTypeSpecifier) ownerName.getParent();
visibility = getVisibility(classDeclSpec, member);
if (visibility >= 0)
return visibility;
}
}
}
throw invalidMember(classType, member);
}
private static int getVisibility(ICPPASTCompositeTypeSpecifier classDeclSpec, IBinding member) {
int visibility = classDeclSpec.getKey() == ICPPASTCompositeTypeSpecifier.k_class ?
ICPPClassType.v_private : ICPPClassType.v_public;
IASTDeclaration[] hostMembers = classDeclSpec.getMembers();
for (IASTDeclaration hostMember : hostMembers) {
if (hostMember instanceof ICPPASTVisibilityLabel) {
visibility = ((ICPPASTVisibilityLabel) hostMember).getVisibility();
continue;
}
while (hostMember instanceof ICPPASTTemplateDeclaration) {
hostMember = ((ICPPASTTemplateDeclaration) hostMember).getDeclaration();
}
if (hostMember instanceof IASTSimpleDeclaration) {
IASTSimpleDeclaration memberDeclaration = (IASTSimpleDeclaration) hostMember;
for (IASTDeclarator memberDeclarator : memberDeclaration.getDeclarators()) {
IBinding memberBinding =
ASTQueries.findInnermostDeclarator(memberDeclarator).getName().resolveBinding();
if (member.equals(memberBinding)) {
return visibility;
}
}
IASTDeclSpecifier declSpec = memberDeclaration.getDeclSpecifier();
if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
IBinding memberBinding =
((ICPPASTCompositeTypeSpecifier) declSpec).getName().resolveBinding();
if (member.equals(memberBinding)) {
return visibility;
}
} else if (declSpec instanceof ICPPASTElaboratedTypeSpecifier
&& memberDeclaration.getDeclarators().length == 0) {
IBinding memberBinding =
((ICPPASTElaboratedTypeSpecifier) declSpec).getName().resolveBinding();
if (member.equals(memberBinding)) {
return visibility;
}
} else if (declSpec instanceof ICPPASTEnumerationSpecifier) {
IBinding enumerationBinding = ((ICPPASTEnumerationSpecifier) declSpec).getName().resolveBinding();
if (member.equals(enumerationBinding)) {
return visibility;
}
}
} else if (hostMember instanceof IASTFunctionDefinition) {
IASTDeclarator declarator = ((IASTFunctionDefinition) hostMember).getDeclarator();
declarator = ASTQueries.findInnermostDeclarator(declarator);
IBinding functionBinding = declarator.getName().resolveBinding();
if (member.equals(functionBinding)) {
return visibility;
}
} else if (hostMember instanceof ICPPASTAliasDeclaration) {
IBinding aliasBinding = ((ICPPASTAliasDeclaration) hostMember).getAlias().resolveBinding();
if (member.equals(aliasBinding)) {
return visibility;
}
} else if (hostMember instanceof ICPPASTUsingDeclaration) {
IBinding usingBinding = ((ICPPASTUsingDeclaration) hostMember).getName().resolveBinding();
if (member.equals(usingBinding)) {
return visibility;
}
} else if (hostMember instanceof ICPPASTNamespaceDefinition) { // Not valid but possible due to the parser
IBinding namespaceBinding = ((ICPPASTNamespaceDefinition) hostMember).getName().resolveBinding();
if (member.equals(namespaceBinding)) {
return visibility;
}
}
}
return -1;
}
private static IllegalArgumentException invalidMember(IBinding classType, IBinding member) {
String name = member.getName();
if (name.isEmpty())
name = "<anonymous>"; //$NON-NLS-1$
return new IllegalArgumentException(name + " is not a member of " + classType.getName()); //$NON-NLS-1$
}
}