org.checkerframework.framework.flow.CFTreeBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java's type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
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 org.checkerframework.javacutil.TypeAnnotationUtils;
import org.checkerframework.javacutil.trees.TreeBuilder;
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;
/**
* 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 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 convertAnnotationMirrorsToAnnotationTrees(
Collection extends AnnotationMirror> annotations) {
List 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 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 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 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;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy