blob: 13b180dbaa9fe35c73ad36a4277d5f794bd530e7 [file] [log] [blame]
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.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.util.AnnotatedTypes;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
/**
* Converts ExpressionTrees into AnnotatedTypeMirrors.
*
* <p>The type of some expressions depends on the checker, so for these expressions, a checker
* should add annotations in a {@link
* org.checkerframework.framework.type.treeannotator.TreeAnnotator} and/or the {@link
* org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator}. These trees are:
*
* <ul>
* <li>{@code BinaryTree}
* <li>{@code CompoundAssignmentTree}
* <li>{@code InstanceOfTree}
* <li>{@code LiteralTree}
* <li>{@code UnaryTree}
* </ul>
*
* Other expressions are in fact type trees and their annotataed type mirrors are computed as type
* trees:
*
* <ul>
* <li>{@code AnnotatedTypeTree}
* <li>{@code TypeCastTree}
* <li>{@code PrimitiveTypeTree}
* <li>{@code ArrayTypeTree}
* <li>{@code ParameterizedTypeTree}
* <li>{@code IntersectionTypeTree}
* </ul>
*/
class TypeFromExpressionVisitor extends TypeFromTreeVisitor {
@Override
public AnnotatedTypeMirror visitBinary(BinaryTree node, AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitCompoundAssignment(
CompoundAssignmentTree node, AnnotatedTypeFactory f) {
return f.type(node);
}
@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 visitUnary(UnaryTree node, AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree node, AnnotatedTypeFactory f) {
return f.fromTypeTree(node);
}
@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 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);
}
@Override
public AnnotatedTypeMirror visitMemberReference(
MemberReferenceTree node, AnnotatedTypeFactory f) {
return f.toAnnotatedType(TreeUtils.typeOf(node), false);
}
@Override
public AnnotatedTypeMirror visitLambdaExpression(
LambdaExpressionTree node, AnnotatedTypeFactory f) {
return f.toAnnotatedType(TreeUtils.typeOf(node), false);
}
@Override
public AnnotatedTypeMirror visitAssignment(AssignmentTree node, AnnotatedTypeFactory f) {
// Recurse on the type of the variable.
return visit(node.getVariable(), f);
}
@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 = TreeUtils.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 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);
}
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")) {
// Node is "MyClass.this", where "MyClass" may be the innermost enclosing type or any
// outer type.
return f.getEnclosingType(TypesUtils.getTypeElement(TreeUtils.typeOf(node)), 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 visitArrayAccess(ArrayAccessTree node, AnnotatedTypeFactory f) {
Pair<Tree, AnnotatedTypeMirror> preAssignmentContext = f.visitorState.getAssignmentContext();
try {
// TODO: what other trees shouldn't maintain the context?
f.visitorState.setAssignmentContext(null);
AnnotatedTypeMirror type = f.getAnnotatedType(node.getExpression());
if (type.getKind() == TypeKind.ARRAY) {
return ((AnnotatedArrayType) type).getComponentType();
} else if (type.getKind() == TypeKind.WILDCARD
&& ((AnnotatedWildcardType) type).isUninferredTypeArgument()) {
// Clean-up after Issue #979.
AnnotatedTypeMirror wcbound = ((AnnotatedWildcardType) type).getExtendsBound();
if (wcbound instanceof AnnotatedArrayType) {
return ((AnnotatedArrayType) wcbound).getComponentType();
}
}
throw new BugInCF("Unexpected type: " + type);
} finally {
f.visitorState.setAssignmentContext(preAssignmentContext);
}
}
@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 = TreeUtils.annotationsFromArrayCreation(node, idx++);
array.addAnnotations(annos);
level = array.getComponentType();
}
// Add top-level annotations.
result.addAnnotations(TreeUtils.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 or default annotation on the declaration of the constructor ({@code @HERE
* public MyClass() {}}).
* </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 default annotations
// so use fromNewClass which does diamond inference and only
// contains explicit annotations.
AnnotatedDeclaredType type = f.fromNewClass(node);
// Add annotations that are on the constructor declaration.
AnnotatedExecutableType ex = f.constructorFromUse(node).executableType;
type.addMissingAnnotations(ex.getReturnType().getAnnotations());
return type;
}
@Override
public AnnotatedTypeMirror visitMethodInvocation(
MethodInvocationTree node, AnnotatedTypeFactory f) {
AnnotatedExecutableType ex = f.methodFromUse(node).executableType;
return ex.getReturnType().asUse();
}
@Override
public AnnotatedTypeMirror visitParenthesized(ParenthesizedTree node, AnnotatedTypeFactory f) {
// Recurse on the expression inside the parens.
return visit(node.getExpression(), f);
}
@Override
public AnnotatedTypeMirror visitWildcard(WildcardTree node, AnnotatedTypeFactory f) {
AnnotatedTypeMirror bound = visit(node.getBound(), f);
AnnotatedTypeMirror result = f.type(node);
assert result instanceof AnnotatedWildcardType;
// Instead of directly overwriting the bound, replace each annotation
// to ensure that the structure of the wildcard will match that created by
// BoundsInitializer/createType.
if (node.getKind() == Tree.Kind.SUPER_WILDCARD) {
f.replaceAnnotations(bound, ((AnnotatedWildcardType) result).getSuperBound());
} else if (node.getKind() == Tree.Kind.EXTENDS_WILDCARD) {
f.replaceAnnotations(bound, ((AnnotatedWildcardType) result).getExtendsBound());
}
return result;
}
}