/*******************************************************************************
* Copyright (c) 2004, 2011 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)
* Bryan Wilkinson (QNX)
* Andrew Ferguson (Symbian)
* Sergey Prigogin (Google)
* Jens Elmenthaler - http://bugs.eclipse.org/173458 (camel case completion)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
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.IASTFieldReference;
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.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IBinding;
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.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
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.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.core.parser.util.ObjectSet;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
/**
* Base implementation for c++ scopes.
*/
public class CPPClassScope extends CPPScope implements ICPPClassScope {
private ICPPMethod[] implicits = null;
public CPPClassScope(ICPPASTCompositeTypeSpecifier physicalNode) {
super(physicalNode);
}
public EScopeKind getKind() {
return EScopeKind.eClassType;
}
/**
* Add in default constructor, copy constructor, copy assignment operator and destructor,
* if appropriate.
* Method will be called after ambiguity resolution.
*/
public void createImplicitMembers() {
//create bindings for the implicit members, if the user declared them then those declarations
//will resolve to these bindings.
ICPPASTCompositeTypeSpecifier compTypeSpec = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName name = compTypeSpec.getName().getLastName();
IBinding binding = name.resolveBinding();
if (!(binding instanceof ICPPClassType))
return;
ICPPClassType clsType = (ICPPClassType) binding;
if (clsType instanceof ICPPClassTemplate) {
try {
clsType= CPPTemplates.instantiateWithinClassTemplate((ICPPClassTemplate) clsType);
} catch (DOMException e) {
}
}
char[] className = name.getLookupKey();
ICPPParameter[] voidPs = new ICPPParameter[] { new CPPParameter(CPPSemantics.VOID_TYPE, 0) };
IType pType = new CPPReferenceType(SemanticUtil.constQualify(clsType), false);
ICPPParameter[] ps = new ICPPParameter[] { new CPPParameter(pType, 0) };
int i= 0;
ImplicitsAnalysis ia= new ImplicitsAnalysis(compTypeSpec);
implicits= new ICPPMethod[ia.getImplicitsToDeclareCount()];
if (!ia.hasUserDeclaredConstructor()) {
//default constructor: A(void)
ICPPMethod m = new CPPImplicitConstructor(this, className, voidPs);
implicits[i++] = m;
addBinding(m);
}
if (!ia.hasUserDeclaredCopyConstructor()) {
//copy constructor: A(const A &)
ICPPMethod m = new CPPImplicitConstructor(this, className, ps);
implicits[i++]=m;
addBinding(m);
}
if (!ia.hasUserDeclaredCopyAssignmentOperator()) {
//copy assignment operator: A& operator = (const A &)
IType refType = new CPPReferenceType(clsType, false);
ICPPFunctionType ft= CPPVisitor.createImplicitFunctionType(refType, ps, false, false);
ICPPMethod m = new CPPImplicitMethod(this, OverloadableOperator.ASSIGN.toCharArray(), ft, ps);
implicits[i++] = m;
addBinding(m);
}
if (!ia.hasUserDeclaredDestructor()) {
//destructor: ~A()
ICPPFunctionType ft= CPPVisitor.createImplicitFunctionType(new CPPBasicType(Kind.eUnspecified, 0), voidPs, false, false);
char[] dtorName = CharArrayUtils.concat("~".toCharArray(), className); //$NON-NLS-1$
ICPPMethod m = new CPPImplicitMethod(this, dtorName, ft, voidPs);
implicits[i++] = m;
addBinding(m);
}
}
@Override
public IScope getParent() {
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
return CPPVisitor.getContainingNonTemplateScope(compName);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.cpp.ICPPScope#addBinding(org.eclipse.cdt.core.dom.ast.IBinding)
*/
@Override
public void addBinding(IBinding binding) {
if (binding instanceof ICPPConstructor) {
addConstructor(binding);
return;
}
super.addBinding(binding);
}
@Override
public void addName(IASTName name) {
// don't add names from inactive code branches
if (!name.isActive())
return;
if (name instanceof ICPPASTQualifiedName) {
// check whether the qualification matches
IBinding b= getClassType();
final ICPPASTQualifiedName qname = (ICPPASTQualifiedName) name;
final IASTName[] names= qname.getNames();
for (int i = names.length-2; i>=0; i--) {
if (b == null || !CharArrayUtils.equals(names[i].getLookupKey(), b.getNameCharArray()))
return;
b= b.getOwner();
}
if (qname.isFullyQualified() && b != null)
return;
}
IASTNode parent = name.getParent();
if (parent instanceof IASTDeclarator) {
if (CPPVisitor.isConstructor(this, (IASTDeclarator) parent)) {
addConstructor(name);
return;
}
}
super.addName(name);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addConstructor(Object constructor) {
if (bindings == null)
bindings = new CharArrayObjectMap(1);
Object o = bindings.get(CONSTRUCTOR_KEY);
if (o != null) {
if (o instanceof ObjectSet) {
((ObjectSet)o).put(constructor);
} else {
ObjectSet set = new ObjectSet(2);
set.put(o);
set.put(constructor);
bindings.put(CONSTRUCTOR_KEY, set);
}
} else {
bindings.put(CONSTRUCTOR_KEY, constructor);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.cpp.ICPPScope#getBinding(int, char[])
*/
@Override
public IBinding getBinding(IASTName name, boolean resolve, IIndexFileSet fileSet) {
char[] c = name.getLookupKey();
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
if (compName instanceof ICPPASTTemplateId) {
compName= ((ICPPASTTemplateId) compName).getTemplateName();
}
if (CharArrayUtils.equals(c, compName.getLookupKey())) {
//9.2 ... The class-name is also inserted into the scope of the class itself
return compName.resolveBinding();
}
return super.getBinding(name, resolve, fileSet);
}
@Override
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet fileSet,
boolean checkPointOfDecl) {
char[] c = name.getLookupKey();
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
if (compName instanceof ICPPASTTemplateId) {
compName= ((ICPPASTTemplateId) compName).getTemplateName();
}
IBinding[] result = null;
if ((!prefixLookup && CharArrayUtils.equals(c, compName.getLookupKey()))
|| (prefixLookup && ContentAssistMatcherFactory.getInstance().match(c, compName.getLookupKey()))) {
if (shallReturnConstructors(name, prefixLookup)) {
result = (IBinding[]) ArrayUtil.addAll(IBinding.class, result, getConstructors(name, resolve));
}
//9.2 ... The class-name is also inserted into the scope of the class itself
result = (IBinding[]) ArrayUtil.append(IBinding.class, result, compName.resolveBinding());
if (!prefixLookup)
return (IBinding[]) ArrayUtil.trim(IBinding.class, result);
}
result = (IBinding[]) ArrayUtil.addAll(IBinding.class, result,
super.getBindings(name, resolve, prefixLookup, fileSet, checkPointOfDecl));
return (IBinding[]) ArrayUtil.trim(IBinding.class, result);
}
static protected boolean shouldResolve(boolean force, IASTName candidate, IASTName forName) {
if (!force || candidate == forName)
return false;
if (forName == null)
return true;
if (!forName.isReference() && !CPPSemantics.declaredBefore(candidate, forName, false))
return false;
return true;
}
public ICPPConstructor[] getConstructors() {
return getConstructors(null, true);
}
private ICPPConstructor[] getConstructors(IASTName forName, boolean forceResolve) {
populateCache();
final CharArrayObjectMap nameMap = bindings;
if (nameMap == null)
return ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
Object o = nameMap.get(CONSTRUCTOR_KEY);
if (o != null) {
IBinding binding = null;
if (o instanceof ObjectSet<?>) {
ObjectSet<?> set = (ObjectSet<?>) o;
IBinding[] bs = null;
for (int i = 0; i < set.size(); i++) {
Object obj = set.keyAt(i);
if (obj instanceof IASTName) {
IASTName n = (IASTName) obj;
binding = shouldResolve(forceResolve, n, forName) ? n.resolveBinding() : n.getBinding();
if (binding instanceof ICPPConstructor) {
bs = (IBinding[]) ArrayUtil.append(ICPPConstructor.class, bs, binding);
}
} else if (obj instanceof ICPPConstructor) {
bs = (IBinding[]) ArrayUtil.append(ICPPConstructor.class, bs, obj);
}
}
return (ICPPConstructor[]) ArrayUtil.trim(ICPPConstructor.class, bs);
} else if (o instanceof IASTName) {
if (shouldResolve(forceResolve, (IASTName) o, forName) || ((IASTName)o).getBinding() != null) {
// always store the name, rather than the binding, such that we can properly flush the scope.
nameMap.put(CONSTRUCTOR_KEY, o);
binding = ((IASTName)o).resolveBinding();
}
} else if (o instanceof IBinding) {
binding = (IBinding) o;
}
if (binding != null && binding instanceof ICPPConstructor) {
return new ICPPConstructor[] { (ICPPConstructor) binding };
}
}
return ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.IScope#find(java.lang.String)
*/
@Override
public IBinding[] find(String name) {
char[] n = name.toCharArray();
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
if (compName instanceof ICPPASTTemplateId) {
compName= ((ICPPASTTemplateId) compName).getTemplateName();
}
if (CharArrayUtils.equals(compName.getLookupKey(), n)) {
return new IBinding[] {compName.resolveBinding()};
}
return super.find(name);
}
public static boolean shallReturnConstructors(IASTName name, boolean isPrefixLookup) {
if (!isPrefixLookup)
return CPPVisitor.isConstructorDeclaration(name);
if (name.getPropertyInParent() == CPPSemantics.STRING_LOOKUP_PROPERTY)
return false;
IASTNode node = name.getParent();
if (node instanceof ICPPASTTemplateId)
return false;
if (node instanceof ICPPASTQualifiedName) {
if (((ICPPASTQualifiedName) node).getLastName() == name)
node = node.getParent();
else
return false;
}
if (node instanceof IASTDeclSpecifier) {
IASTNode parent = node.getParent();
if (parent instanceof IASTTypeId && parent.getParent() instanceof ICPPASTNewExpression)
return true;
return false;
} else if (node instanceof IASTFieldReference) {
return false;
}
return true;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope#getClassType()
*/
public ICPPClassType getClassType() {
ICPPASTCompositeTypeSpecifier compSpec = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
final IASTName name = compSpec.getName();
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPClassType)
return (ICPPClassType) binding;
return new CPPClassType.CPPClassTypeProblem(name, ISemanticProblem.BINDING_NO_CLASS, name.toCharArray());
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope#getImplicitMethods()
*/
public ICPPMethod[] getImplicitMethods() {
if (implicits == null)
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
return implicits;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.cpp.ICPPScope#getScopeName()
*/
@Override
public IName getScopeName() {
IASTNode node = getPhysicalNode();
if (node instanceof ICPPASTCompositeTypeSpecifier) {
return ((ICPPASTCompositeTypeSpecifier)node).getName();
}
return null;
}
}
/**
* Helps analysis of the class declaration for user declared members relevant
* to deciding which implicit bindings to declare.
*
* @see chapter 12 of the ISO specification
*/
class ImplicitsAnalysis {
private boolean hasUserDeclaredConstructor;
private boolean hasUserDeclaredCopyConstructor;
private boolean hasUserDeclaredCopyAssignmentOperator;
private boolean hasUserDeclaredDestructor;
ImplicitsAnalysis(ICPPASTCompositeTypeSpecifier compSpec) {
ICPPASTFunctionDeclarator[] ctors= getUserDeclaredCtorOrDtor(compSpec, true);
hasUserDeclaredConstructor= ctors.length> 0;
hasUserDeclaredCopyConstructor= false;
hasUserDeclaredCopyAssignmentOperator= false;
hasUserDeclaredDestructor= getUserDeclaredCtorOrDtor(compSpec, false).length>0;
outer: for (int i=0; i<ctors.length; i++) {
ICPPASTFunctionDeclarator dcltor= ctors[i];
IASTParameterDeclaration[] ps = dcltor.getParameters();
if (ps.length >= 1) {
if (paramHasTypeReferenceToTheAssociatedClassType(ps[0], compSpec.getName().getRawSignature())) {
// and all remaining arguments have initializers
for (int j = 1; j < ps.length; j++) {
if (ps[j].getDeclarator().getInitializer() == null) {
continue outer;
}
}
hasUserDeclaredCopyConstructor= true;
}
}
}
boolean hasUserDeclaredCAO= getUserDeclaredCopyAssignmentOperators(compSpec).length > 0;
hasUserDeclaredCopyAssignmentOperator= hasUserDeclaredCAO;
}
public int getImplicitsToDeclareCount() {
return (!hasUserDeclaredDestructor ? 1 : 0)
+ (!hasUserDeclaredConstructor ? 1 : 0)
+ (!hasUserDeclaredCopyConstructor ? 1 : 0)
+ (!hasUserDeclaredCopyAssignmentOperator ? 1 : 0);
}
private static ICPPASTFunctionDeclarator[] getUserDeclaredCtorOrDtor(ICPPASTCompositeTypeSpecifier compSpec, boolean constructor) {
List<ICPPASTFunctionDeclarator> result= new ArrayList<ICPPASTFunctionDeclarator>();
IASTDeclaration[] members = compSpec.getMembers();
char[] name = compSpec.getName().getLookupKey();
IASTDeclarator dcltor = null;
IASTDeclSpecifier spec = null;
for (IASTDeclaration member : members) {
if (member instanceof IASTSimpleDeclaration) {
IASTDeclarator[] dtors = ((IASTSimpleDeclaration)member).getDeclarators();
if (dtors.length == 0 || dtors.length > 1)
continue;
dcltor = dtors[0];
spec = ((IASTSimpleDeclaration)member).getDeclSpecifier();
} else if (member instanceof IASTFunctionDefinition) {
dcltor = ((IASTFunctionDefinition)member).getDeclarator();
spec = ((IASTFunctionDefinition)member).getDeclSpecifier();
}
if (!(dcltor instanceof ICPPASTFunctionDeclarator) || !(spec instanceof IASTSimpleDeclSpecifier) ||
((IASTSimpleDeclSpecifier)spec).getType() != IASTSimpleDeclSpecifier.t_unspecified) {
continue;
}
boolean nameEquals= false;
char[] dtorname= ASTQueries.findInnermostDeclarator(dcltor).getName().getLookupKey();
if (constructor) {
nameEquals= CharArrayUtils.equals(dtorname, name);
} else {
if (dtorname.length > 0 && dtorname[0] == '~') {
nameEquals= CharArrayUtils.equals(dtorname, 1, name.length, name);
}
}
if (!nameEquals)
continue;
result.add((ICPPASTFunctionDeclarator) dcltor);
}
return result.toArray(new ICPPASTFunctionDeclarator[result.size()]);
}
private static ICPPASTFunctionDeclarator[] getUserDeclaredCopyAssignmentOperators(ICPPASTCompositeTypeSpecifier compSpec) {
List<ICPPASTFunctionDeclarator> result= new ArrayList<ICPPASTFunctionDeclarator>();
IASTDeclaration[] members = compSpec.getMembers();
IASTDeclarator dcltor = null;
for (IASTDeclaration member : members) {
if (member instanceof IASTSimpleDeclaration) {
IASTDeclarator[] dtors = ((IASTSimpleDeclaration)member).getDeclarators();
if (dtors.length == 0 || dtors.length > 1)
continue;
dcltor = dtors[0];
} else if (member instanceof IASTFunctionDefinition) {
dcltor = ((IASTFunctionDefinition)member).getDeclarator();
}
if (dcltor instanceof ICPPASTFunctionDeclarator == false)
continue;
final char[] nchars= ASTQueries.findInnermostDeclarator(dcltor).getName().getLookupKey();
if (!CharArrayUtils.equals(nchars, OverloadableOperator.ASSIGN.toCharArray()))
continue;
IASTParameterDeclaration[] ps = ((ICPPASTFunctionDeclarator)dcltor).getParameters();
if (ps.length != 1 || !paramHasTypeReferenceToTheAssociatedClassType(ps[0], null))
continue;
result.add((ICPPASTFunctionDeclarator)dcltor);
}
return result.toArray(new ICPPASTFunctionDeclarator[result.size()]);
}
/**
* @param compSpec the name the parameter must have in order to match, or null for any name
* @param dec
* @return whether the specified parameter is a reference to the associated class type, and
* (optionally) if it has the specified name
*/
private static boolean paramHasTypeReferenceToTheAssociatedClassType(IASTParameterDeclaration dec, String name) {
boolean result= false;
IASTDeclarator pdtor= ASTQueries.findTypeRelevantDeclarator(dec.getDeclarator());
if (pdtor != null && pdtor.getPointerOperators().length == 1 &&
pdtor.getPointerOperators()[0] instanceof ICPPASTReferenceOperator &&
pdtor.getParent() == dec &&
dec.getDeclSpecifier() instanceof ICPPASTNamedTypeSpecifier) {
ICPPASTNamedTypeSpecifier nts= (ICPPASTNamedTypeSpecifier) dec.getDeclSpecifier();
if (name == null || name.equals(nts.getName().getRawSignature())) {
result= true;
}
}
return result;
}
public boolean hasUserDeclaredConstructor() {
return hasUserDeclaredConstructor;
}
public boolean hasUserDeclaredCopyConstructor() {
return hasUserDeclaredCopyConstructor;
}
public boolean hasUserDeclaredCopyAssignmentOperator() {
return hasUserDeclaredCopyAssignmentOperator;
}
public boolean hasUserDeclaredDestructor() {
return hasUserDeclaredDestructor;
}
}