package org.checkerframework.framework.type;
import com.sun.source.tree.AnnotatedTypeTree;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ArrayTypeTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.IntersectionTypeTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.WildcardTree;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeMerger;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
/** Converts ExpressionTrees into AnnotatedTypeMirrors */
class TypeFromExpressionVisitor extends TypeFromTreeVisitor {
@Override
public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree node, AnnotatedTypeFactory f) {
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitArrayAccess(ArrayAccessTree node, AnnotatedTypeFactory f) {
Pair<Tree, AnnotatedTypeMirror> preAssCtxt = f.visitorState.getAssignmentContext();
try {
// TODO: what other trees shouldn't maintain the context?
f.visitorState.setAssignmentContext(null);
AnnotatedTypeMirror type = f.getAnnotatedType(node.getExpression());
assert type instanceof AnnotatedArrayType;
return ((AnnotatedArrayType) type).getComponentType();
} finally {
f.visitorState.setAssignmentContext(preAssCtxt);
}
}
@Override
public AnnotatedTypeMirror visitAssignment(AssignmentTree node, AnnotatedTypeFactory f) {
// Recurse on the type of the variable.
return visit(node.getVariable(), f);
}
@Override
public AnnotatedTypeMirror visitBinary(BinaryTree node, AnnotatedTypeFactory f) {
AnnotatedTypeMirror res = f.type(node);
// TODO: why do we need to clear the type?
res.clearAnnotations();
return res;
}
@Override
public AnnotatedTypeMirror visitCompoundAssignment(
CompoundAssignmentTree node, AnnotatedTypeFactory f) {
// Recurse on the type of the variable.
AnnotatedTypeMirror res = visit(node.getVariable(), f);
// TODO: why do we need to clear the type?
res.clearAnnotations();
return res;
}
@Override
public AnnotatedTypeMirror visitConditionalExpression(
ConditionalExpressionTree node, AnnotatedTypeFactory f) {
// The Java type of a conditional expression is generally the LUB of the boxed types
// of the true and false expressions, but with a few exceptions. See JLS 15.25.
// So, use the type of the ConditionalExpressionTree instead of InternalUtils#leastUpperBound
TypeMirror alub = InternalUtils.typeOf(node);
AnnotatedTypeMirror trueType = f.getAnnotatedType(node.getTrueExpression());
AnnotatedTypeMirror falseType = f.getAnnotatedType(node.getFalseExpression());
return AnnotatedTypes.leastUpperBound(f, trueType, falseType, alub);
}
@Override
public AnnotatedTypeMirror visitIdentifier(IdentifierTree node, AnnotatedTypeFactory f) {
if (node.getName().contentEquals("this") || node.getName().contentEquals("super")) {
AnnotatedDeclaredType res = f.getSelfType(node);
return res;
}
Element elt = TreeUtils.elementFromUse(node);
AnnotatedTypeMirror selfType = f.getImplicitReceiverType(node);
if (selfType != null) {
return AnnotatedTypes.asMemberOf(f.types, f, selfType, elt).asUse();
}
return f.getAnnotatedType(elt);
}
@Override
public AnnotatedTypeMirror visitInstanceOf(InstanceOfTree node, AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitLiteral(LiteralTree node, AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitMemberSelect(MemberSelectTree node, AnnotatedTypeFactory f) {
Element elt = TreeUtils.elementFromUse(node);
if (TreeUtils.isClassLiteral(node)) {
// the type of a class literal is the type of the "class" element.
return f.getAnnotatedType(elt);
}
String kind = elt.getKind().toString();
switch (elt.getKind()) {
case METHOD:
case PACKAGE: // "java.lang" in new java.lang.Short("2")
case CLASS: // o instanceof MyClass.InnerClass
case ENUM:
case INTERFACE: // o instanceof MyClass.InnerInterface
case ANNOTATION_TYPE:
return f.fromElement(elt);
default:
// Fall-through.
}
if (node.getIdentifier().contentEquals("this")) {
// TODO: why don't we use getSelfType here?
return f.getEnclosingType(
(TypeElement) InternalUtils.symbol(node.getExpression()), node);
} else {
// node must be a field access, so get the type of the expression, and then call
// asMemberOf.
AnnotatedTypeMirror t = f.getAnnotatedType(node.getExpression());
return AnnotatedTypes.asMemberOf(f.types, f, t, elt).asUse();
}
}
@Override
public AnnotatedTypeMirror visitMethodInvocation(
MethodInvocationTree node, AnnotatedTypeFactory f) {
AnnotatedExecutableType ex = f.methodFromUse(node).first;
return ex.getReturnType().asUse();
}
@Override
public AnnotatedTypeMirror visitNewArray(NewArrayTree node, AnnotatedTypeFactory f) {
// Don't use fromTypeTree here, because node.getType() is not an
// array type!
AnnotatedArrayType result = (AnnotatedArrayType) f.type(node);
if (node.getType() == null) // e.g., byte[] b = {(byte)1, (byte)2};
return result;
annotateArrayAsArray(result, node, f);
return result;
}
private AnnotatedTypeMirror descendBy(AnnotatedTypeMirror type, int depth) {
AnnotatedTypeMirror result = type;
while (depth > 0) {
result = ((AnnotatedArrayType) result).getComponentType();
depth--;
}
return result;
}
private void annotateArrayAsArray(
AnnotatedArrayType result, NewArrayTree node, AnnotatedTypeFactory f) {
// Copy annotations from the type.
AnnotatedTypeMirror treeElem = f.fromTypeTree(node.getType());
boolean hasInit = node.getInitializers() != null;
AnnotatedTypeMirror typeElem = descendBy(result, hasInit ? 1 : node.getDimensions().size());
while (true) {
typeElem.addAnnotations(treeElem.getAnnotations());
if (!(treeElem instanceof AnnotatedArrayType)) break;
assert typeElem instanceof AnnotatedArrayType;
treeElem = ((AnnotatedArrayType) treeElem).getComponentType();
typeElem = ((AnnotatedArrayType) typeElem).getComponentType();
}
// Add all dimension annotations.
int idx = 0;
AnnotatedTypeMirror level = result;
while (level.getKind() == TypeKind.ARRAY) {
AnnotatedArrayType array = (AnnotatedArrayType) level;
List<? extends AnnotationMirror> annos =
InternalUtils.annotationsFromArrayCreation(node, idx++);
array.addAnnotations(annos);
level = array.getComponentType();
}
// Add top-level annotations.
result.addAnnotations(InternalUtils.annotationsFromArrayCreation(node, -1));
}
/**
* Creates an AnnotatedDeclaredType for the NewClassTree and adds, for each hierarchy, one of:
*
* <ul>
* <li>an explicit annotation on the new class expression ({@code new @HERE MyClass()}), or
* <li>an explicit annotation on the declaration of the class ({@code @HERE class MyClass
* {}}), or
* <li>an explicit annotation on the declaration of the constructor ({@code @HERE public
* MyClass() {}}), or
* <li>no annotation for this hierarchy.
* </ul>
*
* @param node NewClassTree
* @param f the type factory
* @return AnnotatedDeclaredType of {@code node}
*/
@Override
public AnnotatedTypeMirror visitNewClass(NewClassTree node, AnnotatedTypeFactory f) {
// constructorFromUse return type has implicits
// so use fromNewClass which does diamond inference and only
// contains explicit annotations and those inherited from the class declaration
AnnotatedDeclaredType type = f.fromNewClass(node);
// Enum constructors lead to trouble.
// TODO: is there more to check? Can one annotate them?
if (isNewEnum(type)) {
return type;
}
// Add annotations that are on the constructor declaration.
// constructorFromUse gives us resolution of polymorphic qualifiers.
// However, it also applies defaulting, so we might apply too many qualifiers.
// Therefore, ensure to only add the qualifiers that are explicitly on
// the constructor, but then take the possibly substituted qualifier.
AnnotatedExecutableType ex = f.constructorFromUse(node).first;
AnnotatedTypes.copyOnlyExplicitConstructorAnnotations(f, type, ex);
return type;
}
@Override
public AnnotatedTypeMirror visitMemberReference(
MemberReferenceTree node, AnnotatedTypeFactory f) {
AnnotatedDeclaredType type =
(AnnotatedDeclaredType) f.toAnnotatedType(InternalUtils.typeOf(node), false);
return type;
}
@Override
public AnnotatedTypeMirror visitLambdaExpression(
LambdaExpressionTree node, AnnotatedTypeFactory f) {
AnnotatedDeclaredType type =
(AnnotatedDeclaredType) f.toAnnotatedType(InternalUtils.typeOf(node), false);
return type;
}
private boolean isNewEnum(AnnotatedDeclaredType type) {
return type.getUnderlyingType().asElement().getKind() == ElementKind.ENUM;
}
@Override
public AnnotatedTypeMirror visitParenthesized(ParenthesizedTree node, AnnotatedTypeFactory f) {
// Recurse on the expression inside the parens.
return visit(node.getExpression(), f);
}
@Override
public AnnotatedTypeMirror visitTypeCast(TypeCastTree node, AnnotatedTypeFactory f) {
// Use the annotated type of the type in the cast.
return f.fromTypeTree(node.getType());
}
@Override
public AnnotatedTypeMirror visitUnary(UnaryTree node, AnnotatedTypeFactory f) {
// TODO: why not visit(node.getExpression(), f)
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitWildcard(WildcardTree node, AnnotatedTypeFactory f) {
AnnotatedTypeMirror bound = visit(node.getBound(), f);
AnnotatedTypeMirror result = f.type(node);
assert result instanceof AnnotatedWildcardType;
// the first time getSuperBound/getExtendsBound is called the bound of this wildcard will be
// appropriately initialized where for the type of node, instead of replacing that bound
// we merge the annotations onto the initialized bound
// This ensures that the structure of the wildcard will match that created by BoundsInitializer/createType
if (node.getKind() == Tree.Kind.SUPER_WILDCARD) {
AnnotatedTypeMerger.merge(bound, ((AnnotatedWildcardType) result).getSuperBound());
} else if (node.getKind() == Tree.Kind.EXTENDS_WILDCARD) {
AnnotatedTypeMerger.merge(bound, ((AnnotatedWildcardType) result).getExtendsBound());
}
return result;
}
@Override
public AnnotatedTypeMirror visitPrimitiveType(PrimitiveTypeTree node, AnnotatedTypeFactory f) {
// for e.g. "int.class"
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitArrayType(ArrayTypeTree node, AnnotatedTypeFactory f) {
// for e.g. "int[].class"
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitParameterizedType(
ParameterizedTypeTree node, AnnotatedTypeFactory f) {
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitIntersectionType(
IntersectionTypeTree node, AnnotatedTypeFactory f) {
return f.fromTypeTree(node);
}
}