blob: 908c5c729821755bb615f5b7b1f618b1bf148e91 [file] [log] [blame]
package org.checkerframework.framework.ajava;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.AnnotationDeclaration;
import com.github.javaparser.ast.body.AnnotationMemberDeclaration;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.EnumConstantDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.InitializerDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.ReceiverParameter;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.ArrayAccessExpr;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.BinaryExpr;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.ConditionalExpr;
import com.github.javaparser.ast.expr.EnclosedExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.FieldAccessExpr;
import com.github.javaparser.ast.expr.InstanceOfExpr;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.LiteralExpr;
import com.github.javaparser.ast.expr.MarkerAnnotationExpr;
import com.github.javaparser.ast.expr.MemberValuePair;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.MethodReferenceExpr;
import com.github.javaparser.ast.expr.Name;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
import com.github.javaparser.ast.expr.SuperExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.expr.TypeExpr;
import com.github.javaparser.ast.expr.UnaryExpr;
import com.github.javaparser.ast.modules.ModuleDeclaration;
import com.github.javaparser.ast.modules.ModuleExportsDirective;
import com.github.javaparser.ast.modules.ModuleOpensDirective;
import com.github.javaparser.ast.modules.ModuleProvidesDirective;
import com.github.javaparser.ast.modules.ModuleRequiresDirective;
import com.github.javaparser.ast.modules.ModuleUsesDirective;
import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations;
import com.github.javaparser.ast.stmt.AssertStmt;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.BreakStmt;
import com.github.javaparser.ast.stmt.CatchClause;
import com.github.javaparser.ast.stmt.ContinueStmt;
import com.github.javaparser.ast.stmt.DoStmt;
import com.github.javaparser.ast.stmt.EmptyStmt;
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ForEachStmt;
import com.github.javaparser.ast.stmt.ForStmt;
import com.github.javaparser.ast.stmt.IfStmt;
import com.github.javaparser.ast.stmt.LabeledStmt;
import com.github.javaparser.ast.stmt.LocalClassDeclarationStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.stmt.SwitchEntry;
import com.github.javaparser.ast.stmt.SwitchStmt;
import com.github.javaparser.ast.stmt.SynchronizedStmt;
import com.github.javaparser.ast.stmt.ThrowStmt;
import com.github.javaparser.ast.stmt.TryStmt;
import com.github.javaparser.ast.stmt.WhileStmt;
import com.github.javaparser.ast.type.ArrayType;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.IntersectionType;
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.ast.type.UnionType;
import com.github.javaparser.ast.type.VoidType;
import com.github.javaparser.ast.type.WildcardType;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.sun.source.tree.AnnotatedTypeTree;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ArrayTypeTree;
import com.sun.source.tree.AssertTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.BreakTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.ContinueTree;
import com.sun.source.tree.DoWhileLoopTree;
import com.sun.source.tree.EmptyStatementTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ErroneousTree;
import com.sun.source.tree.ExportsTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.IntersectionTypeTree;
import com.sun.source.tree.LabeledStatementTree;
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.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.ModuleTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.OpensTree;
import com.sun.source.tree.PackageTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.ProvidesTree;
import com.sun.source.tree.RequiresTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.SynchronizedTree;
import com.sun.source.tree.ThrowTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.TreeVisitor;
import com.sun.source.tree.TryTree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.UnionTypeTree;
import com.sun.source.tree.UsesTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.tree.WildcardTree;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.checkerframework.javacutil.BugInCF;
/**
* A visitor that processes javac trees and JavaParser nodes simultaneously, matching corresponding
* nodes.
*
* <p>By default, visits all children of a javac tree along with corresponding JavaParser nodes. The
* JavaParser node corresponding to a javac tree is always passed as the secondary parameter to the
* {@code visit} methods.
*
* <p>To perform an action on a particular tree type, override one of the methods starting with
* "process". For each javac tree type JavacType, and for each possible JavaParser node type
* JavaParserNode that it may be matched to, this class contains a method {@code
* processJavacType(JavacTypeTree javacTree, JavaParserNode javaParserNode)}. These are named after
* the visit methods in {@code com.sun.source.tree.TreeVisitor}, but for each javac tree type there
* may be multiple process methods for each possible node type it could be matched to.
*
* <p>The {@code process} methods are called in pre-order. That is, process methods for a parent are
* called before its children.
*/
public abstract class JointJavacJavaParserVisitor implements TreeVisitor<Void, Node> {
@Override
public Void visitAnnotation(AnnotationTree javacTree, Node javaParserNode) {
// javac stores annotation arguments as assignments, so @MyAnno("myArg") is stored the same
// as @MyAnno(value="myArg") which has a single element argument list with an assignment.
if (javaParserNode instanceof MarkerAnnotationExpr) {
processAnnotation(javacTree, (MarkerAnnotationExpr) javaParserNode);
} else if (javaParserNode instanceof SingleMemberAnnotationExpr) {
SingleMemberAnnotationExpr node = (SingleMemberAnnotationExpr) javaParserNode;
processAnnotation(javacTree, node);
assert javacTree.getArguments().size() == 1;
ExpressionTree value = javacTree.getArguments().get(0);
assert value instanceof AssignmentTree;
AssignmentTree assignment = (AssignmentTree) value;
assert assignment.getVariable().getKind() == Kind.IDENTIFIER;
assert ((IdentifierTree) assignment.getVariable()).getName().contentEquals("value");
assignment.getExpression().accept(this, node.getMemberValue());
} else if (javaParserNode instanceof NormalAnnotationExpr) {
NormalAnnotationExpr node = (NormalAnnotationExpr) javaParserNode;
processAnnotation(javacTree, node);
assert javacTree.getArguments().size() == node.getPairs().size();
Iterator<MemberValuePair> argIter = node.getPairs().iterator();
for (ExpressionTree arg : javacTree.getArguments()) {
assert arg instanceof AssignmentTree;
AssignmentTree assignment = (AssignmentTree) arg;
IdentifierTree memberName = (IdentifierTree) assignment.getVariable();
MemberValuePair javaParserArg = argIter.next();
assert memberName.getName().contentEquals(javaParserArg.getNameAsString());
assignment.getExpression().accept(this, javaParserArg.getValue());
}
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitAnnotatedType(AnnotatedTypeTree javacTree, Node javaParserNode) {
castNode(NodeWithAnnotations.class, javaParserNode, javacTree);
processAnnotatedType(javacTree, javaParserNode);
javacTree.getUnderlyingType().accept(this, javaParserNode);
return null;
}
@Override
public Void visitArrayAccess(ArrayAccessTree javacTree, Node javaParserNode) {
ArrayAccessExpr node = castNode(ArrayAccessExpr.class, javaParserNode, javacTree);
processArrayAccess(javacTree, node);
javacTree.getExpression().accept(this, node.getName());
javacTree.getIndex().accept(this, node.getIndex());
return null;
}
@Override
public Void visitArrayType(ArrayTypeTree javacTree, Node javaParserNode) {
ArrayType node = castNode(ArrayType.class, javaParserNode, javacTree);
processArrayType(javacTree, node);
javacTree.getType().accept(this, node.getComponentType());
return null;
}
@Override
public Void visitAssert(AssertTree javacTree, Node javaParserNode) {
AssertStmt node = castNode(AssertStmt.class, javaParserNode, javacTree);
processAssert(javacTree, node);
javacTree.getCondition().accept(this, node.getCheck());
visitOptional(javacTree.getDetail(), node.getMessage());
return null;
}
@Override
public Void visitAssignment(AssignmentTree javacTree, Node javaParserNode) {
AssignExpr node = castNode(AssignExpr.class, javaParserNode, javacTree);
processAssignment(javacTree, node);
javacTree.getVariable().accept(this, node.getTarget());
javacTree.getExpression().accept(this, node.getValue());
return null;
}
@Override
public Void visitBinary(BinaryTree javacTree, Node javaParserNode) {
BinaryExpr node = castNode(BinaryExpr.class, javaParserNode, javacTree);
processBinary(javacTree, node);
javacTree.getLeftOperand().accept(this, node.getLeft());
javacTree.getRightOperand().accept(this, node.getRight());
return null;
}
@Override
public Void visitBlock(BlockTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof InitializerDeclaration) {
return javacTree.accept(this, ((InitializerDeclaration) javaParserNode).getBody());
}
BlockStmt node = castNode(BlockStmt.class, javaParserNode, javacTree);
processBlock(javacTree, node);
processStatements(javacTree.getStatements(), node.getStatements());
return null;
}
/**
* Given a matching sequence of statements for a block, visits each javac statement with its
* corresponding JavaParser statement, excluding synthetic javac trees like no-argument
* constructors.
*
* @param javacStatements sequence of javac trees for statements
* @param javaParserStatements sequence of JavaParser statements representing the same block as
* {@code javacStatements}
*/
private void processStatements(
Iterable<? extends StatementTree> javacStatements, Iterable<Statement> javaParserStatements) {
PeekingIterator<StatementTree> javacIter =
Iterators.peekingIterator(javacStatements.iterator());
PeekingIterator<Statement> javaParserIter =
Iterators.peekingIterator(javaParserStatements.iterator());
while (javacIter.hasNext() || javaParserIter.hasNext()) {
// Skip synthetic javac super() calls by checking if the JavaParser statement matches.
if (javacIter.hasNext()
&& isDefaultSuperConstructorCall(javacIter.peek())
&& (!javaParserIter.hasNext() || !isDefaultSuperConstructorCall(javaParserIter.peek()))) {
javacIter.next();
continue;
}
// In javac, a line like "int i = 0, j = 0" is expanded as two sibling VariableTree
// instances. In javaParser this is one VariableDeclarationExpr with two nested
// VariableDeclarators. Match the declarators with the VariableTrees.
if (javaParserIter.hasNext()
&& javaParserIter.peek().isExpressionStmt()
&& javaParserIter.peek().asExpressionStmt().getExpression().isVariableDeclarationExpr()) {
for (VariableDeclarator decl :
javaParserIter
.next()
.asExpressionStmt()
.getExpression()
.asVariableDeclarationExpr()
.getVariables()) {
assert javacIter.hasNext();
assert javacIter.peek().getKind() == Kind.VARIABLE;
javacIter.next().accept(this, decl);
}
continue;
}
assert javacIter.hasNext();
assert javaParserIter.hasNext();
javacIter.next().accept(this, javaParserIter.next());
}
assert !javacIter.hasNext();
assert !javaParserIter.hasNext();
}
/**
* Returns whether a javac statement represents a method call {@code super()}.
*
* @param statement the javac statement to check
* @return true if statement is a method invocation named "super" with no arguments, false
* otherwise
*/
public static boolean isDefaultSuperConstructorCall(StatementTree statement) {
if (statement.getKind() != Kind.EXPRESSION_STATEMENT) {
return false;
}
ExpressionStatementTree expressionStatement = (ExpressionStatementTree) statement;
if (expressionStatement.getExpression().getKind() != Kind.METHOD_INVOCATION) {
return false;
}
MethodInvocationTree invocation = (MethodInvocationTree) expressionStatement.getExpression();
if (invocation.getMethodSelect().getKind() != Kind.IDENTIFIER) {
return false;
}
if (!((IdentifierTree) invocation.getMethodSelect()).getName().contentEquals("super")) {
return false;
}
return invocation.getArguments().isEmpty();
}
/**
* Returns whether a JavaParser statement represents a method call {@code super()}.
*
* @param statement the JavaParser statement to check
* @return true if statement is an explicit super constructor invocation with no arguments
*/
private boolean isDefaultSuperConstructorCall(Statement statement) {
if (!statement.isExplicitConstructorInvocationStmt()) {
return false;
}
ExplicitConstructorInvocationStmt invocation = statement.asExplicitConstructorInvocationStmt();
boolean isSuper = !invocation.isThis();
return isSuper && invocation.getArguments().isEmpty();
}
@Override
public Void visitBreak(BreakTree javacTree, Node javaParserNode) {
BreakStmt node = castNode(BreakStmt.class, javaParserNode, javacTree);
processBreak(javacTree, node);
return null;
}
@Override
public Void visitCase(CaseTree javacTree, Node javaParserNode) {
SwitchEntry node = castNode(SwitchEntry.class, javaParserNode, javacTree);
processCase(javacTree, node);
// The expression is null if and only if the case is the default case.
// Java 12 introduced multiple label cases, but expressions should contain at most one
// element for Java 11 and below.
List<Expression> expressions = node.getLabels();
if (javacTree.getExpression() == null) {
assert expressions.isEmpty();
} else {
assert expressions.size() == 1;
javacTree.getExpression().accept(this, expressions.get(0));
}
processStatements(javacTree.getStatements(), node.getStatements());
return null;
}
@Override
public Void visitCatch(CatchTree javacTree, Node javaParserNode) {
CatchClause node = castNode(CatchClause.class, javaParserNode, javacTree);
processCatch(javacTree, node);
javacTree.getParameter().accept(this, node.getParameter());
javacTree.getBlock().accept(this, node.getBody());
return null;
}
@Override
public Void visitClass(ClassTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration node = (ClassOrInterfaceDeclaration) javaParserNode;
processClass(javacTree, node);
visitLists(javacTree.getTypeParameters(), node.getTypeParameters());
if (javacTree.getKind() == Kind.CLASS) {
if (javacTree.getExtendsClause() == null) {
assert node.getExtendedTypes().isEmpty();
} else {
assert node.getExtendedTypes().size() == 1;
javacTree.getExtendsClause().accept(this, node.getExtendedTypes().get(0));
}
visitLists(javacTree.getImplementsClause(), node.getImplementedTypes());
} else if (javacTree.getKind() == Kind.INTERFACE) {
visitLists(javacTree.getImplementsClause(), node.getExtendedTypes());
}
visitClassMembers(javacTree.getMembers(), node.getMembers());
} else if (javaParserNode instanceof AnnotationDeclaration) {
AnnotationDeclaration node = (AnnotationDeclaration) javaParserNode;
processClass(javacTree, node);
visitClassMembers(javacTree.getMembers(), node.getMembers());
} else if (javaParserNode instanceof LocalClassDeclarationStmt) {
javacTree.accept(this, ((LocalClassDeclarationStmt) javaParserNode).getClassDeclaration());
} else if (javaParserNode instanceof EnumDeclaration) {
EnumDeclaration node = (EnumDeclaration) javaParserNode;
processClass(javacTree, node);
visitLists(javacTree.getImplementsClause(), node.getImplementedTypes());
// In an enum declaration, javac stores the enum constants expanded as constant variable
// members, whereas JavaParser stores them as one object. Need to match them.
assert javacTree.getKind() == Kind.ENUM;
List<Tree> javacMembers = new ArrayList<>(javacTree.getMembers());
// Discard a synthetic constructor if it exists. If there are any constants in this
// enum, then they will show up as the first members of the javac tree, except for
// possibly a synthetic constructor.
if (!node.getEntries().isEmpty()) {
while (!javacMembers.isEmpty() && javacMembers.get(0).getKind() != Kind.VARIABLE) {
javacMembers.remove(0);
}
}
for (EnumConstantDeclaration entry : node.getEntries()) {
assert !javacMembers.isEmpty();
javacMembers.get(0).accept(this, entry);
javacMembers.remove(0);
}
visitClassMembers(javacMembers, node.getMembers());
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
/**
* Given a list of class members for javac and JavaParser, visits each javac member with its
* corresponding JavaParser member. Skips synthetic javac members.
*
* @param javacMembers a list of trees forming the members of a javac {@code ClassTree}
* @param javaParserMembers a list of nodes forming the members of a JavaParser {@code
* ClassOrInterfaceDeclaration} or an {@code ObjectCreationExpr} with an anonymous class body
* that corresponds to {@code javacMembers}
*/
private void visitClassMembers(
List<? extends Tree> javacMembers, List<BodyDeclaration<?>> javaParserMembers) {
PeekingIterator<Tree> javacIter = Iterators.peekingIterator(javacMembers.iterator());
PeekingIterator<BodyDeclaration<?>> javaParserIter =
Iterators.peekingIterator(javaParserMembers.iterator());
while (javacIter.hasNext() || javaParserIter.hasNext()) {
// Skip javac's synthetic no-argument constructors.
if (javacIter.hasNext()
&& isNoArgumentConstructor(javacIter.peek())
&& (!javaParserIter.hasNext() || !isNoArgumentConstructor(javaParserIter.peek()))) {
javacIter.next();
continue;
}
// In javac, a line like int i = 0, j = 0 is expanded as two sibling VariableTree
// instances. In JavaParser this is one FieldDeclaration with two nested
// VariableDeclarators. Match the declarators with the VariableTrees.
if (javaParserIter.hasNext() && javaParserIter.peek().isFieldDeclaration()) {
for (VariableDeclarator decl : javaParserIter.next().asFieldDeclaration().getVariables()) {
assert javacIter.hasNext();
assert javacIter.peek().getKind() == Kind.VARIABLE;
javacIter.next().accept(this, decl);
}
continue;
}
assert javacIter.hasNext();
assert javaParserIter.hasNext();
javacIter.next().accept(this, javaParserIter.next());
}
assert !javacIter.hasNext();
assert !javaParserIter.hasNext();
}
/**
* Visits the the members of an anonymous class body.
*
* <p>In normal classes, javac inserts a synthetic no-argument constructor if no constructor is
* explicitly defined, which is skipped when visiting members. Anonymous class bodies may
* introduce constructors that take arguments if the constructor invocation that created them was
* passed arguments. For example, if {@code MyClass} has a constructor taking a single integer
* argument, then writing {@code new MyClass(5) { }} expands to the javac tree
*
* <pre>{@code
* new MyClass(5) {
* (int arg) {
* super(arg);
* }
* }
* }</pre>
*
* <p>This method skips these synthetic constructors.
*
* @param javacBody body of an anonymous class body
* @param javaParserMembers list of members for the anonymous class body of an {@code
* ObjectCreationExpr}
*/
public void visitAnonymousClassBody(
ClassTree javacBody, List<BodyDeclaration<?>> javaParserMembers) {
List<Tree> javacMembers = new ArrayList<>(javacBody.getMembers());
if (!javacMembers.isEmpty()) {
Tree member = javacMembers.get(0);
if (member.getKind() == Kind.METHOD) {
MethodTree methodTree = (MethodTree) member;
if (methodTree.getName().contentEquals("<init>")) {
javacMembers.remove(0);
}
}
}
visitClassMembers(javacMembers, javaParserMembers);
}
/**
* Returns whether {@code member} is a javac constructor declaration that takes no arguments.
*
* @param member the javac tree to check
* @return true if {@code member} is a method declaration with name {@code <init>} that takes no
* arguments
*/
public static boolean isNoArgumentConstructor(Tree member) {
if (member.getKind() != Kind.METHOD) {
return false;
}
MethodTree methodTree = (MethodTree) member;
return methodTree.getName().contentEquals("<init>") && methodTree.getParameters().isEmpty();
}
/**
* Returns whether {@code member} is a JavaParser constructor declaration that takes no arguments.
*
* @param member the JavaParser body declaration to check
* @return true if {@code member} is a constructor declaration that takes no arguments
*/
private boolean isNoArgumentConstructor(BodyDeclaration<?> member) {
return member.isConstructorDeclaration()
&& member.asConstructorDeclaration().getParameters().isEmpty();
}
@Override
public Void visitCompilationUnit(CompilationUnitTree javacTree, Node javaParserNode) {
CompilationUnit node = castNode(CompilationUnit.class, javaParserNode, javacTree);
processCompilationUnit(javacTree, node);
visitOptional(javacTree.getPackage(), node.getPackageDeclaration());
visitLists(javacTree.getImports(), node.getImports());
visitLists(javacTree.getTypeDecls(), node.getTypes());
return null;
}
@Override
public Void visitCompoundAssignment(CompoundAssignmentTree javacTree, Node javaParserNode) {
AssignExpr node = castNode(AssignExpr.class, javaParserNode, javacTree);
processCompoundAssignment(javacTree, node);
javacTree.getVariable().accept(this, node.getTarget());
javacTree.getExpression().accept(this, node.getValue());
return null;
}
@Override
public Void visitConditionalExpression(ConditionalExpressionTree javacTree, Node javaParserNode) {
ConditionalExpr node = castNode(ConditionalExpr.class, javaParserNode, javacTree);
processConditionalExpression(javacTree, node);
javacTree.getCondition().accept(this, node.getCondition());
javacTree.getTrueExpression().accept(this, node.getThenExpr());
javacTree.getFalseExpression().accept(this, node.getElseExpr());
return null;
}
@Override
public Void visitContinue(ContinueTree javacTree, Node javaParserNode) {
ContinueStmt node = castNode(ContinueStmt.class, javaParserNode, javacTree);
processContinue(javacTree, node);
return null;
}
@Override
public Void visitDoWhileLoop(DoWhileLoopTree javacTree, Node javaParserNode) {
DoStmt node = castNode(DoStmt.class, javaParserNode, javacTree);
processDoWhileLoop(javacTree, node);
// In javac the condition is parenthesized but not in JavaParser.
ExpressionTree condition = ((ParenthesizedTree) javacTree.getCondition()).getExpression();
condition.accept(this, node.getCondition());
javacTree.getStatement().accept(this, node.getBody());
return null;
}
@Override
public Void visitEmptyStatement(EmptyStatementTree javacTree, Node javaParserNode) {
EmptyStmt node = castNode(EmptyStmt.class, javaParserNode, javacTree);
processEmptyStatement(javacTree, node);
return null;
}
@Override
public Void visitEnhancedForLoop(EnhancedForLoopTree javacTree, Node javaParserNode) {
ForEachStmt node = castNode(ForEachStmt.class, javaParserNode, javacTree);
processEnhancedForLoop(javacTree, node);
javacTree.getVariable().accept(this, node.getVariableDeclarator());
javacTree.getExpression().accept(this, node.getIterable());
javacTree.getStatement().accept(this, node.getBody());
return null;
}
@Override
public Void visitErroneous(ErroneousTree javacTree, Node javaParserNode) {
// An erroneous tree is a malformed expression, so skip.
return null;
}
@Override
public Void visitExports(ExportsTree javacTree, Node javaParserNode) {
ModuleExportsDirective node = castNode(ModuleExportsDirective.class, javaParserNode, javacTree);
processExports(javacTree, node);
visitLists(javacTree.getModuleNames(), node.getModuleNames());
javacTree.getPackageName().accept(this, node.getName());
return null;
}
@Override
public Void visitExpressionStatement(ExpressionStatementTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof ExpressionStmt) {
ExpressionStmt node = (ExpressionStmt) javaParserNode;
processExpressionStatemen(javacTree, node);
javacTree.getExpression().accept(this, node.getExpression());
} else if (javaParserNode instanceof ExplicitConstructorInvocationStmt) {
// In this case the javac expression will be a MethodTree. Since JavaParser doesn't
// surround explicit constructor invocations in an expression statement, we match
// javaParserNode to the javac expression rather than the javac expression statement.
javacTree.getExpression().accept(this, javaParserNode);
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitForLoop(ForLoopTree javacTree, Node javaParserNode) {
ForStmt node = castNode(ForStmt.class, javaParserNode, javacTree);
processForLoop(javacTree, node);
Iterator<? extends StatementTree> javacInitializers = javacTree.getInitializer().iterator();
for (Expression initializer : node.getInitialization()) {
if (initializer.isVariableDeclarationExpr()) {
for (VariableDeclarator declarator :
initializer.asVariableDeclarationExpr().getVariables()) {
assert javacInitializers.hasNext();
javacInitializers.next().accept(this, declarator);
}
} else if (initializer.isAssignExpr()) {
ExpressionStatementTree statement = (ExpressionStatementTree) javacInitializers.next();
statement.getExpression().accept(this, initializer);
} else {
assert javacInitializers.hasNext();
javacInitializers.next().accept(this, initializer);
}
}
assert !javacInitializers.hasNext();
visitOptional(javacTree.getCondition(), node.getCompare());
// Javac stores a list of expression statements and JavaParser stores a list of statements,
// the javac statements must be unwrapped.
assert javacTree.getUpdate().size() == node.getUpdate().size();
Iterator<Expression> javaParserUpdates = node.getUpdate().iterator();
for (ExpressionStatementTree javacUpdate : javacTree.getUpdate()) {
// Match the inner javac expression with the JavaParser expression.
javacUpdate.getExpression().accept(this, javaParserUpdates.next());
}
javacTree.getStatement().accept(this, node.getBody());
return null;
}
@Override
public Void visitIdentifier(IdentifierTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof ClassOrInterfaceType) {
processIdentifier(javacTree, (ClassOrInterfaceType) javaParserNode);
} else if (javaParserNode instanceof Name) {
processIdentifier(javacTree, (Name) javaParserNode);
} else if (javaParserNode instanceof NameExpr) {
processIdentifier(javacTree, (NameExpr) javaParserNode);
} else if (javaParserNode instanceof SimpleName) {
processIdentifier(javacTree, (SimpleName) javaParserNode);
} else if (javaParserNode instanceof ThisExpr) {
processIdentifier(javacTree, (ThisExpr) javaParserNode);
} else if (javaParserNode instanceof SuperExpr) {
processIdentifier(javacTree, (SuperExpr) javaParserNode);
} else if (javaParserNode instanceof TypeExpr) {
// This occurs in a member reference like MyClass::myMember. The MyClass is wrapped in a
// TypeExpr.
javacTree.accept(this, ((TypeExpr) javaParserNode).getType());
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitIf(IfTree javacTree, Node javaParserNode) {
IfStmt node = castNode(IfStmt.class, javaParserNode, javacTree);
processIf(javacTree, node);
assert javacTree.getCondition().getKind() == Kind.PARENTHESIZED;
ExpressionTree condition = ((ParenthesizedTree) javacTree.getCondition()).getExpression();
condition.accept(this, node.getCondition());
javacTree.getThenStatement().accept(this, node.getThenStmt());
visitOptional(javacTree.getElseStatement(), node.getElseStmt());
return null;
}
@Override
public Void visitImport(ImportTree javacTree, Node javaParserNode) {
ImportDeclaration node = castNode(ImportDeclaration.class, javaParserNode, javacTree);
processImport(javacTree, node);
// In javac trees, a name like "a.*" is stored as a member select, but JavaParser just
// stores "a" and records that the name ends in an asterisk.
if (node.isAsterisk()) {
assert javacTree.getQualifiedIdentifier().getKind() == Kind.MEMBER_SELECT;
MemberSelectTree identifier = (MemberSelectTree) javacTree.getQualifiedIdentifier();
identifier.getExpression().accept(this, node.getName());
} else {
javacTree.getQualifiedIdentifier().accept(this, node.getName());
}
return null;
}
@Override
public Void visitInstanceOf(InstanceOfTree javacTree, Node javaParserNode) {
InstanceOfExpr node = castNode(InstanceOfExpr.class, javaParserNode, javacTree);
processInstanceOf(javacTree, node);
javacTree.getExpression().accept(this, node.getExpression());
javacTree.getType().accept(this, node.getType());
return null;
}
@Override
public Void visitIntersectionType(IntersectionTypeTree javacTree, Node javaParserNode) {
IntersectionType node = castNode(IntersectionType.class, javaParserNode, javacTree);
processIntersectionType(javacTree, node);
visitLists(javacTree.getBounds(), node.getElements());
return null;
}
@Override
public Void visitLabeledStatement(LabeledStatementTree javacTree, Node javaParserNode) {
LabeledStmt node = castNode(LabeledStmt.class, javaParserNode, javacTree);
processLabeledStatement(javacTree, node);
javacTree.getStatement().accept(this, node.getStatement());
return null;
}
@Override
public Void visitLambdaExpression(LambdaExpressionTree javacTree, Node javaParserNode) {
LambdaExpr node = castNode(LambdaExpr.class, javaParserNode, javacTree);
processLambdaExpression(javacTree, node);
visitLists(javacTree.getParameters(), node.getParameters());
switch (javacTree.getBodyKind()) {
case EXPRESSION:
assert node.getBody() instanceof ExpressionStmt;
ExpressionStmt body = (ExpressionStmt) node.getBody();
javacTree.getBody().accept(this, body.getExpression());
break;
case STATEMENT:
javacTree.getBody().accept(this, node.getBody());
break;
}
return null;
}
@Override
public Void visitLiteral(LiteralTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof LiteralExpr) {
processLiteral(javacTree, (LiteralExpr) javaParserNode);
} else if (javaParserNode instanceof UnaryExpr) {
// Occurs for negative literals such as -7.
processLiteral(javacTree, (UnaryExpr) javaParserNode);
} else if (javaParserNode instanceof BinaryExpr) {
// Occurs for expression like "a" + "b" where javac compresses them to "ab" but
// JavaParser doesn't.
processLiteral(javacTree, (BinaryExpr) javaParserNode);
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitMemberReference(MemberReferenceTree javacTree, Node javaParserNode) {
MethodReferenceExpr node = castNode(MethodReferenceExpr.class, javaParserNode, javacTree);
processMemberReference(javacTree, node);
if (node.getScope().isTypeExpr()) {
javacTree.getQualifierExpression().accept(this, node.getScope().asTypeExpr().getType());
} else {
javacTree.getQualifierExpression().accept(this, node.getScope());
}
assert (javacTree.getTypeArguments() != null) == node.getTypeArguments().isPresent();
if (javacTree.getTypeArguments() != null) {
visitLists(javacTree.getTypeArguments(), node.getTypeArguments().get());
}
return null;
}
@Override
public Void visitMemberSelect(MemberSelectTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof FieldAccessExpr) {
FieldAccessExpr node = (FieldAccessExpr) javaParserNode;
processMemberSelect(javacTree, node);
javacTree.getExpression().accept(this, node.getScope());
} else if (javaParserNode instanceof Name) {
Name node = (Name) javaParserNode;
processMemberSelect(javacTree, node);
assert node.getQualifier().isPresent();
javacTree.getExpression().accept(this, node.getQualifier().get());
} else if (javaParserNode instanceof ClassOrInterfaceType) {
ClassOrInterfaceType node = (ClassOrInterfaceType) javaParserNode;
processMemberSelect(javacTree, node);
assert node.getScope().isPresent();
javacTree.getExpression().accept(this, node.getScope().get());
} else if (javaParserNode instanceof ClassExpr) {
ClassExpr node = (ClassExpr) javaParserNode;
processMemberSelect(javacTree, node);
javacTree.getExpression().accept(this, node.getType());
} else if (javaParserNode instanceof ThisExpr) {
ThisExpr node = (ThisExpr) javaParserNode;
processMemberSelect(javacTree, node);
assert node.getTypeName().isPresent();
javacTree.getExpression().accept(this, node.getTypeName().get());
} else if (javaParserNode instanceof SuperExpr) {
SuperExpr node = (SuperExpr) javaParserNode;
processMemberSelect(javacTree, node);
assert node.getTypeName().isPresent();
javacTree.getExpression().accept(this, node.getTypeName().get());
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitMethod(MethodTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof MethodDeclaration) {
visitMethodForMethodDeclaration(javacTree, (MethodDeclaration) javaParserNode);
} else if (javaParserNode instanceof ConstructorDeclaration) {
visitMethodForConstructorDeclaration(javacTree, (ConstructorDeclaration) javaParserNode);
} else if (javaParserNode instanceof AnnotationMemberDeclaration) {
visitMethodForAnnotationMemberDeclaration(
javacTree, (AnnotationMemberDeclaration) javaParserNode);
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
throw new BugInCF("unreachable");
}
return null;
}
/**
* Visits a method declaration in the case where the matched JavaParser node was a {@code
* MethodDeclaration}.
*
* @param javacTree method declaration to visit
* @param javaParserNode corresponding JavaParser method declaration
*/
private void visitMethodForMethodDeclaration(
MethodTree javacTree, MethodDeclaration javaParserNode) {
processMethod(javacTree, javaParserNode);
// TODO: Handle modifiers. In javac this is a ModifiersTree but in JavaParser it's a list of
// modifiers. This is a problem because a ModifiersTree has separate accessors to
// annotations and other modifiers, so the order doesn't match. It might be that for
// JavaParser, the annotations and other modifiers are also accessed separately.
javacTree.getReturnType().accept(this, javaParserNode.getType());
// Unlike other javac constructs, the javac list is non-null even if no type parameters are
// present.
visitLists(javacTree.getTypeParameters(), javaParserNode.getTypeParameters());
// JavaParser sometimes inserts a receiver parameter that is not present in the source code.
// (Example: on an explicitly-written toString for an enum class.)
if (javacTree.getReceiverParameter() != null
&& javaParserNode.getReceiverParameter().isPresent()) {
javacTree.getReceiverParameter().accept(this, javaParserNode.getReceiverParameter().get());
}
visitLists(javacTree.getParameters(), javaParserNode.getParameters());
visitLists(javacTree.getThrows(), javaParserNode.getThrownExceptions());
visitOptional(javacTree.getBody(), javaParserNode.getBody());
}
/**
* Visits a method declaration in the case where the matched JavaParser node was a {@code
* ConstructorDeclaration}.
*
* @param javacTree method declaration to visit
* @param javaParserNode corresponding JavaParser constructor declaration
*/
private void visitMethodForConstructorDeclaration(
MethodTree javacTree, ConstructorDeclaration javaParserNode) {
processMethod(javacTree, javaParserNode);
visitLists(javacTree.getTypeParameters(), javaParserNode.getTypeParameters());
visitOptional(javacTree.getReceiverParameter(), javaParserNode.getReceiverParameter());
visitLists(javacTree.getParameters(), javaParserNode.getParameters());
visitLists(javacTree.getThrows(), javaParserNode.getThrownExceptions());
javacTree.getBody().accept(this, javaParserNode.getBody());
}
/**
* Visits a method declaration in the case where the matched JavaParser node was a {@code
* AnnotationMemberDeclaration}.
*
* @param javacTree method declaration to visit
* @param javaParserNode corresponding JavaParser annotation member declaration
*/
private void visitMethodForAnnotationMemberDeclaration(
MethodTree javacTree, AnnotationMemberDeclaration javaParserNode) {
processMethod(javacTree, javaParserNode);
javacTree.getReturnType().accept(this, javaParserNode.getType());
visitOptional(javacTree.getDefaultValue(), javaParserNode.getDefaultValue());
}
@Override
public Void visitMethodInvocation(MethodInvocationTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof MethodCallExpr) {
MethodCallExpr node = (MethodCallExpr) javaParserNode;
processMethodInvocation(javacTree, node);
// In javac, the type arguments will be empty if no type arguments are specified, but in
// JavaParser the type arguments will have the none Optional value.
assert javacTree.getTypeArguments().isEmpty() != node.getTypeArguments().isPresent();
if (!javacTree.getTypeArguments().isEmpty()) {
visitLists(javacTree.getTypeArguments(), node.getTypeArguments().get());
}
// In JavaParser, the method name itself and receiver are stored as fields of the
// invocation itself, but in javac they might be combined into one MemberSelectTree.
// That member select may also be a single IdentifierTree if no receiver was written.
// This requires one layer of unnesting.
ExpressionTree methodSelect = javacTree.getMethodSelect();
if (methodSelect.getKind() == Kind.IDENTIFIER) {
methodSelect.accept(this, node.getName());
} else if (methodSelect.getKind() == Kind.MEMBER_SELECT) {
MemberSelectTree selection = (MemberSelectTree) methodSelect;
assert node.getScope().isPresent();
selection.getExpression().accept(this, node.getScope().get());
} else {
throw new BugInCF("Unexpected method selection type: %s", methodSelect);
}
visitLists(javacTree.getArguments(), node.getArguments());
} else if (javaParserNode instanceof ExplicitConstructorInvocationStmt) {
ExplicitConstructorInvocationStmt node = (ExplicitConstructorInvocationStmt) javaParserNode;
processMethodInvocation(javacTree, node);
assert javacTree.getTypeArguments().isEmpty() != node.getTypeArguments().isPresent();
if (!javacTree.getTypeArguments().isEmpty()) {
visitLists(javacTree.getTypeArguments(), node.getTypeArguments().get());
}
visitLists(javacTree.getArguments(), node.getArguments());
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitModifiers(ModifiersTree arg0, Node arg1) {
// TODO How to handle this? I don't think there's a corresponding JavaParser class, maybe
// the NodeWithModifiers interface?
return null;
}
@Override
public Void visitModule(ModuleTree javacTree, Node javaParserNode) {
ModuleDeclaration node = castNode(ModuleDeclaration.class, javaParserNode, javacTree);
processModule(javacTree, node);
javacTree.getName().accept(this, node.getName());
return null;
}
@Override
public Void visitNewArray(NewArrayTree javacTree, Node javaParserNode) {
// TODO: Implement this.
//
// Some notes:
// - javacTree.getAnnotations() seems to always return empty, any annotations on the base
// type seem to go on the type itself in javacTree.getType(). The JavaParser version doesn't
// even have a corresponding getAnnotations method.
// - When there are no initializers, both systems use similar representations. The
// dimensions line up.
// - When there is an initializer, they differ greatly for multi-dimensional arrays. Javac
// turns an expression like new int[][]{{1, 2}, {3, 4}} into a single NewArray tree with
// type int[] and two initializer elements {1, 2} and {3, 4}. However, for each of the
// sub-initializers, it creates an implicit NewArray tree with a null component type.
// JavaParser keeps the whole expression as one ArrayCreationExpr with multiple dimensions
// and the initializer stored in special ArrayInitializerExpr type.
return null;
}
@Override
public Void visitNewClass(NewClassTree javacTree, Node javaParserNode) {
ObjectCreationExpr node = castNode(ObjectCreationExpr.class, javaParserNode, javacTree);
processNewClass(javacTree, node);
// When using Java 11 javac, an expression like this.new MyInnerClass() would store "this"
// as the enclosing expression. In Java 8 javac, this would be stored as new
// MyInnerClass(this). So, we only traverse the enclosing expression if present in both.
if (javacTree.getEnclosingExpression() != null && node.getScope().isPresent()) {
javacTree.getEnclosingExpression().accept(this, node.getScope().get());
}
javacTree.getIdentifier().accept(this, node.getType());
if (javacTree.getTypeArguments().isEmpty()) {
assert !node.getTypeArguments().isPresent();
} else {
assert node.getTypeArguments().isPresent();
visitLists(javacTree.getTypeArguments(), node.getTypeArguments().get());
}
// Remove synthetic javac argument. When using Java 11, an expression like this.new
// MyInnerClass() would store "this" as the enclosing expression. In Java 8, this would be
// stored as new MyInnerClass(this). So, for the argument lists to match, we may have to
// remove the first argument.
List<? extends ExpressionTree> javacArgs = new ArrayList<>(javacTree.getArguments());
if (javacArgs.size() > node.getArguments().size()) {
javacArgs.remove(0);
}
visitLists(javacArgs, node.getArguments());
assert (javacTree.getClassBody() != null) == node.getAnonymousClassBody().isPresent();
if (javacTree.getClassBody() != null) {
visitAnonymousClassBody(javacTree.getClassBody(), node.getAnonymousClassBody().get());
}
return null;
}
@Override
public Void visitOpens(OpensTree javacTree, Node javaParserNode) {
ModuleOpensDirective node = castNode(ModuleOpensDirective.class, javaParserNode, javacTree);
processOpens(javacTree, node);
javacTree.getPackageName().accept(this, node.getName());
visitLists(javacTree.getModuleNames(), node.getModuleNames());
return null;
}
@Override
public Void visitOther(Tree javacTree, Node javaParserNode) {
processOther(javacTree, javaParserNode);
return null;
}
@Override
public Void visitPackage(PackageTree javacTree, Node javaParserNode) {
PackageDeclaration node = castNode(PackageDeclaration.class, javaParserNode, javacTree);
processPackage(javacTree, node);
// visitLists(javacTree.getAnnotations(), node.getAnnotations());
javacTree.getPackageName().accept(this, node.getName());
return null;
}
@Override
public Void visitParameterizedType(ParameterizedTypeTree javacTree, Node javaParserNode) {
ClassOrInterfaceType node = castNode(ClassOrInterfaceType.class, javaParserNode, javacTree);
processParameterizedType(javacTree, node);
javacTree.getType().accept(this, node);
// TODO: In a parameterized type, will the first branch ever run?
if (javacTree.getTypeArguments().isEmpty()) {
assert !node.getTypeArguments().isPresent() || node.getTypeArguments().get().isEmpty();
} else {
assert node.getTypeArguments().isPresent();
visitLists(javacTree.getTypeArguments(), node.getTypeArguments().get());
}
return null;
}
@Override
public Void visitParenthesized(ParenthesizedTree javacTree, Node javaParserNode) {
EnclosedExpr node = castNode(EnclosedExpr.class, javaParserNode, javacTree);
processParenthesized(javacTree, node);
javacTree.getExpression().accept(this, node.getInner());
return null;
}
@Override
public Void visitPrimitiveType(PrimitiveTypeTree javacTree, Node javaParserNode) {
if (javaParserNode instanceof PrimitiveType) {
processPrimitiveType(javacTree, (PrimitiveType) javaParserNode);
} else if (javaParserNode instanceof VoidType) {
processPrimitiveType(javacTree, (VoidType) javaParserNode);
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitProvides(ProvidesTree javacTree, Node javaParserNode) {
ModuleProvidesDirective node =
castNode(ModuleProvidesDirective.class, javaParserNode, javacTree);
processProvides(javacTree, node);
javacTree.getServiceName().accept(this, node.getName());
visitLists(javacTree.getImplementationNames(), node.getWith());
return null;
}
@Override
public Void visitRequires(RequiresTree javacTree, Node javaParserNode) {
ModuleRequiresDirective node =
castNode(ModuleRequiresDirective.class, javaParserNode, javacTree);
processRequires(javacTree, node);
javacTree.getModuleName().accept(this, node.getName());
return null;
}
@Override
public Void visitReturn(ReturnTree javacTree, Node javaParserNode) {
ReturnStmt node = castNode(ReturnStmt.class, javaParserNode, javacTree);
processReturn(javacTree, node);
visitOptional(javacTree.getExpression(), node.getExpression());
return null;
}
@Override
public Void visitSwitch(SwitchTree javacTree, Node javaParserNode) {
SwitchStmt node = castNode(SwitchStmt.class, javaParserNode, javacTree);
processSwitch(javacTree, node);
// Switch expressions are always parenthesized in javac but never in JavaParser.
ExpressionTree expression = ((ParenthesizedTree) javacTree.getExpression()).getExpression();
expression.accept(this, node.getSelector());
visitLists(javacTree.getCases(), node.getEntries());
return null;
}
@Override
public Void visitSynchronized(SynchronizedTree javacTree, Node javaParserNode) {
SynchronizedStmt node = castNode(SynchronizedStmt.class, javaParserNode, javacTree);
processSynchronized(javacTree, node);
((ParenthesizedTree) javacTree.getExpression())
.getExpression()
.accept(this, node.getExpression());
javacTree.getBlock().accept(this, node.getBody());
return null;
}
@Override
public Void visitThrow(ThrowTree javacTree, Node javaParserNode) {
ThrowStmt node = castNode(ThrowStmt.class, javaParserNode, javacTree);
processThrow(javacTree, node);
javacTree.getExpression().accept(this, node.getExpression());
return null;
}
@Override
public Void visitTry(TryTree javacTree, Node javaParserNode) {
TryStmt node = castNode(TryStmt.class, javaParserNode, javacTree);
processTry(javacTree, node);
Iterator<? extends Tree> javacResources = javacTree.getResources().iterator();
for (Expression resource : node.getResources()) {
if (resource.isVariableDeclarationExpr()) {
for (VariableDeclarator declarator : resource.asVariableDeclarationExpr().getVariables()) {
assert javacResources.hasNext();
javacResources.next().accept(this, declarator);
}
} else {
assert javacResources.hasNext();
javacResources.next().accept(this, resource);
}
}
javacTree.getBlock().accept(this, node.getTryBlock());
visitLists(javacTree.getCatches(), node.getCatchClauses());
visitOptional(javacTree.getFinallyBlock(), node.getFinallyBlock());
return null;
}
@Override
public Void visitTypeCast(TypeCastTree javacTree, Node javaParserNode) {
CastExpr node = castNode(CastExpr.class, javaParserNode, javacTree);
processTypeCast(javacTree, node);
javacTree.getType().accept(this, node.getType());
javacTree.getExpression().accept(this, node.getExpression());
return null;
}
@Override
public Void visitTypeParameter(TypeParameterTree javacTree, Node javaParserNode) {
TypeParameter node = castNode(TypeParameter.class, javaParserNode, javacTree);
processTypeParameter(javacTree, node);
visitLists(javacTree.getBounds(), node.getTypeBound());
return null;
}
@Override
public Void visitUnary(UnaryTree javacTree, Node javaParserNode) {
UnaryExpr node = castNode(UnaryExpr.class, javaParserNode, javacTree);
processUnary(javacTree, node);
javacTree.getExpression().accept(this, node.getExpression());
return null;
}
@Override
public Void visitUnionType(UnionTypeTree javacTree, Node javaParserNode) {
UnionType node = castNode(UnionType.class, javaParserNode, javacTree);
processUnionType(javacTree, node);
visitLists(javacTree.getTypeAlternatives(), node.getElements());
return null;
}
@Override
public Void visitUses(UsesTree javacTree, Node javaParserNode) {
ModuleUsesDirective node = castNode(ModuleUsesDirective.class, javaParserNode, javacTree);
processUses(javacTree, node);
javacTree.getServiceName().accept(this, node.getName());
return null;
}
@Override
public Void visitVariable(VariableTree javacTree, Node javaParserNode) {
// Javac uses the class VariableTree to represent multiple syntactic concepts such as
// variable declarations, parameters, and fields.
if (javaParserNode instanceof VariableDeclarator) {
// JavaParser uses VariableDeclarator as parts of other declaration types like
// VariableDeclarationExpr when multiple variables may be declared.
VariableDeclarator node = (VariableDeclarator) javaParserNode;
processVariable(javacTree, node);
// Don't process the variable type when it's the Java keyword "var".
if (!node.getType().isVarType()
&& (!node.getType().isClassOrInterfaceType()
|| !node.getType().asClassOrInterfaceType().getName().asString().equals("var"))) {
javacTree.getType().accept(this, node.getType());
}
// The name expression can be null, even when a name exists.
if (javacTree.getNameExpression() != null) {
javacTree.getNameExpression().accept(this, node.getName());
}
visitOptional(javacTree.getInitializer(), node.getInitializer());
} else if (javaParserNode instanceof Parameter) {
Parameter node = (Parameter) javaParserNode;
processVariable(javacTree, node);
if (node.isVarArgs()) {
ArrayTypeTree arrayType;
// A varargs parameter's type will either be an ArrayTypeTree or an
// AnnotatedType depending on whether it has an annotation.
if (javacTree.getType().getKind() == Kind.ARRAY_TYPE) {
arrayType = (ArrayTypeTree) javacTree.getType();
} else {
AnnotatedTypeTree annotatedType = (AnnotatedTypeTree) javacTree.getType();
arrayType = (ArrayTypeTree) annotatedType.getUnderlyingType();
}
arrayType.getType().accept(this, node.getType());
} else {
// Types for lambda parameters without explicit types don't have JavaParser nodes,
// don't process them.
if (!node.getType().isUnknownType()) {
javacTree.getType().accept(this, node.getType());
}
}
// The name expression can be null, even when a name exists.
if (javacTree.getNameExpression() != null) {
javacTree.getNameExpression().accept(this, node.getName());
}
assert javacTree.getInitializer() == null;
} else if (javaParserNode instanceof ReceiverParameter) {
ReceiverParameter node = (ReceiverParameter) javaParserNode;
processVariable(javacTree, node);
javacTree.getType().accept(this, node.getType());
// The name expression can be null, even when a name exists.
if (javacTree.getNameExpression() != null) {
javacTree.getNameExpression().accept(this, node.getName());
}
assert javacTree.getInitializer() == null;
} else if (javaParserNode instanceof EnumConstantDeclaration) {
// In javac, an enum constant is expanded as a variable declaration initialized to a
// constuctor call.
EnumConstantDeclaration node = (EnumConstantDeclaration) javaParserNode;
processVariable(javacTree, node);
if (javacTree.getNameExpression() != null) {
javacTree.getNameExpression().accept(this, node.getName());
}
assert javacTree.getInitializer().getKind() == Kind.NEW_CLASS;
NewClassTree constructor = (NewClassTree) javacTree.getInitializer();
visitLists(constructor.getArguments(), node.getArguments());
if (constructor.getClassBody() != null) {
visitAnonymousClassBody(constructor.getClassBody(), node.getClassBody());
} else {
assert node.getClassBody().isEmpty();
}
} else {
throwUnexpectedNodeType(javacTree, javaParserNode);
}
return null;
}
@Override
public Void visitWhileLoop(WhileLoopTree javacTree, Node javaParserNode) {
WhileStmt node = castNode(WhileStmt.class, javaParserNode, javacTree);
processWhileLoop(javacTree, node);
// While loop conditions are always parenthesized in javac but never in JavaParser.
assert javacTree.getCondition().getKind() == Kind.PARENTHESIZED;
ExpressionTree condition = ((ParenthesizedTree) javacTree.getCondition()).getExpression();
condition.accept(this, node.getCondition());
javacTree.getStatement().accept(this, node.getBody());
return null;
}
@Override
public Void visitWildcard(WildcardTree javacTree, Node javaParserNode) {
WildcardType node = castNode(WildcardType.class, javaParserNode, javacTree);
processWildcard(javacTree, node);
// In javac, whether the bound is an extends or super clause depends on the kind of the tree.
assert (javacTree.getKind() == Kind.EXTENDS_WILDCARD) == node.getExtendedType().isPresent();
assert (javacTree.getKind() == Kind.SUPER_WILDCARD) == node.getSuperType().isPresent();
switch (javacTree.getKind()) {
case UNBOUNDED_WILDCARD:
break;
case EXTENDS_WILDCARD:
javacTree.getBound().accept(this, node.getExtendedType().get());
break;
case SUPER_WILDCARD:
javacTree.getBound().accept(this, node.getSuperType().get());
break;
default:
throw new BugInCF("Unexpected wildcard kind: %s", javacTree);
}
return null;
}
/**
* Process an {@code AnnotationTree} with multiple key-value pairs like {@code @MyAnno(a=5,
* b=10)}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAnnotation(
AnnotationTree javacTree, NormalAnnotationExpr javaParserNode);
/**
* Process an {@code AnnotationTree} with no arguments like {@code @MyAnno}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAnnotation(
AnnotationTree javacTree, MarkerAnnotationExpr javaParserNode);
/**
* Process an {@code AnnotationTree} with a single argument like {@code MyAnno(5)}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAnnotation(
AnnotationTree javacTree, SingleMemberAnnotationExpr javaParserNode);
/**
* Process an {@code AnnotatedTypeTree}.
*
* <p>In javac, a type with an annotation is represented as an {@code AnnotatedTypeTree} with a
* nested tree for the base type whereas in JavaParser the annotations are store directly on the
* node for the base type. As a result, the JavaParser base type node will be processed twice,
* once with the {@code AnnotatedTypeTree} and once with the tree for the base type.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAnnotatedType(AnnotatedTypeTree javacTree, Node javaParserNode);
/**
* Process an {@code ArrayAccessTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processArrayAccess(
ArrayAccessTree javacTree, ArrayAccessExpr javaParserNode);
/**
* Process an {@code ArrayTypeTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processArrayType(ArrayTypeTree javacTree, ArrayType javaParserNode);
/**
* Process an {@code AssertTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAssert(AssertTree javacTree, AssertStmt javaParserNode);
/**
* Process an {@code AssignmentTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processAssignment(AssignmentTree javacTree, AssignExpr javaParserNode);
/**
* Process a {@code BinaryTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processBinary(BinaryTree javacTree, BinaryExpr javaParserNode);
/**
* Process a {@code BlockTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processBlock(BlockTree javacTree, BlockStmt javaParserNode);
/**
* Process a {@code BreakTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processBreak(BreakTree javacTree, BreakStmt javaParserNode);
/**
* Process a {@code CaseTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processCase(CaseTree javacTree, SwitchEntry javaParserNode);
/**
* Process a {@code CatchTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processCatch(CatchTree javacTree, CatchClause javaParserNode);
/**
* Process a {@code ClassTree} representing an annotation declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processClass(ClassTree javacTree, AnnotationDeclaration javaParserNode);
/**
* Process a {@code ClassTree} representing an annotation declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processClass(
ClassTree javacTree, ClassOrInterfaceDeclaration javaParserNode);
/**
* Process a {@code ClassTree} representing an enum declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processClass(ClassTree javacTree, EnumDeclaration javaParserNode);
/**
* Process a {@code CompilationUnitTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processCompilationUnit(
CompilationUnitTree javacTree, CompilationUnit javaParserNode);
/**
* Process a {@code ConditionalExpressionTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processConditionalExpression(
ConditionalExpressionTree javacTree, ConditionalExpr javaParserNode);
/**
* Process a {@code ContinueTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processContinue(ContinueTree javacTree, ContinueStmt javaParserNode);
/**
* Process a {@code DoWhileLoopTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processDoWhileLoop(DoWhileLoopTree javacTree, DoStmt javaParserNode);
/**
* Process an {@code EmptyStatementTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processEmptyStatement(
EmptyStatementTree javacTree, EmptyStmt javaParserNode);
/**
* Process an {@code EnhancedForLoopTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processEnhancedForLoop(
EnhancedForLoopTree javacTree, ForEachStmt javaParserNode);
/**
* Process an {@code ExportsTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processExports(ExportsTree javacTree, ModuleExportsDirective javaParserNode);
/**
* Process an {@code ExpressionStatementTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processExpressionStatemen(
ExpressionStatementTree javacTree, ExpressionStmt javaParserNode);
/**
* Process a {@code ForLoopTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processForLoop(ForLoopTree javacTree, ForStmt javaParserNode);
/**
* Process an {@code IdentifierTree} representing a class or interface type.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(
IdentifierTree javacTree, ClassOrInterfaceType javaParserNode);
/**
* Process an {@code IdentifierTree} representing a name that may contain dots.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(IdentifierTree javacTree, Name javaParserNode);
/**
* Process an {@code IdentifierTree} representing an expression that evaluates to the value of a
* variable.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(IdentifierTree javacTree, NameExpr javaParserNode);
/**
* Process an {@code IdentifierTree} representing a name without dots.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(IdentifierTree javacTree, SimpleName javaParserNode);
/**
* Process an {@code IdentifierTree} representing a {@code super} expression like the {@code
* super} in {@code super.myMethod()} or {@code MyClass.super.myMethod()}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(IdentifierTree javacTree, SuperExpr javaParserNode);
/**
* Process an {@code IdentifierTree} representing a {@code this} expression like the {@code this}
* in {@code MyClass = this}, {@code this.myMethod()}, or {@code MyClass.this.myMethod()}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIdentifier(IdentifierTree javacTree, ThisExpr javaParserNode);
/**
* Process an {@code IfTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIf(IfTree javacTree, IfStmt javaParserNode);
/**
* Process an {@code ImportTree}.
*
* <p>Wildcards are stored differently between the two. In a statement like {@code import a.*;},
* the name is stored as a {@code MemberSelectTree} with {@code a} and {@code *}. In JavaParser
* this is just stored as {@code a} but with a method that returns whether it has a wildcard.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processImport(ImportTree javacTree, ImportDeclaration javaParserNode);
/**
* Process an {@code InstanceOfTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processInstanceOf(InstanceOfTree javacTree, InstanceOfExpr javaParserNode);
/**
* Process an {@code IntersectionType}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processIntersectionType(
IntersectionTypeTree javacTree, IntersectionType javaParserNode);
/**
* Process a {@code LabeledStatement}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processLabeledStatement(
LabeledStatementTree javacTree, LabeledStmt javaParserNode);
/**
* Process a {@code LambdaExpressionTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processLambdaExpression(
LambdaExpressionTree javacTree, LambdaExpr javaParserNode);
/**
* Process a {@code LiteralTree} for a String literal defined using concatenation.
*
* <p>For an expression like {@code "a" + "b"}, javac stores a single String literal {@code "ab"}
* but JavaParser stores it as an operation with two operands.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processLiteral(LiteralTree javacTree, BinaryExpr javaParserNode);
/**
* Process a {@code LiteralTree} for a literal expression prefixed with {@code +} or {@code -}
* like {@code +5} or {@code -2}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processLiteral(LiteralTree javacTree, UnaryExpr javaParserNode);
/**
* Process a {@code LiteralTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processLiteral(LiteralTree javacTree, LiteralExpr javaParserNode);
/**
* Process a {@code MemberReferenceTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberReference(
MemberReferenceTree javacTree, MethodReferenceExpr javaParserNode);
/**
* Process a {@code MemberSelectTree} for a class expression like {@code MyClass.class}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(MemberSelectTree javacTree, ClassExpr javaParserNode);
/**
* Process a {@code MemberSelectTree} for a type with a name containing dots, like {@code
* mypackage.MyClass}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(
MemberSelectTree javacTree, ClassOrInterfaceType javaParserNode);
/**
* Process a {@code MemberSelectTree} for a field access expression like {@code myObj.myField}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(
MemberSelectTree javacTree, FieldAccessExpr javaParserNode);
/**
* Process a {@code MemberSelectTree} for a name that contains dots.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(MemberSelectTree javacTree, Name javaParserNode);
/**
* Process a {@code MemberSelectTree} for a this expression with a class like {@code
* MyClass.this}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(MemberSelectTree javacTree, ThisExpr javaParserNode);
/**
* Process a {@code MemberSelectTree} for a super expression with a class like {@code
* super.MyClass}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMemberSelect(MemberSelectTree javacTree, SuperExpr javaParserNode);
/**
* Process a {@code MethodTree} representing a regular method declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMethod(MethodTree javacTree, MethodDeclaration javaParserNode);
/**
* Process a {@code MethodTree} representing a constructor declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMethod(MethodTree javacTree, ConstructorDeclaration javaParserNode);
/**
* Process a {@code MethodTree} representing a value field for an annotation.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMethod(
MethodTree javacTree, AnnotationMemberDeclaration javaParserNode);
/**
* Process a {@code MethodInvocationTree} representing a constructor invocation.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMethodInvocation(
MethodInvocationTree javacTree, ExplicitConstructorInvocationStmt javaParserNode);
/**
* Process a {@code MethodInvocationTree} representing a regular method invocation.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processMethodInvocation(
MethodInvocationTree javacTree, MethodCallExpr javaParserNode);
/**
* Process a {@code ModuleTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processModule(ModuleTree javacTree, ModuleDeclaration javaParserNode);
/**
* Process a {@code NewClassTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processNewClass(NewClassTree javacTree, ObjectCreationExpr javaParserNode);
/**
* Process an {@code OpensTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processOpens(OpensTree javacTree, ModuleOpensDirective javaParserNode);
/**
* Process a {@code Tree} that isn't an instance of any specific tree class.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processOther(Tree javacTree, Node javaParserNode);
/**
* Process a {@code PackageTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processPackage(PackageTree javacTree, PackageDeclaration javaParserNode);
/**
* Process a {@code ParameterizedTypeTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processParameterizedType(
ParameterizedTypeTree javacTree, ClassOrInterfaceType javaParserNode);
/**
* Process a {@code ParenthesizedTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processParenthesized(
ParenthesizedTree javacTree, EnclosedExpr javaParserNode);
/**
* Process a {@code PrimitiveTypeTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processPrimitiveType(
PrimitiveTypeTree javacTree, PrimitiveType javaParserNode);
/**
* Process a {@code PrimitiveTypeTree} representing a void type.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processPrimitiveType(PrimitiveTypeTree javacTree, VoidType javaParserNode);
/**
* Process a {@code ProvidesTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processProvides(
ProvidesTree javacTree, ModuleProvidesDirective javaParserNode);
/**
* Process a {@code RequiresTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processRequires(
RequiresTree javacTree, ModuleRequiresDirective javaParserNode);
/**
* Process a {@code RetrunTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processReturn(ReturnTree javacTree, ReturnStmt javaParserNode);
/**
* Process a {@code SwitchTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processSwitch(SwitchTree javacTree, SwitchStmt javaParserNode);
/**
* Process a {@code SynchronizedTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processSynchronized(
SynchronizedTree javacTree, SynchronizedStmt javaParserNode);
/**
* Process a {@code ThrowTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processThrow(ThrowTree javacTree, ThrowStmt javaParserNode);
/**
* Process a {@code TryTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processTry(TryTree javacTree, TryStmt javaParserNode);
/**
* Process a {@code TypeCastTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processTypeCast(TypeCastTree javacTree, CastExpr javaParserNode);
/**
* Process a {@code TypeParameterTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processTypeParameter(
TypeParameterTree javacTree, TypeParameter javaParserNode);
/**
* Process a {@code UnaryTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processUnary(UnaryTree javacTree, UnaryExpr javaParserNode);
/**
* Process a {@code UnionTypeTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processUnionType(UnionTypeTree javacTree, UnionType javaParserNode);
/**
* Process a {@code UsesTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processUses(UsesTree javacTree, ModuleUsesDirective javaParserNode);
/**
* Process a {@code VariableTree} representing an enum constant declaration. In an enum like
* {@code enum MyEnum { MY_CONSTANT }}, javac expands {@code MY_CONSTANT} as a constant variable.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processVariable(
VariableTree javacTree, EnumConstantDeclaration javaParserNode);
/**
* Process a {@code VariableTree} representing a parameter to a method or constructor.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processVariable(VariableTree javacTree, Parameter javaParserNode);
/**
* Process a {@code VariableTree} representing the receiver parameter of a method.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processVariable(VariableTree javacTree, ReceiverParameter javaParserNode);
/**
* Process a {@code VariableTree} representing a regular variable declaration.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processVariable(VariableTree javacTree, VariableDeclarator javaParserNode);
/**
* Process a {@code WhileLoopTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processWhileLoop(WhileLoopTree javacTree, WhileStmt javaParserNode);
/**
* Process a {@code WhileLoopTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processWildcard(WildcardTree javacTree, WildcardType javaParserNode);
/**
* Process a {@code CompoundAssignmentTree}.
*
* @param javacTree tree to process
* @param javaParserNode corresponding JavaParser node
*/
public abstract void processCompoundAssignment(
CompoundAssignmentTree javacTree, AssignExpr javaParserNode);
/**
* Given a list of javac trees and a list of JavaParser nodes, where the elements of the lists
* correspond to each other, visit each javac tree along with its corresponding JavaParser node.
*
* <p>The two lists must be of the same length and elements at corresponding positions must match.
*
* @param javacTrees list of trees
* @param javaParserNodes list of corresponding JavaParser nodes
*/
private void visitLists(List<? extends Tree> javacTrees, List<? extends Node> javaParserNodes) {
assert javacTrees.size() == javaParserNodes.size();
Iterator<? extends Node> nodeIter = javaParserNodes.iterator();
for (Tree tree : javacTrees) {
tree.accept(this, nodeIter.next());
}
}
/**
* Visit an optional syntax construct. Whether the javac tree is non-null must match whether the
* JavaParser optional is present.
*
* @param javacTree a javac tree or null
* @param javaParserNode an optional JavaParser node, which might not be present
*/
private void visitOptional(Tree javacTree, Optional<? extends Node> javaParserNode) {
assert javacTree != null == javaParserNode.isPresent()
: String.format("visitOptional(%s, %s)", javacTree, javaParserNode);
if (javacTree != null) {
javacTree.accept(this, javaParserNode.get());
}
}
/**
* Cast {@code javaParserNode} to type {@code type} and return it.
*
* @param <T> the type of {@code type}
* @param type the type to cast to
* @param javaParserNode the object to cast
* @param javacTree the javac tree that corresponds to {@code javaParserNode}; used only for error
* reporting
* @return javaParserNode, casted to {@code type}
*/
public <T> T castNode(Class<T> type, Node javaParserNode, Tree javacTree) {
if (type.isInstance(javaParserNode)) {
return type.cast(javaParserNode);
}
throwUnexpectedNodeType(javacTree, javaParserNode, type);
throw new BugInCF("unreachable");
}
/**
* Given a javac tree and JavaPaser node which were visited but didn't correspond to each other,
* throws an exception indicating that the visiting process failed for those nodes.
*
* @param javacTree a tree that was visited
* @param javaParserNode a node that was visited at the same time as {@code javacTree}, but which
* was not of the correct type for that tree
* @throws BugInCF that indicates the javac trees and JavaParser nodes were desynced during the
* visitng process at {@code javacTree} and {@code javaParserNode}
*/
private void throwUnexpectedNodeType(Tree javacTree, Node javaParserNode) {
throw new BugInCF(
"desynced trees: %s [%s], %s [%s]",
javacTree, javacTree.getClass(), javaParserNode, javaParserNode.getClass());
}
/**
* Given a javac tree and JavaPaser node which were visited but didn't correspond to each other,
* throws an exception indicating that the visiting process failed for those nodes because {@code
* javaParserNode} was expected to be of type {@code expectedType}.
*
* @param javacTree a tree that was visited
* @param javaParserNode a node that was visited at the same time as {@code javacTree}, but which
* was not of the correct type for that tree
* @param expectedType the type {@code javaParserNode} was expected to be based on {@code
* javacTree}
* @throws BugInCF that indicates the javac trees and JavaParser nodes were desynced during the
* visitng process at {@code javacTree} and {@code javaParserNode}
*/
private void throwUnexpectedNodeType(Tree javacTree, Node javaParserNode, Class<?> expectedType) {
throw new BugInCF(
"desynced trees: %s [%s], %s [%s (expected %s)]",
javacTree, javacTree.getClass(), javaParserNode, javaParserNode.getClass(), expectedType);
}
}