/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.errorprone.refaster; import com.google.auto.value.AutoValue; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.errorprone.refaster.PlaceholderUnificationVisitor.State; import com.google.errorprone.refaster.UPlaceholderExpression.PlaceholderParamIdent; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.CatchTree; import com.sun.source.tree.CompoundAssignmentTree; import com.sun.source.tree.ConditionalExpressionTree; import com.sun.source.tree.DoWhileLoopTree; import com.sun.source.tree.EnhancedForLoopTree; 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.InstanceOfTree; import com.sun.source.tree.LabeledStatementTree; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.ParenthesizedTree; 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.TreeVisitor; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCInstanceOf; import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCMemberReference; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCSynchronized; import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.Tag; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; import java.util.Collections; import java.util.EnumSet; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Given a tree as input, returns all the ways this placeholder invocation could be matched with * that tree, represented as States, containing the {@code Unifier}, the list of all parameters of * the placeholder method that were unified with subtrees of the given tree, and a * {@code JCExpression} representing the implementation of the placeholder method, with references * to the placeholder parameters replaced with a corresponding {@code PlaceholderParamIdent}. */ @AutoValue abstract class PlaceholderUnificationVisitor extends SimpleTreeVisitor<Choice<? extends State<? extends JCTree>>, State<?>> { /** * Represents the state of a placeholder unification in progress, including the * current unifier state, the parameters of the placeholder method that have been bound, * and a result used to store additional state. */ @AutoValue abstract static class State<R> { static <R> State<R> create( List<UVariableDecl> seenParameters, Unifier unifier, @Nullable R result) { return new AutoValue_PlaceholderUnificationVisitor_State<R>( seenParameters, unifier, result); } public abstract List<UVariableDecl> seenParameters(); public abstract Unifier unifier(); @Nullable public abstract R result(); public <R2> State<R2> withResult(R2 result) { return create(seenParameters(), unifier(), result); } public State<R> fork() { return create(seenParameters(), unifier().fork(), result()); } } public static PlaceholderUnificationVisitor create( TreeMaker maker, Map<UVariableDecl, UExpression> arguments) { return new AutoValue_PlaceholderUnificationVisitor(maker, ImmutableMap.copyOf(arguments)); } abstract TreeMaker maker(); abstract ImmutableMap<UVariableDecl, UExpression> arguments(); /** * Returns all the ways this tree might be unified with the arguments to this placeholder * invocation. That is, if the placeholder invocation looks like * {@code placeholder(arg1, arg2, ...)}, then the {@code Choice} will contain any ways this * tree can be unified with {@code arg1}, {@code arg2}, or the other arguments. */ Choice<State<PlaceholderParamIdent>> tryBindArguments( final ExpressionTree node, final State<?> state) { return Choice.from(arguments().entrySet()) .thenChoose(new Function<Map.Entry<UVariableDecl, UExpression>, Choice<State<PlaceholderParamIdent>>>() { @Override public Choice<State<PlaceholderParamIdent>> apply( final Map.Entry<UVariableDecl, UExpression> entry) { return unifyParam(entry.getKey(), entry.getValue(), node, state.fork()); } }); } private Choice<State<PlaceholderParamIdent>> unifyParam( final UVariableDecl placeholderParam, UExpression placeholderArg, ExpressionTree toUnify, final State<?> state) { return placeholderArg.unify(toUnify, state.unifier()).transform( new Function<Unifier, State<PlaceholderParamIdent>>() { @Override public State<PlaceholderParamIdent> apply(Unifier unifier) { return State.create(state.seenParameters().prepend(placeholderParam), unifier, new PlaceholderParamIdent(placeholderParam, unifier.getContext())); } }); } public Choice<? extends State<? extends JCTree>> unify(@Nullable Tree node, State<?> state) { if (node instanceof ExpressionTree) { return unifyExpression((ExpressionTree) node, state); } else if (node == null) { return Choice.of(state.<JCTree>withResult(null)); } else { return node.accept(this, state); } } public Choice<State<List<JCTree>>> unify( @Nullable Iterable<? extends Tree> nodes, State<?> state) { if (nodes == null) { return Choice.of(state.<List<JCTree>>withResult(null)); } Choice<State<List<JCTree>>> choice = Choice.of(state.withResult(List.<JCTree>nil())); for (final Tree node : nodes) { choice = choice.thenChoose(new Function<State<List<JCTree>>, Choice<State<List<JCTree>>>>() { @Override public Choice<State<List<JCTree>>> apply( final State<List<JCTree>> state) { return unify(node, state).transform(new Function< State<? extends JCTree>, State<List<JCTree>>>() { @Override public State<List<JCTree>> apply( State<? extends JCTree> treeState) { return treeState.withResult(state.result().prepend(treeState.result())); } }); } }); } return choice.transform(new Function<State<List<JCTree>>, State<List<JCTree>>>() { @Override public State<List<JCTree>> apply(State<List<JCTree>> state) { return state.withResult(state.result().reverse()); } }); } static boolean equivalentExprs(Unifier unifier, JCExpression expr1, JCExpression expr2) { try { return Types.instance(unifier.getContext()).isSameType(expr2.type, expr1.type) && expr2.toString().equals(expr1.toString()); } catch (NullPointerException e) { throw new RuntimeException( "Types are: " + expr2 + " " + expr2.type + " and " + expr1 + " " + expr1.type, e); } } /** * Verifies that the given tree does not directly conflict with an already-bound * {@code UFreeIdent} or {@code ULocalVarIdent}. */ static final TreeVisitor<Boolean, Unifier> FORBIDDEN_REFERENCE_VISITOR = new SimpleTreeVisitor<Boolean, Unifier>() { @Override protected Boolean defaultAction(Tree node, Unifier unifier) { if (!(node instanceof JCExpression)) { return false; } JCExpression expr = (JCExpression) node; for (UFreeIdent.Key key : Iterables.filter(unifier.getBindings().keySet(), UFreeIdent.Key.class)) { JCExpression keyBinding = unifier.getBinding(key); if (equivalentExprs(unifier, expr, keyBinding)) { return true; } } return false; } @Override public Boolean visitIdentifier(IdentifierTree node, Unifier unifier) { for (LocalVarBinding localBinding : Iterables.filter(unifier.getBindings().values(), LocalVarBinding.class)) { if (localBinding.getSymbol().equals(ASTHelpers.getSymbol(node))) { return true; } } return defaultAction(node, unifier); } }; /** * Returns all the ways this placeholder invocation might unify with the specified tree: either * by unifying the entire tree with an argument to the placeholder invocation, or by recursing * on the subtrees. */ @SuppressWarnings("unchecked") public Choice<? extends State<? extends JCExpression>> unifyExpression( @Nullable ExpressionTree node, State<?> state) { if (node == null) { return Choice.of(state.<JCExpression>withResult(null)); } Choice<? extends State<? extends JCExpression>> tryBindArguments = tryBindArguments(node, state); if (!node.accept(FORBIDDEN_REFERENCE_VISITOR, state.unifier())) { return tryBindArguments.or((Choice) node.accept(this, state)); } else { return tryBindArguments; } } /** * Returns all the ways this placeholder invocation might unify with the specified list of * trees. */ public Choice<State<List<JCExpression>>> unifyExpressions( @Nullable Iterable<? extends ExpressionTree> nodes, State<?> state) { return unify(nodes, state).transform( new Function<State<List<JCTree>>, State<List<JCExpression>>>() { @Override public State<List<JCExpression>> apply(State<List<JCTree>> state) { return state.withResult(List.convert(JCExpression.class, state.result())); } }); } @SuppressWarnings("unchecked") public Choice<? extends State<? extends JCStatement>> unifyStatement( @Nullable StatementTree node, State<?> state) { return (Choice<? extends State<? extends JCStatement>>) unify(node, state); } public Choice<State<List<JCStatement>>> unifyStatements( @Nullable Iterable<? extends StatementTree> nodes, State<?> state) { return unify(nodes, state).transform( new Function<State<List<JCTree>>, State<List<JCStatement>>>() { @Override public State<List<JCStatement>> apply(State<List<JCTree>> state) { return state.withResult(List.convert(JCStatement.class, state.result())); } }); } @Override protected Choice<State<JCTree>> defaultAction( Tree node, State<?> state) { return Choice.of(state.withResult((JCTree) node)); } /* * All the visit methods look more or less like this one: we recursively visit the first * subnode of the provided tree, and for each unification state of that subnode, we * continue recursively through the next subnode, and when we have recursed through all * subnodes, we rebuild the same type of tree node with each subtree replaced by its copied or * replaced version. * * This looks much less ugly with lambdas and streams, but we can't use those yet. */ @Override public Choice<State<JCArrayAccess>> visitArrayAccess( final ArrayAccessTree node, State<?> state) { return unifyExpression(node.getExpression(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCArrayAccess>>>() { @Override public Choice<State<JCArrayAccess>> apply( final State<? extends JCExpression> expressionState) { return unifyExpression(node.getIndex(), expressionState) .transform(new Function<State<? extends JCExpression>, State<JCArrayAccess>>() { @Override public State<JCArrayAccess> apply( State<? extends JCExpression> indexState) { return indexState.withResult( maker().Indexed(expressionState.result(), indexState.result())); } }); } }); } @Override public Choice<State<JCBinary>> visitBinary( final BinaryTree node, State<?> state) { final Tag tag = ((JCBinary) node).getTag(); return unifyExpression(node.getLeftOperand(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCBinary>>>() { @Override public Choice<State<JCBinary>> apply( final State<? extends JCExpression> leftState) { return unifyExpression(node.getRightOperand(), leftState) .transform(new Function<State<? extends JCExpression>, State<JCBinary>>() { @Override public State<JCBinary> apply( State<? extends JCExpression> rightState) { return rightState.withResult( maker().Binary(tag, leftState.result(), rightState.result())); } }); } }); } @Override public Choice<State<JCMethodInvocation>> visitMethodInvocation( final MethodInvocationTree node, State<?> state) { return unifyExpression(node.getMethodSelect(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCMethodInvocation>>>() { @Override public Choice<State<JCMethodInvocation>> apply( final State<? extends JCExpression> selectState) { return unifyExpressions(node.getArguments(), selectState) .transform(new Function<State<List<JCExpression>>, State<JCMethodInvocation>>() { @Override public State<JCMethodInvocation> apply( State<List<JCExpression>> argsState) { return argsState.withResult( maker().Apply(null, selectState.result(), argsState.result())); } }); } }); } @Override public Choice<State<JCFieldAccess>> visitMemberSelect( final MemberSelectTree node, State<?> state) { return unifyExpression(node.getExpression(), state) .transform(new Function<State<? extends JCExpression>, State<JCFieldAccess>>() { @Override public State<JCFieldAccess> apply( State<? extends JCExpression> exprState) { return exprState.withResult( maker().Select(exprState.result(), (Name) node.getIdentifier())); } }); } @Override public Choice<State<JCParens>> visitParenthesized( ParenthesizedTree node, State<?> state) { return unifyExpression(node.getExpression(), state) .transform(new Function<State<? extends JCExpression>, State<JCParens>>() { @Override public State<JCParens> apply( State<? extends JCExpression> expressionState) { return expressionState.withResult(maker().Parens(expressionState.result())); } }); } private static final Set<Tag> MUTATING_UNARY_TAGS = Collections.unmodifiableSet(EnumSet.of(Tag.PREINC, Tag.PREDEC, Tag.POSTINC, Tag.POSTDEC)); @Override public Choice<State<JCUnary>> visitUnary( UnaryTree node, State<?> state) { final Tag tag = ((JCUnary) node).getTag(); return unifyExpression(node.getExpression(), state) .thenOption(new Function<State<? extends JCExpression>, Optional<State<JCUnary>>>() { @Override public Optional<State<JCUnary>> apply( State<? extends JCExpression> expressionState) { if (MUTATING_UNARY_TAGS.contains(tag) && expressionState.result() instanceof PlaceholderParamIdent) { return Optional.absent(); } return Optional.of( expressionState.withResult(maker().Unary(tag, expressionState.result()))); } }); } @Override public Choice<State<JCTypeCast>> visitTypeCast( final TypeCastTree node, State<?> state) { return unifyExpression(node.getExpression(), state) .transform(new Function<State<? extends JCExpression>, State<JCTypeCast>>() { @Override public State<JCTypeCast> apply( State<? extends JCExpression> expressionState) { return expressionState.withResult( maker().TypeCast((JCTree) node.getType(), expressionState.result())); } }); } @Override public Choice<State<JCInstanceOf>> visitInstanceOf( final InstanceOfTree node, State<?> state) { return unifyExpression(node.getExpression(), state) .transform(new Function<State<? extends JCExpression>, State<JCInstanceOf>>() { @Override public State<JCInstanceOf> apply( State<? extends JCExpression> expressionState) { return expressionState.withResult( maker().TypeTest(expressionState.result(), (JCTree) node.getType())); } }); } @Override public Choice<State<JCNewClass>> visitNewClass( final NewClassTree node, State<?> state) { if (node.getEnclosingExpression() != null || (node.getTypeArguments() != null && !node.getTypeArguments().isEmpty()) || node.getClassBody() != null) { return Choice.none(); } return unifyExpression(node.getIdentifier(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCNewClass>>>() { @Override public Choice<State<JCNewClass>> apply( final State<? extends JCExpression> identifierState) { return unifyExpressions(node.getArguments(), identifierState) .transform(new Function<State<List<JCExpression>>, State<JCNewClass>>() { @Override public State<JCNewClass> apply( State<List<JCExpression>> argsState) { return argsState.withResult(maker().NewClass( null, null, identifierState.result(), argsState.result(), null)); } }); } }); } @Override public Choice<State<JCNewArray>> visitNewArray( final NewArrayTree node, State<?> state) { return unifyExpressions(node.getDimensions(), state) .thenChoose(new Function<State<List<JCExpression>>, Choice<State<JCNewArray>>>() { @Override public Choice<State<JCNewArray>> apply( final State<List<JCExpression>> dimsState) { return unifyExpressions(node.getInitializers(), dimsState) .transform(new Function<State<List<JCExpression>>, State<JCNewArray>>() { @Override public State<JCNewArray> apply( State<List<JCExpression>> initsState) { return initsState.withResult(maker().NewArray( (JCExpression) node.getType(), dimsState.result(), initsState.result())); } }); } }); } @Override public Choice<State<JCConditional>> visitConditionalExpression( final ConditionalExpressionTree node, State<?> state) { return unifyExpression(node.getCondition(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCConditional>>>() { @Override public Choice<State<JCConditional>> apply( final State<? extends JCExpression> condState) { return unifyExpression(node.getTrueExpression(), condState) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCConditional>>>() { @Override public Choice<State<JCConditional>> apply( final State<? extends JCExpression> trueState) { return unifyExpression(node.getFalseExpression(), trueState) .transform( new Function<State<? extends JCExpression>, State<JCConditional>>() { @Override public State<JCConditional> apply( State<? extends JCExpression> falseState) { return falseState.withResult(maker().Conditional( condState.result(), trueState.result(), falseState.result())); } }); } }); } }); } @Override public Choice<State<JCAssign>> visitAssignment( final AssignmentTree node, State<?> state) { return unifyExpression(node.getVariable(), state) .thenChoose(new Function<State<? extends JCExpression>, Choice<State<JCAssign>>>() { @Override public Choice<State<JCAssign>> apply( final State<? extends JCExpression> varState) { if (varState.result() instanceof PlaceholderParamIdent) { // forbid assignment to placeholder variables return Choice.none(); } return unifyExpression(node.getExpression(), varState) .transform(new Function<State<? extends JCExpression>, State<JCAssign>>() { @Override public State<JCAssign> apply( State<? extends JCExpression> exprState) { return exprState.withResult( maker().Assign(varState.result(), exprState.result())); } }); } }); } @Override public Choice<State<JCAssignOp>> visitCompoundAssignment( final CompoundAssignmentTree node, State<?> state) { return unifyExpression(node.getVariable(), state).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCAssignOp>>>() { @Override public Choice<State<JCAssignOp>> apply(final State<? extends JCExpression> varState) { if (varState.result() instanceof PlaceholderParamIdent) { // forbid assignment to placeholder variables return Choice.none(); } return unifyExpression(node.getExpression(), varState).transform( new Function<State<? extends JCExpression>, State<JCAssignOp>>() { @Override public State<JCAssignOp> apply(State<? extends JCExpression> exprState) { return exprState.withResult(maker().Assignop(((JCAssignOp) node).getTag(), varState.result(), exprState.result())); } }); } }); } @Override public Choice<State<JCExpressionStatement>> visitExpressionStatement( ExpressionStatementTree node, State<?> state) { return unifyExpression(node.getExpression(), state).transform(new Function< State<? extends JCExpression>, State<JCExpressionStatement>>() { @Override public State<JCExpressionStatement> apply( State<? extends JCExpression> exprState) { return exprState.withResult(maker().Exec(exprState.result())); } }); } @Override public Choice<State<JCBlock>> visitBlock(BlockTree node, State<?> state) { return unifyStatements(node.getStatements(), state).transform(new Function< State<List<JCStatement>>, State<JCBlock>>() { @Override public State<JCBlock> apply( State<List<JCStatement>> state) { return state.withResult(maker().Block(0, state.result())); } }); } @Override public Choice<State<JCThrow>> visitThrow(ThrowTree node, State<?> state) { return unifyExpression(node.getExpression(), state).transform(new Function< State<? extends JCExpression>, State<JCThrow>>() { @Override public State<JCThrow> apply( State<? extends JCExpression> exprState) { return exprState.withResult(maker().Throw(exprState.result())); } }); } @Override public Choice<State<JCEnhancedForLoop>> visitEnhancedForLoop( final EnhancedForLoopTree node, State<?> state) { return unifyExpression(node.getExpression(), state).thenChoose(new Function< State<? extends JCExpression>, Choice<State<JCEnhancedForLoop>>>() { @Override public Choice<State<JCEnhancedForLoop>> apply( final State<? extends JCExpression> exprState) { return unifyStatement(node.getStatement(), exprState).transform(new Function< State<? extends JCStatement>, State<JCEnhancedForLoop>>() { @Override public State<JCEnhancedForLoop> apply( State<? extends JCStatement> stmtState) { return stmtState.withResult( maker().ForeachLoop( (JCVariableDecl) node.getVariable(), exprState.result(), stmtState.result())); } }); } }); } @Override public Choice<State<JCIf>> visitIf(final IfTree node, State<?> state) { return unifyExpression(node.getCondition(), state).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCIf>>>() { @Override public Choice<State<JCIf>> apply(final State<? extends JCExpression> condState) { return unifyStatement(node.getThenStatement(), condState).thenChoose( new Function<State<? extends JCStatement>, Choice<State<JCIf>>>() { @Override public Choice<State<JCIf>> apply(final State<? extends JCStatement> thenState) { return unifyStatement(node.getElseStatement(), thenState).transform( new Function<State<? extends JCStatement>, State<JCIf>>() { @Override public State<JCIf> apply(State<? extends JCStatement> elseState) { return elseState.withResult(maker().If(condState.result(), thenState.result(), elseState.result())); } }); } }); } }); } @Override public Choice<State<JCDoWhileLoop>> visitDoWhileLoop(final DoWhileLoopTree node, State<?> state) { return unifyStatement(node.getStatement(), state).thenChoose( new Function<State<? extends JCStatement>, Choice<State<JCDoWhileLoop>>>() { @Override public Choice<State<JCDoWhileLoop>> apply( final State<? extends JCStatement> stmtState) { return unifyExpression(node.getCondition(), stmtState).transform( new Function<State<? extends JCExpression>, State<JCDoWhileLoop>>() { @Override public State<JCDoWhileLoop> apply(State<? extends JCExpression> condState) { return condState.withResult( maker().DoLoop(stmtState.result(), condState.result())); } }); } }); } @Override public Choice<State<JCForLoop>> visitForLoop(final ForLoopTree node, State<?> state) { return unifyStatements(node.getInitializer(), state).thenChoose( new Function<State<List<JCStatement>>, Choice<State<JCForLoop>>>() { @Override public Choice<State<JCForLoop>> apply(final State<List<JCStatement>> initsState) { return unifyExpression(node.getCondition(), initsState).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCForLoop>>>() { @Override public Choice<State<JCForLoop>> apply( final State<? extends JCExpression> condState) { return unifyStatements(node.getUpdate(), condState).thenChoose( new Function<State<List<JCStatement>>, Choice<State<JCForLoop>>>() { @Override public Choice<State<JCForLoop>> apply( final State<List<JCStatement>> updateState) { return unifyStatement(node.getStatement(), updateState).transform( new Function<State<? extends JCStatement>, State<JCForLoop>>() { @Override public State<JCForLoop> apply( State<? extends JCStatement> stmtState) { return stmtState.withResult(maker().ForLoop(initsState.result(), condState.result(), List.convert( JCExpressionStatement.class, updateState.result()), stmtState.result())); } }); } }); } }); } }); } @Override public Choice<State<JCLabeledStatement>> visitLabeledStatement( final LabeledStatementTree node, State<?> state) { return unifyStatement(node.getStatement(), state).transform( new Function<State<? extends JCStatement>, State<JCLabeledStatement>>() { @Override public State<JCLabeledStatement> apply(State<? extends JCStatement> stmtState) { return stmtState.withResult( maker().Labelled((Name) node.getLabel(), stmtState.result())); } }); } @Override public Choice<State<JCVariableDecl>> visitVariable(final VariableTree node, State<?> state) { return unifyExpression(node.getInitializer(), state).transform( new Function<State<? extends JCExpression>, State<JCVariableDecl>>() { @Override public State<JCVariableDecl> apply(State<? extends JCExpression> initState) { return initState.withResult(maker().VarDef((JCModifiers) node.getModifiers(), (Name) node.getName(), (JCExpression) node.getType(), initState.result())); } }); } @Override public Choice<State<JCWhileLoop>> visitWhileLoop(final WhileLoopTree node, State<?> state) { return unifyExpression(node.getCondition(), state).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCWhileLoop>>>() { @Override public Choice<State<JCWhileLoop>> apply(final State<? extends JCExpression> condState) { return unifyStatement(node.getStatement(), condState).transform( new Function<State<? extends JCStatement>, State<JCWhileLoop>>() { @Override public State<JCWhileLoop> apply(State<? extends JCStatement> stmtState) { return stmtState.withResult( maker().WhileLoop(condState.result(), stmtState.result())); } }); } }); } @Override public Choice<State<JCSynchronized>> visitSynchronized(final SynchronizedTree node, State<?> state) { return unifyExpression(node.getExpression(), state).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCSynchronized>>>() { @Override public Choice<State<JCSynchronized>> apply( final State<? extends JCExpression> exprState) { return unifyStatement(node.getBlock(), exprState).transform( new Function<State<? extends JCStatement>, State<JCSynchronized>>() { @Override public State<JCSynchronized> apply(State<? extends JCStatement> blockState) { return blockState.withResult( maker().Synchronized(exprState.result(), (JCBlock) blockState.result())); } }); } }); } @Override public Choice<State<JCReturn>> visitReturn(ReturnTree node, State<?> state) { return unifyExpression(node.getExpression(), state).transform( new Function<State<? extends JCExpression>, State<JCReturn>>() { @Override public State<JCReturn> apply(State<? extends JCExpression> exprState) { return exprState.withResult(maker().Return(exprState.result())); } }); } @Override public Choice<State<JCTry>> visitTry(final TryTree node, State<?> state) { return unify(node.getResources(), state).thenChoose( new Function<State<List<JCTree>>, Choice<State<JCTry>>>() { @Override public Choice<State<JCTry>> apply(final State<List<JCTree>> resourcesState) { return unifyStatement(node.getBlock(), resourcesState).thenChoose( new Function<State<? extends JCStatement>, Choice<State<JCTry>>>() { @Override public Choice<State<JCTry>> apply( final State<? extends JCStatement> blockState) { return unify(node.getCatches(), blockState).thenChoose( new Function<State<List<JCTree>>, Choice<State<JCTry>>>() { @Override public Choice<State<JCTry>> apply( final State<List<JCTree>> catchesState) { return unifyStatement(node.getFinallyBlock(), catchesState).transform( new Function<State<? extends JCStatement>, State<JCTry>>() { @Override public State<JCTry> apply( State<? extends JCStatement> finallyState) { return finallyState.withResult(maker().Try( resourcesState.result(), (JCBlock) blockState.result(), List.convert(JCCatch.class, catchesState.result()), (JCBlock) finallyState.result())); } }); } }); } }); } }); } @Override public Choice<State<JCCatch>> visitCatch(final CatchTree node, State<?> state) { return unifyStatement(node.getBlock(), state).transform( new Function<State<? extends JCStatement>, State<JCCatch>>() { @Override public State<JCCatch> apply(State<? extends JCStatement> blockState) { return blockState.withResult(maker().Catch( (JCVariableDecl) node.getParameter(), (JCBlock) blockState.result())); } }); } @Override public Choice<State<JCSwitch>> visitSwitch(final SwitchTree node, State<?> state) { return unifyExpression(node.getExpression(), state).thenChoose( new Function<State<? extends JCExpression>, Choice<State<JCSwitch>>>() { @Override public Choice<State<JCSwitch>> apply(final State<? extends JCExpression> exprState) { return unify(node.getCases(), exprState).transform( new Function<State<List<JCTree>>, State<JCSwitch>>() { @Override public State<JCSwitch> apply(State<List<JCTree>> casesState) { return casesState.withResult(maker().Switch(exprState.result(), List.convert(JCCase.class, casesState.result()))); } }); } }); } @Override public Choice<State<JCCase>> visitCase(final CaseTree node, State<?> state) { return unifyStatements(node.getStatements(), state).transform( new Function<State<List<JCStatement>>, State<JCCase>>() { @Override public State<JCCase> apply(State<List<JCStatement>> stmtsState) { return stmtsState.withResult( maker().Case((JCExpression) node.getExpression(), stmtsState.result())); } }); } @Override public Choice<State<JCLambda>> visitLambdaExpression( final LambdaExpressionTree node, State<?> state) { return unify(node.getBody(), state).transform( new Function<State<? extends JCTree>, State<JCLambda>>() { @Override public State<JCLambda> apply(State<? extends JCTree> bodyState) { return bodyState.withResult(maker().Lambda( List.convert(JCVariableDecl.class, (List<? extends VariableTree>) node.getParameters()), bodyState.result())); } }); } @Override public Choice<State<JCMemberReference>> visitMemberReference(final MemberReferenceTree node, State<?> state) { return unifyExpression(node.getQualifierExpression(), state).transform( new Function<State<? extends JCExpression>, State<JCMemberReference>>() { @Override public State<JCMemberReference> apply(State<? extends JCExpression> exprState) { return exprState.withResult(maker().Reference(node.getMode(), (Name) node.getName(), exprState.result(), List.convert(JCExpression.class, (List<? extends ExpressionTree>) node.getTypeArguments()))); } }); } }