/*******************************************************************************
* 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:
* John Camelon (IBM) - Initial API and implementation
* Bryan Wilkinson (QNX)
* Mike Kucera (IBM)
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.LVALUE;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.PRVALUE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.glvalueType;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.prvalueType;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.typeFromFunctionCall;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICPPASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
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.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CVQualifier;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
public class CPPASTFieldReference extends ASTNode implements ICPPASTFieldReference, IASTAmbiguityParent,
ICPPASTCompletionContext {
private boolean isTemplate;
private IASTExpression owner;
private IASTName name;
private boolean isDeref;
private IASTImplicitName[] implicitNames = null;
public CPPASTFieldReference() {
}
public CPPASTFieldReference(IASTName name, IASTExpression owner) {
setFieldName(name);
setFieldOwner(owner);
}
public CPPASTFieldReference copy() {
return copy(CopyStyle.withoutLocations);
}
public CPPASTFieldReference copy(CopyStyle style) {
CPPASTFieldReference copy = new CPPASTFieldReference();
copy.setFieldName(name == null ? null : name.copy(style));
copy.setFieldOwner(owner == null ? null : owner.copy(style));
copy.isTemplate = isTemplate;
copy.isDeref = isDeref;
copy.setOffsetAndLength(this);
if (style == CopyStyle.withLocations) {
copy.setCopyLocation(this);
}
return copy;
}
public boolean isTemplate() {
return isTemplate;
}
public void setIsTemplate(boolean value) {
assertNotFrozen();
isTemplate = value;
}
public IASTExpression getFieldOwner() {
return owner;
}
public void setFieldOwner(IASTExpression expression) {
assertNotFrozen();
owner = expression;
if (expression != null) {
expression.setParent(this);
expression.setPropertyInParent(FIELD_OWNER);
}
}
public IASTName getFieldName() {
return name;
}
public void setFieldName(IASTName name) {
assertNotFrozen();
this.name = name;
if (name != null) {
name.setParent(this);
name.setPropertyInParent(FIELD_NAME);
}
}
public boolean isPointerDereference() {
return isDeref;
}
public void setIsPointerDereference(boolean value) {
assertNotFrozen();
isDeref = value;
}
public IASTImplicitName[] getImplicitNames() {
if (implicitNames == null) {
if (!isDeref)
return implicitNames = IASTImplicitName.EMPTY_NAME_ARRAY;
// collect the function bindings
List<ICPPFunction> functionBindings = new ArrayList<ICPPFunction>();
getFieldOwnerType(functionBindings);
if (functionBindings.isEmpty())
return implicitNames = IASTImplicitName.EMPTY_NAME_ARRAY;
// create a name to wrap each binding
implicitNames = new IASTImplicitName[functionBindings.size()];
int i=-1;
for (ICPPFunction op : functionBindings) {
if (op != null && !(op instanceof CPPImplicitFunction)) {
CPPASTImplicitName operatorName = new CPPASTImplicitName(OverloadableOperator.ARROW, this);
operatorName.setBinding(op);
operatorName.computeOperatorOffsets(owner, true);
implicitNames[++i] = operatorName;
}
}
implicitNames= ArrayUtil.trimAt(IASTImplicitName.class, implicitNames, i);
}
return implicitNames;
}
@Override
public boolean accept(ASTVisitor action) {
if (action.shouldVisitExpressions) {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default: break;
}
}
if (owner != null && !owner.accept(action))
return false;
if (action.shouldVisitImplicitNames) {
for (IASTImplicitName name : getImplicitNames()) {
if (!name.accept(action))
return false;
}
}
if (name != null && !name.accept(action))
return false;
if (action.shouldVisitExpressions) {
switch (action.leave(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default: break;
}
}
return true;
}
public int getRoleForName(IASTName n) {
if (n == name)
return r_reference;
return r_unclear;
}
public void replace(IASTNode child, IASTNode other) {
if (child == owner) {
other.setPropertyInParent(child.getPropertyInParent());
other.setParent(child.getParent());
owner = (IASTExpression) other;
}
}
public IType getExpressionType() {
IASTName name= getFieldName();
IBinding binding = name.resolvePreBinding();
try {
if (binding instanceof IVariable) {
IType e2= ((IVariable) binding).getType();
e2= SemanticUtil.getNestedType(e2, TDEF);
if (e2 instanceof ICPPReferenceType) {
e2= glvalueType(e2);
} else if (binding instanceof ICPPField && !((ICPPField) binding).isStatic()) {
IType e1= getFieldOwner().getExpressionType();
if (isPointerDereference()) {
e1= SemanticUtil.getNestedType(e1, TDEF | REF | CVTYPE);
if (e1 instanceof IPointerType) {
e1= ((IPointerType) e1).getType();
}
}
e2 = addQualifiersForAccess((ICPPField) binding, e2, e1);
if (!isPointerDereference() && owner.getValueCategory() == PRVALUE) {
e2= prvalueType(e2);
} else {
e2= glvalueType(e2);
}
}
return SemanticUtil.mapToAST(e2, this);
}
if (binding instanceof IEnumerator) {
return ((IEnumerator) binding).getType();
}
if (binding instanceof IFunction) {
return SemanticUtil.mapToAST(((IFunction) binding).getType(), this);
}
if (binding instanceof ICPPUnknownBinding) {
// mstodo type of unknown.
return CPPUnknownClass.createUnnamedInstance();
}
if (binding instanceof IProblemBinding) {
return new ProblemType(ISemanticProblem.TYPE_UNRESOLVED_NAME);
}
return new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION);
} catch (DOMException e) {
return e.getProblem();
}
}
public static IType addQualifiersForAccess(ICPPField field, IType fieldType, IType ownerType) {
CVQualifier cvq1 = SemanticUtil.getCVQualifier(ownerType);
CVQualifier cvq2 = SemanticUtil.getCVQualifier(fieldType);
if (field.isMutable()) {
// Remove const, add union of volatile.
if (cvq2.isConst()) {
fieldType= SemanticUtil.getNestedType(fieldType, ALLCVQ | TDEF | REF);
}
fieldType= SemanticUtil.addQualifiers(fieldType, false, cvq1.isVolatile() || cvq2.isVolatile(), cvq2.isRestrict());
} else {
fieldType= SemanticUtil.addQualifiers(fieldType, cvq1.isConst(), cvq1.isVolatile(), cvq2.isRestrict());
}
return fieldType;
}
public ValueCategory getValueCategory() {
IASTName name= getFieldName();
IBinding binding = name.resolvePreBinding();
if (binding instanceof IVariable) {
IType e2= ((IVariable) binding).getType();
e2= SemanticUtil.getNestedType(e2, TDEF);
if (e2 instanceof ICPPReferenceType) {
return LVALUE;
}
if (binding instanceof ICPPField && !((ICPPField) binding).isStatic()) {
if (isPointerDereference())
return LVALUE;
return owner.getValueCategory();
}
return LVALUE;
}
if (binding instanceof IFunction) {
return LVALUE;
}
return PRVALUE;
}
public boolean isLValue() {
return getValueCategory() == LVALUE;
}
public IBinding[] findBindings(IASTName n, boolean isPrefix, String[] namespaces) {
IBinding[] bindings = CPPSemantics.findBindingsForContentAssist(n, isPrefix, namespaces);
List<IBinding> filtered = new ArrayList<IBinding>();
for (IBinding binding : bindings) {
if (binding instanceof ICPPMethod) {
ICPPMethod method = (ICPPMethod) binding;
if (method.isImplicit()) {
continue;
}
}
filtered.add(binding);
}
return filtered.toArray(new IBinding[filtered.size()]);
}
public IBinding[] findBindings(IASTName n, boolean isPrefix) {
return findBindings(n, isPrefix, null);
}
/**
* For a pointer dereference expression e1->e2, return the type that e1 ultimately evaluates to
* after chaining overloaded class member access operators <code>operator->()</code> calls.
*/
public IType getFieldOwnerType() {
return getFieldOwnerType(null);
}
/*
* Also collects the function bindings if requested.
*/
private IType getFieldOwnerType(Collection<ICPPFunction> functionBindings) {
final IASTExpression owner = getFieldOwner();
if (owner == null)
return null;
IType type= owner.getExpressionType();
if (!isPointerDereference())
return type;
// bug 205964: as long as the type is a class type, recurse.
// Be defensive and allow a max of 20 levels.
for (int j = 0; j < 20; j++) {
// for unknown types we cannot determine the overloaded -> operator
IType classType= getUltimateTypeUptoPointers(type);
if (classType instanceof ICPPUnknownType)
return CPPUnknownClass.createUnnamedInstance();
if (!(classType instanceof ICPPClassType))
break;
/*
* 13.5.6-1: An expression x->m is interpreted as (x.operator->())->m for a
* class object x of type T
*
* Construct an AST fragment for x.operator-> which the lookup routines can
* examine for type information.
*/
ICPPFunction op = CPPSemantics.findOverloadedOperator(this, type, (ICPPClassType) classType);
if (op == null)
break;
if (functionBindings != null)
functionBindings.add(op);
type= typeFromFunctionCall(op);
type= SemanticUtil.mapToAST(type, owner);
}
IType prValue= prvalueType(type);
if (prValue instanceof IPointerType) {
return glvalueType(((IPointerType) prValue).getType());
}
return new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION);
}
}