/*******************************************************************************
* 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:
* 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 static org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter.EMPTY_CPPPARAMETER_ARRAY;
import static org.eclipse.cdt.core.parser.util.ArrayUtil.addAll;
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.CPPBasicType.UNSPECIFIED_TYPE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import java.util.Arrays;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IName;
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.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
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.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.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
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.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
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.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.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.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
import org.eclipse.core.runtime.IStatus;
/**
* Base implementation for C++ scopes.
*/
public class CPPClassScope extends CPPScope implements ICPPClassScope {
private static final ICPPFunctionType DESTRUCTOR_FUNCTION_TYPE =
CPPVisitor.createImplicitFunctionType(UNSPECIFIED_TYPE, EMPTY_CPPPARAMETER_ARRAY, false, false);
private ICPPMethod[] implicits;
public CPPClassScope(ICPPASTCompositeTypeSpecifier physicalNode) {
super(physicalNode);
}
@Override
public EScopeKind getKind() {
return EScopeKind.eClassType;
}
/**
* Adds in default constructor, copy constructor, copy assignment operator and destructor,
* if appropriate. The 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 classType = (ICPPClassType) binding;
if (classType instanceof ICPPClassTemplate) {
classType= (ICPPClassType) ((ICPPClassTemplate) classType).asDeferredInstance();
}
char[] className = name.getLookupKey();
IType pType = new CPPReferenceType(SemanticUtil.constQualify(classType), false);
ICPPParameter[] params = new ICPPParameter[] { new CPPParameter(pType, 0) };
int i= 0;
ImplicitsAnalysis ia= new ImplicitsAnalysis(compTypeSpec, classType);
implicits= new ICPPMethod[ia.getImplicitsToDeclareCount()];
if (!ia.hasUserDeclaredConstructor()) {
// Default constructor: A(void)
ICPPMethod m = new CPPImplicitConstructor(this, className, EMPTY_CPPPARAMETER_ARRAY,
compTypeSpec);
implicits[i++] = m;
addBinding(m);
}
if (!ia.hasUserDeclaredCopyConstructor()) {
// Copy constructor: A(const A &)
ICPPMethod m = new CPPImplicitConstructor(this, className, params, compTypeSpec);
implicits[i++] = m;
addBinding(m);
}
if (!ia.hasUserDeclaredCopyAssignmentOperator()) {
// Copy assignment operator: A& operator = (const A &)
IType refType = new CPPReferenceType(classType, false);
ICPPFunctionType ft= CPPVisitor.createImplicitFunctionType(refType, params, false, false);
ICPPMethod m =
new CPPImplicitMethod(this, OverloadableOperator.ASSIGN.toCharArray(), ft, params, false);
implicits[i++] = m;
addBinding(m);
}
if (!ia.hasUserDeclaredDestructor()) {
// Destructor: ~A()
char[] dtorName = CharArrayUtils.concat("~".toCharArray(), className); //$NON-NLS-1$
ICPPMethod m = new CPPImplicitMethod(this, dtorName, DESTRUCTOR_FUNCTION_TYPE, EMPTY_CPPPARAMETER_ARRAY,
false);
implicits[i++] = m;
addBinding(m);
}
markInheritedConstructorsSourceBases(compTypeSpec);
}
/**
* Marks bases that serve as sources of inherited constructors.
*/
private void markInheritedConstructorsSourceBases(ICPPASTCompositeTypeSpecifier compositeTypeSpec) {
ICPPBase[] bases = ClassTypeHelper.getBases(getClassType(), compositeTypeSpec);
if (bases.length == 0)
return;
IASTDeclaration[] members = compositeTypeSpec.getMembers();
for (IASTDeclaration member : members) {
if (member instanceof ICPPASTUsingDeclaration) {
IASTName name = ((ICPPASTUsingDeclaration) member).getName();
if (!(name instanceof ICPPASTQualifiedName))
continue;
ICPPASTQualifiedName qName = (ICPPASTQualifiedName) name;
ICPPASTNameSpecifier[] qualifier = qName.getQualifier();
IBinding parent = qualifier[qualifier.length - 1].resolveBinding();
if (!(parent instanceof IType) || parent instanceof IProblemBinding)
continue;
if (isConstructorNameForType(qName.getLastName().getSimpleID(), (IType) parent)) {
IType type = SemanticUtil.getNestedType((IType) parent, TDEF);
for (ICPPBase base : bases) {
IType baseClass = base.getBaseClassType();
if (type.isSameType(baseClass)) {
if (base instanceof CPPBaseClause) {
((CPPBaseClause) base).setInheritedConstructorsSource(true);
} else {
CCorePlugin.log(IStatus.ERROR, "Unexpected type of base (" //$NON-NLS-1$
+ base.getClass().getSimpleName() + ") for '" //$NON-NLS-1$
+ compositeTypeSpec.getRawSignature() + "'"); //$NON-NLS-1$
}
}
}
}
}
}
}
private static boolean isConstructorNameForType(char[] lastName, IType type) {
while (type instanceof IBinding) {
if (Arrays.equals(((IBinding) type).getNameCharArray(), lastName))
return true;
if (!(type instanceof ITypedef))
break;
type = ((ITypedef) type).getType();
}
return false;
}
@Override
public IScope getParent() {
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
return CPPVisitor.getContainingNonTemplateScope(compName);
}
@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 ICPPASTNameSpecifier[] qualifier = qname.getQualifier();
for (int i = qualifier.length; --i >= 0;) {
if (b == null)
return;
char[] segmentName;
if (qualifier[i] instanceof IASTName) {
segmentName = ((IASTName) qualifier[i]).getLookupKey();
} else {
IBinding segmentBinding = qualifier[i].resolveBinding();
if (segmentBinding == null)
return;
segmentName = segmentBinding.getNameCharArray();
}
if (!CharArrayUtils.equals(segmentName, 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);
}
}
@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(ScopeLookupData lookup) {
char[] c = lookup.getLookupKey();
final boolean prefixLookup= lookup.isPrefixLookup();
ICPPASTCompositeTypeSpecifier compType = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
IASTName compName = compType.getName().getLastName();
if (compName instanceof ICPPASTTemplateId) {
compName= ((ICPPASTTemplateId) compName).getTemplateName();
}
IBinding[] result = IBinding.EMPTY_BINDING_ARRAY;
int n = 0;
if ((!prefixLookup && CharArrayUtils.equals(c, compName.getLookupKey()))
|| (prefixLookup && ContentAssistMatcherFactory.getInstance().match(c, compName.getLookupKey()))) {
final IASTName lookupName = lookup.getLookupName();
if (shallReturnConstructors(lookupName, prefixLookup)) {
ICPPConstructor[] constructors = getConstructors(lookupName, lookup.isResolve());
result = addAll(result, constructors);
n += constructors.length;
}
// 9.2 ... The class-name is also inserted into the scope of the class itself.
result = appendAt(result, n++, compName.resolveBinding());
}
IBinding[] bindings = super.getBindings(lookup);
result = addAll(result, bindings);
n += bindings.length;
return trim(result, n);
}
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;
}
@Override
public ICPPConstructor[] getConstructors() {
return getConstructors(null, true);
}
private ICPPConstructor[] getConstructors(IASTName forName, boolean forceResolve) {
populateCache();
final CharArrayObjectMap<Object> 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;
ICPPConstructor[] bs = ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
int n = 0;
for (int i = 0; i < set.size(); i++) {
Object obj = set.keyAt(i);
if (obj instanceof IASTName) {
IASTName name = (IASTName) obj;
binding = shouldResolve(forceResolve, name, forName) ?
name.resolveBinding() : name.getBinding();
if (binding instanceof ICPPConstructor) {
bs = appendAt(bs, n++, (ICPPConstructor) binding);
}
} else if (obj instanceof ICPPConstructor) {
bs = appendAt(bs, n++, (ICPPConstructor) obj);
}
}
return trim(bs, n);
} else if (o instanceof IASTName) {
if (shouldResolve(forceResolve, (IASTName) o, forName) || ((IASTName) o).getBinding() != null) {
// Always store the name, rather than the binding, so 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;
}
@Override
public IBinding[] find(String name, IASTTranslationUnit tu) {
return find(name);
}
@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 (name == null)
return false;
if (!isPrefixLookup) {
return CPPVisitor.isConstructorDeclaration(name)
|| CPPVisitor.isLastNameInUsingDeclaration(name);
}
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;
}
@Override
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());
}
@Override
public ICPPMethod[] getImplicitMethods() {
if (implicits == null)
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
return implicits;
}
@Override
public IName getScopeName() {
IASTNode node = getPhysicalNode();
if (node instanceof ICPPASTCompositeTypeSpecifier) {
return ((ICPPASTCompositeTypeSpecifier) node).getName();
}
return null;
}
}