blob: fef48a68975af25e96406ac89ffb6f4d294613ae [file] [log] [blame]
package org.checkerframework.framework.flow;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.util.List;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import org.checkerframework.javacutil.TypeAnnotationUtils;
import org.checkerframework.javacutil.trees.TreeBuilder;
/**
* The TreeBuilder permits the creation of new AST Trees using the non-public Java compiler API
* TreeMaker. Initially, it will support construction of desugared Trees required by the CFGBuilder,
* e.g. the pieces of a desugared enhanced for loop.
*/
public class CFTreeBuilder extends TreeBuilder {
/**
* To avoid infinite recursions, record each wildcard that has been converted to a tree. This set
* is cleared each time {@link #buildAnnotatedType(TypeMirror)} is called.
*/
private final Set<WildcardType> visitedWildcards = new HashSet<>();
/**
* Creates a {@code CFTreeBuilder}.
*
* @param env environment
*/
public CFTreeBuilder(ProcessingEnvironment env) {
super(env);
}
/**
* Builds an AST Tree representing a type, including AnnotationTrees for its annotations.
*
* @param type the type
* @return a Tree representing the type
*/
public Tree buildAnnotatedType(TypeMirror type) {
visitedWildcards.clear();
return createAnnotatedType(type);
}
/**
* Converts a list of AnnotationMirrors to the a corresponding list of new AnnotationTrees.
*
* @param annotations the annotations
* @return new annotation trees representing the annotations
*/
private List<JCAnnotation> convertAnnotationMirrorsToAnnotationTrees(
Collection<? extends AnnotationMirror> annotations) {
List<JCAnnotation> annotationTrees = List.nil();
for (AnnotationMirror am : annotations) {
// TODO: what TypeAnnotationPosition should be used?
Attribute.TypeCompound typeCompound =
TypeAnnotationUtils.createTypeCompoundFromAnnotationMirror(
am, TypeAnnotationUtils.unknownTAPosition(), env);
JCAnnotation annotationTree = maker.Annotation(typeCompound);
JCAnnotation typeAnnotationTree =
maker.TypeAnnotation(annotationTree.getAnnotationType(), annotationTree.getArguments());
typeAnnotationTree.attribute = typeCompound;
annotationTrees = annotationTrees.append(typeAnnotationTree);
}
return annotationTrees;
}
/**
* Builds an AST Tree representing a type, including AnnotationTrees for its annotations. This
* internal method differs from the public {@link #buildAnnotatedType(TypeMirror)} only in that it
* does not reset the list of visited wildcards.
*
* @param type the type for which to create a tree
* @return a Tree representing the type
*/
private Tree createAnnotatedType(TypeMirror type) {
// Implementation based on com.sun.tools.javac.tree.TreeMaker.Type
// Convert the annotations from a set of AnnotationMirrors
// to a list of AnnotationTrees.
java.util.List<? extends AnnotationMirror> annotations = type.getAnnotationMirrors();
List<JCAnnotation> annotationTrees = convertAnnotationMirrorsToAnnotationTrees(annotations);
// Convert the underlying type from a TypeMirror to an ExpressionTree and combine with the
// AnnotationTrees to form a ClassTree of kind ANNOTATION_TYPE.
JCExpression typeTree;
switch (type.getKind()) {
case BYTE:
typeTree = maker.TypeIdent(TypeTag.BYTE);
break;
case CHAR:
typeTree = maker.TypeIdent(TypeTag.CHAR);
break;
case SHORT:
typeTree = maker.TypeIdent(TypeTag.SHORT);
break;
case INT:
typeTree = maker.TypeIdent(TypeTag.INT);
break;
case LONG:
typeTree = maker.TypeIdent(TypeTag.LONG);
break;
case FLOAT:
typeTree = maker.TypeIdent(TypeTag.FLOAT);
break;
case DOUBLE:
typeTree = maker.TypeIdent(TypeTag.DOUBLE);
break;
case BOOLEAN:
typeTree = maker.TypeIdent(TypeTag.BOOLEAN);
break;
case VOID:
typeTree = maker.TypeIdent(TypeTag.VOID);
break;
case TYPEVAR:
// No recursive annotations.
TypeVariable underlyingTypeVar = (TypeVariable) type;
typeTree = maker.Ident((TypeSymbol) underlyingTypeVar.asElement());
break;
case WILDCARD:
WildcardType wildcardType = (WildcardType) type;
boolean visitedBefore = !visitedWildcards.add(wildcardType);
if (!visitedBefore && wildcardType.getExtendsBound() != null) {
Tree annotatedExtendsBound = createAnnotatedType(wildcardType.getExtendsBound());
typeTree =
maker.Wildcard(
maker.TypeBoundKind(BoundKind.EXTENDS), (JCTree) annotatedExtendsBound);
} else if (!visitedBefore && wildcardType.getSuperBound() != null) {
Tree annotatedSuperBound = createAnnotatedType(wildcardType.getSuperBound());
typeTree =
maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), (JCTree) annotatedSuperBound);
} else {
typeTree = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
}
break;
case INTERSECTION:
IntersectionType intersectionType = (IntersectionType) type;
List<JCExpression> components = List.nil();
for (TypeMirror bound : intersectionType.getBounds()) {
components = components.append((JCExpression) createAnnotatedType(bound));
}
typeTree = maker.TypeIntersection(components);
break;
// case UNION:
// TODO: case UNION similar to INTERSECTION, but write test first.
case DECLARED:
typeTree = maker.Type((Type) type);
if (typeTree instanceof JCTypeApply) {
// Replace the type parameters with annotated versions.
DeclaredType annotatedDeclaredType = (DeclaredType) type;
List<JCExpression> typeArgTrees = List.nil();
for (TypeMirror arg : annotatedDeclaredType.getTypeArguments()) {
typeArgTrees = typeArgTrees.append((JCExpression) createAnnotatedType(arg));
}
JCExpression clazz = (JCExpression) ((JCTypeApply) typeTree).getType();
typeTree = maker.TypeApply(clazz, typeArgTrees);
}
break;
case ARRAY:
ArrayType arrayType = (ArrayType) type;
Tree componentTree = createAnnotatedType(arrayType.getComponentType());
typeTree = maker.TypeArray((JCExpression) componentTree);
break;
case ERROR:
typeTree = maker.TypeIdent(TypeTag.ERROR);
break;
default:
assert false : "unexpected type: " + type;
typeTree = null;
break;
}
typeTree.setType((Type) type);
if (annotationTrees.isEmpty()) {
return typeTree;
}
JCAnnotatedType annotatedTypeTree = maker.AnnotatedType(annotationTrees, typeTree);
annotatedTypeTree.setType((Type) type);
return annotatedTypeTree;
}
}