org.checkerframework.javacutil.trees.TreeBuilder 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.javacutil.trees;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;
/**
* The TreeBuilder permits the creation of new AST Trees using the non-public Java compiler API
* TreeMaker.
*/
public class TreeBuilder {
protected final Elements elements;
protected final Types modelTypes;
protected final com.sun.tools.javac.code.Types javacTypes;
protected final TreeMaker maker;
protected final Names names;
protected final Symtab symtab;
protected final ProcessingEnvironment env;
public TreeBuilder(ProcessingEnvironment env) {
this.env = env;
Context context = ((JavacProcessingEnvironment) env).getContext();
elements = env.getElementUtils();
modelTypes = env.getTypeUtils();
javacTypes = com.sun.tools.javac.code.Types.instance(context);
maker = TreeMaker.instance(context);
names = Names.instance(context);
symtab = Symtab.instance(context);
}
/**
* Builds an AST Tree to access the iterator() method of some iterable expression.
*
* @param iterableExpr an expression whose type is a subtype of Iterable
* @return a MemberSelectTree that accesses the iterator() method of the expression
*/
public MemberSelectTree buildIteratorMethodAccess(ExpressionTree iterableExpr) {
DeclaredType exprType = (DeclaredType) TypesUtils.upperBound(TreeUtils.typeOf(iterableExpr));
assert exprType != null : "expression must be of declared type Iterable<>";
TypeElement exprElement = (TypeElement) exprType.asElement();
// Find the iterator() method of the iterable type
Symbol.MethodSymbol iteratorMethod = null;
for (ExecutableElement method : ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
if (method.getParameters().isEmpty() && method.getSimpleName().contentEquals("iterator")) {
iteratorMethod = (Symbol.MethodSymbol) method;
}
}
assert iteratorMethod != null
: "@AssumeAssertion(nullness): no iterator method declared for expression type";
Type.MethodType methodType = (Type.MethodType) iteratorMethod.asType();
Symbol.TypeSymbol methodClass = methodType.asElement();
DeclaredType iteratorType = (DeclaredType) methodType.getReturnType();
iteratorType =
(DeclaredType) javacTypes.asSuper((Type) iteratorType, symtab.iteratorType.asElement());
int numIterTypeArgs = iteratorType.getTypeArguments().size();
assert numIterTypeArgs <= 1 : "expected at most one type argument for Iterator";
if (numIterTypeArgs == 1) {
TypeMirror elementType = iteratorType.getTypeArguments().get(0);
// Remove captured type variable from a wildcard.
if (elementType instanceof Type.CapturedType) {
elementType = ((Type.CapturedType) elementType).wildcard;
iteratorType =
modelTypes.getDeclaredType(
(TypeElement) modelTypes.asElement(iteratorType), elementType);
}
}
// Replace the iterator method's generic return type with
// the actual element type of the expression.
Type.MethodType updatedMethodType =
new Type.MethodType(
com.sun.tools.javac.util.List.nil(),
(Type) iteratorType,
com.sun.tools.javac.util.List.nil(),
methodClass);
JCTree.JCFieldAccess iteratorAccess =
(JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) iterableExpr, iteratorMethod);
iteratorAccess.setType(updatedMethodType);
return iteratorAccess;
}
/**
* Builds an AST Tree to access the hasNext() method of an iterator.
*
* @param iteratorExpr an expression whose type is a subtype of Iterator
* @return a MemberSelectTree that accesses the hasNext() method of the expression
*/
public MemberSelectTree buildHasNextMethodAccess(ExpressionTree iteratorExpr) {
DeclaredType exprType = (DeclaredType) TreeUtils.typeOf(iteratorExpr);
assert exprType != null : "expression must be of declared type Iterator<>";
TypeElement exprElement = (TypeElement) exprType.asElement();
// Find the hasNext() method of the iterator type
Symbol.MethodSymbol hasNextMethod = null;
for (ExecutableElement method : ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
if (method.getParameters().isEmpty() && method.getSimpleName().contentEquals("hasNext")) {
hasNextMethod = (Symbol.MethodSymbol) method;
}
}
assert hasNextMethod != null : "no hasNext method declared for expression type";
JCTree.JCFieldAccess hasNextAccess =
(JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) iteratorExpr, hasNextMethod);
hasNextAccess.setType(hasNextMethod.asType());
return hasNextAccess;
}
/**
* Builds an AST Tree to access the next() method of an iterator.
*
* @param iteratorExpr an expression whose type is a subtype of Iterator
* @return a MemberSelectTree that accesses the next() method of the expression
*/
public MemberSelectTree buildNextMethodAccess(ExpressionTree iteratorExpr) {
DeclaredType exprType = (DeclaredType) TreeUtils.typeOf(iteratorExpr);
assert exprType != null : "expression must be of declared type Iterator<>";
TypeElement exprElement = (TypeElement) exprType.asElement();
// Find the next() method of the iterator type
Symbol.MethodSymbol nextMethod = null;
for (ExecutableElement method : ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
if (method.getParameters().isEmpty() && method.getSimpleName().contentEquals("next")) {
nextMethod = (Symbol.MethodSymbol) method;
}
}
assert nextMethod != null
: "@AssumeAssertion(nullness): no next method declared for expression type";
Type.MethodType methodType = (Type.MethodType) nextMethod.asType();
Symbol.TypeSymbol methodClass = methodType.asElement();
Type elementType;
if (exprType.getTypeArguments().isEmpty()) {
elementType = symtab.objectType;
} else {
elementType = (Type) exprType.getTypeArguments().get(0);
}
// Replace the next method's generic return type with
// the actual element type of the expression.
Type.MethodType updatedMethodType =
new Type.MethodType(
com.sun.tools.javac.util.List.nil(),
elementType,
com.sun.tools.javac.util.List.nil(),
methodClass);
JCTree.JCFieldAccess nextAccess =
(JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) iteratorExpr, nextMethod);
nextAccess.setType(updatedMethodType);
return nextAccess;
}
/**
* Builds an AST Tree to dereference the length field of an array.
*
* @param expression the array expression whose length is being accessed
* @return a MemberSelectTree to dereference the length of the array
*/
public MemberSelectTree buildArrayLengthAccess(ExpressionTree expression) {
return (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expression, symtab.lengthVar);
}
/**
* Builds an AST Tree to call a method designated by the argument expression.
*
* @param methodExpr an expression denoting a method with no arguments
* @return a MethodInvocationTree to call the argument method
*/
public MethodInvocationTree buildMethodInvocation(ExpressionTree methodExpr) {
return maker.App((JCTree.JCExpression) methodExpr);
}
/**
* Builds an AST Tree to call a method designated by methodExpr, with one argument designated by
* argExpr.
*
* @param methodExpr an expression denoting a method with one argument
* @param argExpr an expression denoting an argument to the method
* @return a MethodInvocationTree to call the argument method
*/
public MethodInvocationTree buildMethodInvocation(
ExpressionTree methodExpr, ExpressionTree argExpr) {
return maker.App(
(JCTree.JCExpression) methodExpr,
com.sun.tools.javac.util.List.of((JCTree.JCExpression) argExpr));
}
/**
* Builds an AST Tree to declare and initialize a variable, with no modifiers.
*
* @param type the type of the variable
* @param name the name of the variable
* @param owner the element containing the new symbol
* @param initializer the initializer expression
* @return a VariableDeclTree declaring the new variable
*/
public VariableTree buildVariableDecl(
TypeMirror type, String name, Element owner, ExpressionTree initializer) {
DetachedVarSymbol sym =
new DetachedVarSymbol(0, names.fromString(name), (Type) type, (Symbol) owner);
VariableTree tree = maker.VarDef(sym, (JCTree.JCExpression) initializer);
sym.setDeclaration(tree);
return tree;
}
/**
* Builds an AST Tree to declare and initialize a variable. The type of the variable is specified
* by a Tree.
*
* @param type the type of the variable, as a Tree
* @param name the name of the variable
* @param owner the element containing the new symbol
* @param initializer the initializer expression
* @return a VariableDeclTree declaring the new variable
*/
public VariableTree buildVariableDecl(
Tree type, String name, Element owner, ExpressionTree initializer) {
Type typeMirror = (Type) TreeUtils.typeOf(type);
DetachedVarSymbol sym =
new DetachedVarSymbol(0, names.fromString(name), typeMirror, (Symbol) owner);
JCTree.JCModifiers mods = maker.Modifiers(0);
JCTree.JCVariableDecl decl =
maker.VarDef(mods, sym.name, (JCTree.JCExpression) type, (JCTree.JCExpression) initializer);
decl.setType(typeMirror);
decl.sym = sym;
sym.setDeclaration(decl);
return decl;
}
/**
* Builds an AST Tree to refer to a variable.
*
* @param decl the declaration of the variable
* @return an IdentifierTree to refer to the variable
*/
public IdentifierTree buildVariableUse(VariableTree decl) {
return (IdentifierTree) maker.Ident((JCTree.JCVariableDecl) decl);
}
/**
* Builds an AST Tree to cast the type of an expression.
*
* @param type the type to cast to
* @param expr the expression to be cast
* @return a cast of the expression to the type
*/
public TypeCastTree buildTypeCast(TypeMirror type, ExpressionTree expr) {
return maker.TypeCast((Type) type, (JCTree.JCExpression) expr);
}
/**
* Builds an AST Tree to assign an expression to a variable.
*
* @param variable the declaration of the variable to assign to
* @param expr the expression to be assigned
* @return a statement assigning the expression to the variable
*/
public StatementTree buildAssignment(VariableTree variable, ExpressionTree expr) {
return maker.Assignment(TreeInfo.symbolFor((JCTree) variable), (JCTree.JCExpression) expr);
}
/**
* Builds an AST Tree to assign an RHS expression to an LHS expression.
*
* @param lhs the expression to be assigned to
* @param rhs the expression to be assigned
* @return a statement assigning the expression to the variable
*/
public AssignmentTree buildAssignment(ExpressionTree lhs, ExpressionTree rhs) {
JCTree.JCAssign assign = maker.Assign((JCTree.JCExpression) lhs, (JCTree.JCExpression) rhs);
assign.setType((Type) TreeUtils.typeOf(lhs));
return assign;
}
/** Builds an AST Tree representing a literal value of primitive or String type. */
public LiteralTree buildLiteral(Object value) {
return maker.Literal(value);
}
/**
* Builds an AST Tree to compare two operands with less than.
*
* @param left the left operand tree
* @param right the right operand tree
* @return a Tree representing "left < right"
*/
public BinaryTree buildLessThan(ExpressionTree left, ExpressionTree right) {
JCTree.JCBinary binary =
maker.Binary(JCTree.Tag.LT, (JCTree.JCExpression) left, (JCTree.JCExpression) right);
binary.setType((Type) modelTypes.getPrimitiveType(TypeKind.BOOLEAN));
return binary;
}
/**
* Builds an AST Tree to dereference an array.
*
* @param array the array to dereference
* @param index the index at which to dereference
* @return a Tree representing the dereference
*/
public ArrayAccessTree buildArrayAccess(ExpressionTree array, ExpressionTree index) {
ArrayType arrayType = (ArrayType) TreeUtils.typeOf(array);
JCTree.JCArrayAccess access =
maker.Indexed((JCTree.JCExpression) array, (JCTree.JCExpression) index);
access.setType((Type) arrayType.getComponentType());
return access;
}
/**
* Builds an AST Tree to refer to a class name.
*
* @param elt an element representing the class
* @return an IdentifierTree referring to the class
*/
public IdentifierTree buildClassUse(Element elt) {
return maker.Ident((Symbol) elt);
}
/**
* Builds an AST Tree to access the valueOf() method of boxed type such as Short or Float.
*
* @param expr an expression whose type is a boxed type
* @return a MemberSelectTree that accesses the valueOf() method of the expression
*/
public MemberSelectTree buildValueOfMethodAccess(Tree expr) {
TypeMirror boxedType = TreeUtils.typeOf(expr);
assert TypesUtils.isBoxedPrimitive(boxedType);
// Find the valueOf(unboxedType) method of the boxed type
Symbol.MethodSymbol valueOfMethod = getValueOfMethod(env, boxedType);
Type.MethodType methodType = (Type.MethodType) valueOfMethod.asType();
JCTree.JCFieldAccess valueOfAccess =
(JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, valueOfMethod);
valueOfAccess.setType(methodType);
return valueOfAccess;
}
/** Returns the valueOf method of a boxed type such as Short or Float. */
public static Symbol.MethodSymbol getValueOfMethod(
ProcessingEnvironment env, TypeMirror boxedType) {
Symbol.MethodSymbol valueOfMethod = null;
TypeMirror unboxedType = env.getTypeUtils().unboxedType(boxedType);
TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement();
for (ExecutableElement method :
ElementFilter.methodsIn(env.getElementUtils().getAllMembers(boxedElement))) {
if (method.getSimpleName().contentEquals("valueOf")) {
List extends VariableElement> params = method.getParameters();
if (params.size() == 1
&& env.getTypeUtils().isSameType(params.get(0).asType(), unboxedType)) {
valueOfMethod = (Symbol.MethodSymbol) method;
}
}
}
assert valueOfMethod != null
: "@AssumeAssertion(nullness): no valueOf method declared for boxed type";
return valueOfMethod;
}
/**
* Builds an AST Tree to access the *Value() method of a boxed type such as Short or Float, where
* * is the corresponding primitive type (i.e. shortValue or floatValue).
*
* @param expr an expression whose type is a boxed type
* @return a MemberSelectTree that accesses the *Value() method of the expression
*/
public MemberSelectTree buildPrimValueMethodAccess(Tree expr) {
TypeMirror boxedType = TreeUtils.typeOf(expr);
TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement();
assert TypesUtils.isBoxedPrimitive(boxedType);
TypeMirror unboxedType = modelTypes.unboxedType(boxedType);
// Find the *Value() method of the boxed type
String primValueName = unboxedType.toString() + "Value";
Symbol.MethodSymbol primValueMethod = null;
for (ExecutableElement method : ElementFilter.methodsIn(elements.getAllMembers(boxedElement))) {
if (method.getSimpleName().contentEquals(primValueName) && method.getParameters().isEmpty()) {
primValueMethod = (Symbol.MethodSymbol) method;
}
}
assert primValueMethod != null
: "@AssumeAssertion(nullness): no *Value method declared for boxed type";
Type.MethodType methodType = (Type.MethodType) primValueMethod.asType();
JCTree.JCFieldAccess primValueAccess =
(JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, primValueMethod);
primValueAccess.setType(methodType);
return primValueAccess;
}
/** Map public AST Tree.Kinds to internal javac JCTree.Tags. */
public JCTree.Tag kindToTag(Tree.Kind kind) {
switch (kind) {
case AND:
return JCTree.Tag.BITAND;
case AND_ASSIGNMENT:
return JCTree.Tag.BITAND_ASG;
case ANNOTATION:
return JCTree.Tag.ANNOTATION;
case ANNOTATION_TYPE:
return JCTree.Tag.TYPE_ANNOTATION;
case ARRAY_ACCESS:
return JCTree.Tag.INDEXED;
case ARRAY_TYPE:
return JCTree.Tag.TYPEARRAY;
case ASSERT:
return JCTree.Tag.ASSERT;
case ASSIGNMENT:
return JCTree.Tag.ASSIGN;
case BITWISE_COMPLEMENT:
return JCTree.Tag.COMPL;
case BLOCK:
return JCTree.Tag.BLOCK;
case BREAK:
return JCTree.Tag.BREAK;
case CASE:
return JCTree.Tag.CASE;
case CATCH:
return JCTree.Tag.CATCH;
case CLASS:
return JCTree.Tag.CLASSDEF;
case CONDITIONAL_AND:
return JCTree.Tag.AND;
case CONDITIONAL_EXPRESSION:
return JCTree.Tag.CONDEXPR;
case CONDITIONAL_OR:
return JCTree.Tag.OR;
case CONTINUE:
return JCTree.Tag.CONTINUE;
case DIVIDE:
return JCTree.Tag.DIV;
case DIVIDE_ASSIGNMENT:
return JCTree.Tag.DIV_ASG;
case DO_WHILE_LOOP:
return JCTree.Tag.DOLOOP;
case ENHANCED_FOR_LOOP:
return JCTree.Tag.FOREACHLOOP;
case EQUAL_TO:
return JCTree.Tag.EQ;
case EXPRESSION_STATEMENT:
return JCTree.Tag.EXEC;
case FOR_LOOP:
return JCTree.Tag.FORLOOP;
case GREATER_THAN:
return JCTree.Tag.GT;
case GREATER_THAN_EQUAL:
return JCTree.Tag.GE;
case IDENTIFIER:
return JCTree.Tag.IDENT;
case IF:
return JCTree.Tag.IF;
case IMPORT:
return JCTree.Tag.IMPORT;
case INSTANCE_OF:
return JCTree.Tag.TYPETEST;
case LABELED_STATEMENT:
return JCTree.Tag.LABELLED;
case LEFT_SHIFT:
return JCTree.Tag.SL;
case LEFT_SHIFT_ASSIGNMENT:
return JCTree.Tag.SL_ASG;
case LESS_THAN:
return JCTree.Tag.LT;
case LESS_THAN_EQUAL:
return JCTree.Tag.LE;
case LOGICAL_COMPLEMENT:
return JCTree.Tag.NOT;
case MEMBER_SELECT:
return JCTree.Tag.SELECT;
case METHOD:
return JCTree.Tag.METHODDEF;
case METHOD_INVOCATION:
return JCTree.Tag.APPLY;
case MINUS:
return JCTree.Tag.MINUS;
case MINUS_ASSIGNMENT:
return JCTree.Tag.MINUS_ASG;
case MODIFIERS:
return JCTree.Tag.MODIFIERS;
case MULTIPLY:
return JCTree.Tag.MUL;
case MULTIPLY_ASSIGNMENT:
return JCTree.Tag.MUL_ASG;
case NEW_ARRAY:
return JCTree.Tag.NEWARRAY;
case NEW_CLASS:
return JCTree.Tag.NEWCLASS;
case NOT_EQUAL_TO:
return JCTree.Tag.NE;
case OR:
return JCTree.Tag.BITOR;
case OR_ASSIGNMENT:
return JCTree.Tag.BITOR_ASG;
case PARENTHESIZED:
return JCTree.Tag.PARENS;
case PLUS:
return JCTree.Tag.PLUS;
case PLUS_ASSIGNMENT:
return JCTree.Tag.PLUS_ASG;
case POSTFIX_DECREMENT:
return JCTree.Tag.POSTDEC;
case POSTFIX_INCREMENT:
return JCTree.Tag.POSTINC;
case PREFIX_DECREMENT:
return JCTree.Tag.PREDEC;
case PREFIX_INCREMENT:
return JCTree.Tag.PREINC;
case REMAINDER:
return JCTree.Tag.MOD;
case REMAINDER_ASSIGNMENT:
return JCTree.Tag.MOD_ASG;
case RETURN:
return JCTree.Tag.RETURN;
case RIGHT_SHIFT:
return JCTree.Tag.SR;
case RIGHT_SHIFT_ASSIGNMENT:
return JCTree.Tag.SR_ASG;
case SWITCH:
return JCTree.Tag.SWITCH;
case SYNCHRONIZED:
return JCTree.Tag.SYNCHRONIZED;
case THROW:
return JCTree.Tag.THROW;
case TRY:
return JCTree.Tag.TRY;
case TYPE_CAST:
return JCTree.Tag.TYPECAST;
case TYPE_PARAMETER:
return JCTree.Tag.TYPEPARAMETER;
case UNARY_MINUS:
return JCTree.Tag.NEG;
case UNARY_PLUS:
return JCTree.Tag.POS;
case UNION_TYPE:
return JCTree.Tag.TYPEUNION;
case UNSIGNED_RIGHT_SHIFT:
return JCTree.Tag.USR;
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
return JCTree.Tag.USR_ASG;
case VARIABLE:
return JCTree.Tag.VARDEF;
case WHILE_LOOP:
return JCTree.Tag.WHILELOOP;
case XOR:
return JCTree.Tag.BITXOR;
case XOR_ASSIGNMENT:
return JCTree.Tag.BITXOR_ASG;
default:
return JCTree.Tag.NO_TAG;
}
}
/**
* Builds an AST Tree to perform a binary operation.
*
* @param type result type of the operation
* @param op AST Tree operator
* @param left the left operand tree
* @param right the right operand tree
* @return a Tree representing "left < right"
*/
public BinaryTree buildBinary(
TypeMirror type, Tree.Kind op, ExpressionTree left, ExpressionTree right) {
JCTree.Tag jcOp = kindToTag(op);
JCTree.JCBinary binary =
maker.Binary(jcOp, (JCTree.JCExpression) left, (JCTree.JCExpression) right);
binary.setType((Type) type);
return binary;
}
/**
* Builds an AST Tree to create a new array with initializers.
*
* @param componentType component type of the new array
* @param elems expression trees of initializers
* @return a NewArrayTree to create a new array with initializers
*/
public NewArrayTree buildNewArray(TypeMirror componentType, List elems) {
List exprs = CollectionsPlume.mapList(JCExpression.class::cast, elems);
JCTree.JCNewArray newArray =
maker.NewArray(
(JCTree.JCExpression) buildClassUse(((Type) componentType).tsym),
com.sun.tools.javac.util.List.nil(),
com.sun.tools.javac.util.List.from(exprs));
newArray.setType(javacTypes.makeArrayType((Type) componentType));
return newArray;
}
}