blob: 50741b31868e49adadde92b2daafdb1ed45737b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2016 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:
* Andrew Niefer (IBM Corporation) - initial API and implementation
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
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.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
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.ICPPParameter;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.AttributeUtil;
import org.eclipse.cdt.internal.core.dom.Linkage;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.ProblemFunctionType;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
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.PlatformObject;
/**
* Binding for C++ function
*/
public class CPPFunction extends PlatformObject implements ICPPFunction, ICPPInternalFunction {
public static final ICPPFunction UNINITIALIZED_FUNCTION = new CPPFunction(null) {
@Override
public String toString() {
return "UNINITIALIZED_FUNCTION"; //$NON-NLS-1$
}
};
protected IASTDeclarator[] declarations;
protected ICPPASTFunctionDeclarator definition;
protected ICPPFunctionType declaredType;
protected ICPPFunctionType type;
private static final int FULLY_RESOLVED = 1;
private static final int RESOLUTION_IN_PROGRESS = 1 << 1;
private int bits;
public CPPFunction(IASTDeclarator declarator) {
if (declarator != null) {
IASTNode parent = ASTQueries.findOutermostDeclarator(declarator).getParent();
if (parent instanceof IASTFunctionDefinition) {
if (declarator instanceof ICPPASTFunctionDeclarator) {
definition = (ICPPASTFunctionDeclarator) declarator;
}
} else {
declarations = new IASTDeclarator[] { declarator };
}
IASTName name= getASTName();
name.setBinding(this);
}
}
private void resolveAllDeclarations() {
if ((bits & (FULLY_RESOLVED | RESOLUTION_IN_PROGRESS)) == 0) {
bits |= RESOLUTION_IN_PROGRESS;
IASTTranslationUnit tu = null;
if (definition != null) {
tu = definition.getTranslationUnit();
} else if (declarations != null) {
tu = declarations[0].getTranslationUnit();
} else {
// Implicit binding
IScope scope = getScope();
IASTNode node = ASTInternal.getPhysicalNodeOfScope(scope);
if (node != null) {
tu = node.getTranslationUnit();
}
}
if (tu != null) {
CPPVisitor.getDeclarations(tu, this);
}
declarations = ArrayUtil.trim(IASTDeclarator.class, declarations);
bits |= FULLY_RESOLVED;
bits &= ~RESOLUTION_IN_PROGRESS;
}
}
@Override
public IASTDeclarator[] getDeclarations() {
return declarations;
}
@Override
public ICPPASTFunctionDeclarator getDefinition() {
return definition;
}
@Override
public final void addDefinition(IASTNode node) {
IASTDeclarator dtor = extractRelevantDtor(node);
if (dtor instanceof ICPPASTFunctionDeclarator) {
ICPPASTFunctionDeclarator fdtor= (ICPPASTFunctionDeclarator) dtor;
updateFunctionParameterBindings(fdtor);
definition = fdtor;
}
}
@Override
public final void addDeclaration(IASTNode node) {
IASTDeclarator dtor = extractRelevantDtor(node);
if (dtor == null) {
return;
}
// function could be declared via a typedef
if (dtor instanceof ICPPASTFunctionDeclarator) {
updateFunctionParameterBindings((ICPPASTFunctionDeclarator) dtor);
}
if (declarations == null || declarations.length == 0) {
declarations = new IASTDeclarator[] { dtor };
} else {
// Keep the lowest offset declaration in [0]
if (((ASTNode) node).getOffset() < ((ASTNode) declarations[0]).getOffset()) {
declarations = ArrayUtil.prepend(declarations, dtor);
} else {
declarations = ArrayUtil.append(declarations, dtor);
}
}
}
private IASTDeclarator extractRelevantDtor(IASTNode node) {
while (node instanceof IASTName)
node = node.getParent();
if (!(node instanceof IASTDeclarator))
return null;
return ASTQueries.findTypeRelevantDeclarator((IASTDeclarator) node);
}
@Override
public ICPPParameter[] getParameters() {
ICPPASTFunctionDeclarator declarator = getDefinition();
IASTDeclarator[] dtors = getDeclarations();
if (dtors != null) {
// In case of multiple function declarations we select the one with the most
// default parameter values.
int defaultValuePosition = -1;
for (IASTDeclarator dtor : dtors) {
if (dtor instanceof ICPPASTFunctionDeclarator) {
if (declarator == null) {
declarator = (ICPPASTFunctionDeclarator) dtor;
} else {
ICPPASTFunctionDeclarator contender = (ICPPASTFunctionDeclarator) dtor;
if (defaultValuePosition < 0)
defaultValuePosition = findFirstDefaultValue(declarator.getParameters());
int pos = findFirstDefaultValue(contender.getParameters());
if (pos < defaultValuePosition) {
declarator = contender;
defaultValuePosition = pos;
}
}
}
}
}
if (declarator == null) {
return CPPBuiltinParameter.createParameterList(getType());
}
IASTParameterDeclaration[] params = declarator.getParameters();
int size = params.length;
ICPPParameter[] result = new ICPPParameter[size];
if (size > 0) {
for (int i = 0; i < size; i++) {
IASTParameterDeclaration param = params[i];
final IASTName name = getParamName(param);
final IBinding binding= name.resolveBinding();
if (binding instanceof ICPPParameter) {
result[i]= (ICPPParameter) binding;
} else {
result[i] = new CPPParameter.CPPParameterProblem(
param, IProblemBinding.SEMANTIC_INVALID_TYPE, name.toCharArray());
}
}
if (result.length == 1 && SemanticUtil.isVoidType(result[0].getType()))
return ICPPParameter.EMPTY_CPPPARAMETER_ARRAY; // f(void) is the same as f()
}
return result;
}
/**
* Returns the position of the first parameter that has a default value.
* If none of the parameters has a default value, return the number of parameters.
*/
static int findFirstDefaultValue(ICPPASTParameterDeclaration[] parameters) {
for (int i = parameters.length; --i >= 0;) {
if (parameters[i].getDeclarator().getInitializer() == null)
return i + 1;
}
return 0;
}
@Override
public IScope getFunctionScope() {
resolveAllDeclarations();
if (definition != null) {
return definition.getFunctionScope();
}
for (IASTDeclarator dtor : declarations) {
if (dtor instanceof ICPPASTFunctionDeclarator) {
return ((ICPPASTFunctionDeclarator) dtor).getFunctionScope();
}
}
// function declaration via typedef
return null;
}
@Override
public String getName() {
return getASTName().toString();
}
@Override
public char[] getNameCharArray() {
return getASTName().getSimpleID();
}
protected IASTName getASTName() {
IASTDeclarator dtor = (definition != null) ? definition : declarations[0];
dtor= ASTQueries.findInnermostDeclarator(dtor);
return dtor.getName().getLastName();
}
@Override
public IScope getScope() {
IASTName n = getASTName();
IScope scope = CPPVisitor.getContainingScope(n);
if (scope instanceof ICPPClassScope) {
ICPPASTDeclSpecifier declSpec = getDeclSpecifier();
if (declSpec != null && declSpec.isFriend()) {
try {
while (scope instanceof ICPPClassScope) {
scope = scope.getParent();
}
} catch (DOMException e) {
}
}
}
return scope;
}
private ICPPASTDeclSpecifier getDeclSpecifier() {
if (definition != null) {
IASTNode node = ASTQueries.findOutermostDeclarator(definition).getParent();
IASTFunctionDefinition def = (IASTFunctionDefinition) node;
return (ICPPASTDeclSpecifier) def.getDeclSpecifier();
} else if (declarations != null && declarations.length != 0) {
IASTNode node = ASTQueries.findOutermostDeclarator(declarations[0]).getParent();
IASTSimpleDeclaration decl = (IASTSimpleDeclaration) node;
return (ICPPASTDeclSpecifier) decl.getDeclSpecifier();
}
return null;
}
// Helper function for getDeclaredType() and getType().
public static ICPPFunctionType toFunctionType(IType type) {
if (type instanceof ICPPFunctionType) {
return (ICPPFunctionType) type;
} else {
type = getNestedType(type, TDEF);
if (type instanceof ICPPFunctionType) {
return (ICPPFunctionType) type;
} else if (type instanceof ISemanticProblem) {
return new ProblemFunctionType(((ISemanticProblem) type).getID());
} else {
// This case is unexpected
return new ProblemFunctionType(ISemanticProblem.TYPE_UNRESOLVED_NAME);
}
}
}
@Override
public ICPPFunctionType getDeclaredType() {
if (declaredType == null) {
IType t = CPPVisitor.createType((definition != null) ? definition : declarations[0],
CPPVisitor.DO_NOT_RESOLVE_PLACEHOLDERS);
declaredType = toFunctionType(t);
}
return declaredType;
}
@Override
public ICPPFunctionType getType() {
if (type == null) {
// TODO: As an optimization, check if declaredType contains placeholders,
// and if it doesn't, just return that.
IType t = CPPVisitor.createType((definition != null) ? definition : declarations[0]);
// The declaration may not specify the return type, so look at the definition.
if (t == ProblemType.NO_NAME) {
findDefinition();
if (definition != null) {
t = CPPVisitor.createType(definition);
}
}
type = toFunctionType(t);
}
return type;
}
@Override
public IBinding resolveParameter(CPPParameter param) {
int pos= param.getParameterPosition();
int tdeclLen= declarations == null ? 0 : declarations.length;
for (int i= -1; i < tdeclLen; i++) {
ICPPASTFunctionDeclarator tdecl;
if (i == -1) {
tdecl= definition;
if (tdecl == null)
continue;
} else {
final IASTDeclarator dtor= declarations[i];
if (!(dtor instanceof ICPPASTFunctionDeclarator)) {
if (dtor == null) {
break;
}
continue;
}
tdecl= (ICPPASTFunctionDeclarator) dtor;
}
IASTParameterDeclaration[] params = tdecl.getParameters();
if (pos < params.length) {
final IASTName oName = getParamName(params[pos]);
return oName.resolvePreBinding();
}
}
return param;
}
private IASTName getParamName(final IASTParameterDeclaration paramDecl) {
return ASTQueries.findInnermostDeclarator(paramDecl.getDeclarator()).getName();
}
protected final void updateFunctionParameterBindings(ICPPASTFunctionDeclarator fdtor) {
IASTParameterDeclaration[] updateParams = fdtor.getParameters();
int k= 0;
int tdeclLen= declarations == null ? 0 : declarations.length;
for (int i= -1; i < tdeclLen && k < updateParams.length; i++) {
ICPPASTFunctionDeclarator tdecl;
if (i == -1) {
tdecl= definition;
if (tdecl == null)
continue;
} else {
final IASTDeclarator dtor= declarations[i];
if (!(dtor instanceof ICPPASTFunctionDeclarator)) {
if (dtor == null) {
break;
}
continue;
}
tdecl= (ICPPASTFunctionDeclarator) dtor;
}
IASTParameterDeclaration[] params = tdecl.getParameters();
int end= Math.min(params.length, updateParams.length);
for (; k < end; k++) {
final IASTName oName = getParamName(params[k]);
IBinding b= oName.resolvePreBinding();
IASTName n = getParamName(updateParams[k]);
n.setBinding(b);
ASTInternal.addDeclaration(b, n);
}
}
}
@Override
public final boolean isStatic() {
return isStatic(true);
}
@Override
public boolean isStatic(boolean resolveAll) {
if (resolveAll && (bits & FULLY_RESOLVED) == 0) {
resolveAllDeclarations();
}
return hasStorageClass(this, IASTDeclSpecifier.sc_static);
}
// static public boolean isStatic
// //2 state bits, most significant = whether or not we've figure this out yet
// //least significant = whether or not we are static
// int state = (bits & IS_STATIC) >> 2;
// if (state > 1) return (state % 2 != 0);
//
// IASTDeclSpecifier declSpec = null;
// IASTFunctionDeclarator dtor = (IASTFunctionDeclarator) getDefinition();
// if (dtor != null) {
// declSpec = ((IASTFunctionDefinition) dtor.getParent()).getDeclSpecifier();
// if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_static) {
// bits |= 3 << 2;
// return true;
// }
// }
//
// IASTFunctionDeclarator[] dtors = (IASTFunctionDeclarator[]) getDeclarations();
// if (dtors != null) {
// for (int i = 0; i < dtors.length; i++) {
// IASTNode parent = dtors[i].getParent();
// declSpec = ((IASTSimpleDeclaration) parent).getDeclSpecifier();
// if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_static) {
// bits |= 3 << 2;
// return true;
// }
// }
// }
// bits |= 2 << 2;
// return false;
// }
@Override
public String[] getQualifiedName() {
return CPPVisitor.getQualifiedName(this);
}
@Override
public char[][] getQualifiedNameCharArray() {
return CPPVisitor.getQualifiedNameCharArray(this);
}
@Override
public boolean isGloballyQualified() throws DOMException {
IScope scope = getScope();
while (scope != null) {
if (scope instanceof ICPPBlockScope)
return false;
scope = scope.getParent();
}
return true;
}
static public boolean hasStorageClass(ICPPInternalFunction function, int storage) {
IASTDeclarator dtor = (IASTDeclarator) function.getDefinition();
IASTNode[] ds = function.getDeclarations();
int i = -1;
do {
if (dtor != null) {
IASTNode parent = dtor.getParent();
while (!(parent instanceof IASTDeclaration))
parent = parent.getParent();
IASTDeclSpecifier declSpec = null;
if (parent instanceof IASTSimpleDeclaration) {
declSpec = ((IASTSimpleDeclaration) parent).getDeclSpecifier();
} else if (parent instanceof IASTFunctionDefinition) {
declSpec = ((IASTFunctionDefinition) parent).getDeclSpecifier();
}
if (declSpec != null && declSpec.getStorageClass() == storage) {
return true;
}
}
if (ds != null && ++i < ds.length) {
dtor = (IASTDeclarator) ds[i];
} else {
break;
}
} while (dtor != null);
return false;
}
public static ICPPASTFunctionDefinition getFunctionDefinition(IASTNode def) {
while (def != null && !(def instanceof IASTDeclaration)) {
def= def.getParent();
}
if (def instanceof ICPPASTFunctionDefinition) {
return (ICPPASTFunctionDefinition) def;
}
return null;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isInline() {
IASTDeclarator dtor = getDefinition();
IASTDeclarator[] ds = getDeclarations();
int i = -1;
do {
if (dtor != null) {
IASTNode parent = dtor.getParent();
while (!(parent instanceof IASTDeclaration)) {
parent = parent.getParent();
}
IASTDeclSpecifier declSpec = null;
if (parent instanceof IASTSimpleDeclaration) {
declSpec = ((IASTSimpleDeclaration) parent).getDeclSpecifier();
} else if (parent instanceof IASTFunctionDefinition) {
declSpec = ((IASTFunctionDefinition) parent).getDeclSpecifier();
}
if (declSpec != null && declSpec.isInline())
return true;
}
if (ds != null && ++i < ds.length) {
dtor = ds[i];
} else {
break;
}
} while (dtor != null);
return false;
}
@Override
public boolean isExternC() {
if (CPPVisitor.isExternC(getDefinition())) {
return true;
}
IASTNode[] ds= getDeclarations();
if (ds != null) {
for (IASTNode element : ds) {
if (CPPVisitor.isExternC(element)) {
return true;
}
}
}
return false;
}
@Override
public boolean isExtern() {
return hasStorageClass(this, IASTDeclSpecifier.sc_extern);
}
@Override
public boolean isAuto() {
return hasStorageClass(this, IASTDeclSpecifier.sc_auto);
}
@Override
public boolean isConstexpr() {
ICPPASTDeclSpecifier declSpec = getDeclSpecifier();
if (declSpec == null)
return false;
return declSpec.isConstexpr();
}
@Override
public boolean isDeleted() {
return isDeletedDefinition(getDefinition());
}
static boolean isDeletedDefinition(IASTNode def) {
ICPPASTFunctionDefinition functionDefinition = getFunctionDefinition(def);
if (functionDefinition != null)
return functionDefinition.isDeleted();
return false;
}
@Override
public boolean isRegister() {
return hasStorageClass(this, IASTDeclSpecifier.sc_register);
}
@Override
public boolean takesVarArgs() {
ICPPASTFunctionDeclarator dtor= getPreferredDtor();
return dtor != null ? dtor.takesVarArgs() : false;
}
@Override
public ILinkage getLinkage() {
return Linkage.CPP_LINKAGE;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(getName());
IFunctionType t = getType();
result.append(t != null ? ASTTypeUtil.getParameterTypeStringAndQualifiers(t) : "()"); //$NON-NLS-1$
return result.toString();
}
@Override
public IBinding getOwner() {
return CPPVisitor.findNameOwner(getASTName(), false);
}
@Override
public IType[] getExceptionSpecification() {
ICPPASTFunctionDeclarator declarator = getPreferredDtor();
if (declarator != null) {
IASTTypeId[] astTypeIds= declarator.getExceptionSpecification();
if (astTypeIds.equals(ICPPASTFunctionDeclarator.NO_EXCEPTION_SPECIFICATION))
return null;
if (astTypeIds.equals(IASTTypeId.EMPTY_TYPEID_ARRAY))
return IType.EMPTY_TYPE_ARRAY;
IType[] typeIds = new IType[astTypeIds.length];
for (int i= 0; i < astTypeIds.length; ++i) {
typeIds[i] = CPPVisitor.createType(astTypeIds[i]);
}
return typeIds;
}
return null;
}
protected ICPPASTFunctionDeclarator getPreferredDtor() {
ICPPASTFunctionDeclarator dtor = getDefinition();
if (dtor != null)
return dtor;
IASTDeclarator[] dtors = getDeclarations();
if (dtors != null) {
for (IASTDeclarator declarator : dtors) {
if (declarator instanceof ICPPASTFunctionDeclarator)
return (ICPPASTFunctionDeclarator) declarator;
}
}
return dtor;
}
@Override
public int getRequiredArgumentCount() {
return getRequiredArgumentCount(getParameters());
}
public static int getRequiredArgumentCount(ICPPParameter[] pars) {
int result = pars.length;
for (int i = pars.length; --i >= 0;) {
final ICPPParameter p = pars[i];
if (p.hasDefaultValue() || p.isParameterPack()) {
result--;
}
}
return result;
}
@Override
public boolean hasParameterPack() {
return hasParameterPack(getParameters());
}
public static boolean hasParameterPack(ICPPParameter[] pars) {
return pars.length > 0 && pars[pars.length - 1].isParameterPack();
}
@Override
public boolean isNoReturn() {
ICPPASTFunctionDeclarator dtor = getPreferredDtor();
if (dtor == null) {
return false;
}
if (AttributeUtil.hasNoreturnAttribute(dtor)) {
return true;
}
IASTNode parent = dtor.getParent();
if (parent instanceof IASTAttributeOwner) {
return AttributeUtil.hasNoreturnAttribute((IASTAttributeOwner) parent);
}
return false;
}
public static ICPPExecution getFunctionBodyExecution(ICPPFunction function) {
if (function instanceof ICPPComputableFunction) {
return ((ICPPComputableFunction) function).getFunctionBodyExecution();
}
return null;
}
@Override
public ICPPExecution getFunctionBodyExecution() {
if (!isConstexpr())
return null;
if (getDefinition() == null) {
// Trigger a search for the function definition.
if (declarations != null && declarations[0] != null) {
IASTTranslationUnit tu = declarations[0].getTranslationUnit();
if (tu != null) {
tu.getDefinitionsInAST(this);
}
}
}
return computeFunctionBodyExecution(getDefinition());
}
public static ICPPExecution computeFunctionBodyExecution(IASTNode def) {
ICPPASTFunctionDefinition fnDef = getFunctionDefinition(def);
if (fnDef != null) {
// Make sure ambiguity resolution has been performed on the function body, even
// if it's a class method and we're still processing the class declaration.
((ASTNode) fnDef).resolvePendingAmbiguities();
if (fnDef.getBody() instanceof CPPASTCompoundStatement) {
CPPASTCompoundStatement body = (CPPASTCompoundStatement) fnDef.getBody();
return body.getExecution();
}
}
return null;
}
private void findDefinition() {
if (definition != null)
return;
IASTName[] definitions = declarations[0].getTranslationUnit().getDefinitionsInAST(this);
if (definitions.length != 0)
addDefinition(definitions[0]);
}
}