Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2014 The Error Prone Authors.
*
* 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 static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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.google.errorprone.util.RuntimeVersion;
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.EnumSet;
import java.util.Map;
import java.util.function.BiFunction;
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>, 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 {
static State create(
List seenParameters, Unifier unifier, @Nullable R result) {
return new AutoValue_PlaceholderUnificationVisitor_State<>(seenParameters, unifier, result);
}
public abstract List seenParameters();
public abstract Unifier unifier();
@Nullable
public abstract R result();
public State withResult(R2 result) {
return create(seenParameters(), unifier(), result);
}
public State fork() {
return create(seenParameters(), unifier().fork(), result());
}
}
public static PlaceholderUnificationVisitor create(
TreeMaker maker, Map arguments) {
return new AutoValue_PlaceholderUnificationVisitor(maker, ImmutableMap.copyOf(arguments));
}
abstract TreeMaker maker();
abstract ImmutableMap 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> tryBindArguments(ExpressionTree node, State state) {
return Choice.from(arguments().entrySet())
.thenChoose(
(Map.Entry entry) ->
unifyParam(entry.getKey(), entry.getValue(), node, state.fork()));
}
private Choice> unifyParam(
UVariableDecl placeholderParam,
UExpression placeholderArg,
ExpressionTree toUnify,
State state) {
return placeholderArg
.unify(toUnify, state.unifier())
.transform(
(Unifier unifier) ->
State.create(
state.seenParameters().prepend(placeholderParam),
unifier,
new PlaceholderParamIdent(placeholderParam, unifier.getContext())));
}
public Choice> unify(@Nullable Tree node, State state) {
if (node instanceof ExpressionTree) {
return unifyExpression((ExpressionTree) node, state);
} else if (node == null) {
return Choice.of(state.withResult(null));
} else {
return node.accept(this, state);
}
}
public Choice>> unify(
@Nullable Iterable nodes, State state) {
if (nodes == null) {
return Choice.of(state.>withResult(null));
}
Choice>> choice = Choice.of(state.withResult(List.nil()));
for (Tree node : nodes) {
choice =
choice.thenChoose(
(State> s) ->
unify(node, s)
.transform(
treeState ->
treeState.withResult(s.result().prepend(treeState.result()))));
}
return choice.transform(s -> s.withResult(s.result().reverse()));
}
static boolean equivalentExprs(Unifier unifier, JCExpression expr1, JCExpression expr2) {
return expr1.type != null
&& expr2.type != null
&& Types.instance(unifier.getContext()).isSameType(expr2.type, expr1.type)
&& expr2.toString().equals(expr1.toString());
}
/**
* Verifies that the given tree does not directly conflict with an already-bound {@code
* UFreeIdent} or {@code ULocalVarIdent}.
*/
static final TreeVisitor FORBIDDEN_REFERENCE_VISITOR =
new SimpleTreeVisitor() {
@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> unifyExpression(
@Nullable ExpressionTree node, State state) {
if (node == null) {
return Choice.of(state.withResult(null));
}
Choice> 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>> unifyExpressions(
@Nullable Iterable nodes, State state) {
return unify(nodes, state)
.transform(s -> s.withResult(List.convert(JCExpression.class, s.result())));
}
@SuppressWarnings("unchecked")
public Choice> unifyStatement(
@Nullable StatementTree node, State state) {
return (Choice>) unify(node, state);
}
public Choice>> unifyStatements(
@Nullable Iterable nodes, State state) {
return chooseSubtrees(
state, s -> unify(nodes, s), stmts -> List.convert(JCStatement.class, stmts));
}
@Override
protected Choice> defaultAction(Tree node, State state) {
return Choice.of(state.withResult((JCTree) node));
}
/**
* This method, and its overloads, take
*
*
*
an initial state
*
functions that, given one state, return a branch choosing a subtree
*
a function that takes pieces of a tree type and recomposes them
*
*/
private static Choice> chooseSubtrees(
State state,
Function, Choice>> choice1,
Function finalizer) {
return choice1.apply(state).transform(s -> s.withResult(finalizer.apply(s.result())));
}
private static Choice> chooseSubtrees(
State state,
Function, Choice>> choice1,
Function, Choice>> choice2,
BiFunction finalizer) {
return choice1
.apply(state)
.thenChoose(
s1 ->
choice2
.apply(s1)
.transform(s2 -> s2.withResult(finalizer.apply(s1.result(), s2.result()))));
}
@FunctionalInterface
private interface TriFunction {
R apply(T1 t1, T2 t2, T3 t3);
}
private static Choice> chooseSubtrees(
State state,
Function, Choice>> choice1,
Function, Choice>> choice2,
Function, Choice>> choice3,
TriFunction finalizer) {
return choice1
.apply(state)
.thenChoose(
s1 ->
choice2
.apply(s1)
.thenChoose(
s2 ->
choice3
.apply(s2)
.transform(
s3 ->
s3.withResult(
finalizer.apply(
s1.result(), s2.result(), s3.result())))));
}
@FunctionalInterface
private interface QuadFunction {
R apply(T1 t1, T2 t2, T3 t3, T4 t4);
}
private static Choice> chooseSubtrees(
State state,
Function, Choice>> choice1,
Function, Choice>> choice2,
Function, Choice>> choice3,
Function, Choice>> choice4,
QuadFunction finalizer) {
return choice1
.apply(state)
.thenChoose(
s1 ->
choice2
.apply(s1)
.thenChoose(
s2 ->
choice3
.apply(s2)
.thenChoose(
s3 ->
choice4
.apply(s3)
.transform(
s4 ->
s4.withResult(
finalizer.apply(
s1.result(),
s2.result(),
s3.result(),
s4.result()))))));
}
@Override
public Choice> visitArrayAccess(ArrayAccessTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
s -> unifyExpression(node.getIndex(), s),
maker()::Indexed);
}
@Override
public Choice> visitBinary(BinaryTree node, State state) {
Tag tag = ((JCBinary) node).getTag();
return chooseSubtrees(
state,
s -> unifyExpression(node.getLeftOperand(), s),
s -> unifyExpression(node.getRightOperand(), s),
(l, r) -> maker().Binary(tag, l, r));
}
@Override
public Choice> visitMethodInvocation(
MethodInvocationTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getMethodSelect(), s),
s -> unifyExpressions(node.getArguments(), s),
(select, args) -> maker().Apply(null, select, args));
}
@Override
public Choice> visitMemberSelect(MemberSelectTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
expr -> maker().Select(expr, (Name) node.getIdentifier()));
}
@Override
public Choice> visitParenthesized(ParenthesizedTree node, State state) {
return chooseSubtrees(state, s -> unifyExpression(node.getExpression(), s), maker()::Parens);
}
private static final ImmutableSet MUTATING_UNARY_TAGS =
ImmutableSet.copyOf(EnumSet.of(Tag.PREINC, Tag.PREDEC, Tag.POSTINC, Tag.POSTDEC));
@Override
public Choice> visitUnary(UnaryTree node, State state) {
Tag tag = ((JCUnary) node).getTag();
return chooseSubtrees(
state, s -> unifyExpression(node.getExpression(), s), expr -> maker().Unary(tag, expr))
.condition(
s ->
!MUTATING_UNARY_TAGS.contains(tag)
|| !(s.result().getExpression() instanceof PlaceholderParamIdent));
}
@Override
public Choice> visitTypeCast(TypeCastTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
expr -> maker().TypeCast((JCTree) node.getType(), expr));
}
@Override
public Choice> visitInstanceOf(InstanceOfTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
expr -> maker().TypeTest(expr, (JCTree) node.getType()));
}
@Override
public Choice> visitNewClass(NewClassTree node, State state) {
if (node.getEnclosingExpression() != null
|| (node.getTypeArguments() != null && !node.getTypeArguments().isEmpty())
|| node.getClassBody() != null) {
return Choice.none();
}
return chooseSubtrees(
state,
s -> unifyExpression(node.getIdentifier(), s),
s -> unifyExpressions(node.getArguments(), s),
(ident, args) -> maker().NewClass(null, null, ident, args, null));
}
@Override
public Choice> visitNewArray(NewArrayTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpressions(node.getDimensions(), s),
s -> unifyExpressions(node.getInitializers(), s),
(dims, inits) -> maker().NewArray((JCExpression) node.getType(), dims, inits));
}
@Override
public Choice> visitConditionalExpression(
ConditionalExpressionTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getCondition(), s),
s -> unifyExpression(node.getTrueExpression(), s),
s -> unifyExpression(node.getFalseExpression(), s),
maker()::Conditional);
}
@Override
public Choice> visitAssignment(AssignmentTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getVariable(), s),
s -> unifyExpression(node.getExpression(), s),
maker()::Assign)
.condition(s -> !(s.result().getVariable() instanceof PlaceholderParamIdent));
}
@Override
public Choice> visitCompoundAssignment(
CompoundAssignmentTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getVariable(), s),
s -> unifyExpression(node.getExpression(), s),
(variable, expr) -> maker().Assignop(((JCAssignOp) node).getTag(), variable, expr))
.condition(assignOp -> !(assignOp.result().getVariable() instanceof PlaceholderParamIdent));
}
@Override
public Choice> visitExpressionStatement(
ExpressionStatementTree node, State state) {
return chooseSubtrees(state, s -> unifyExpression(node.getExpression(), s), maker()::Exec);
}
@Override
public Choice> visitBlock(BlockTree node, State state) {
return chooseSubtrees(
state, s -> unifyStatements(node.getStatements(), s), stmts -> maker().Block(0, stmts));
}
@Override
public Choice> visitThrow(ThrowTree node, State state) {
return chooseSubtrees(state, s -> unifyExpression(node.getExpression(), s), maker()::Throw);
}
@Override
public Choice> visitEnhancedForLoop(
EnhancedForLoopTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
s -> unifyStatement(node.getStatement(), s),
(expr, stmt) -> maker().ForeachLoop((JCVariableDecl) node.getVariable(), expr, stmt));
}
@Override
public Choice> visitIf(IfTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getCondition(), s),
s -> unifyStatement(node.getThenStatement(), s),
s -> unifyStatement(node.getElseStatement(), s),
maker()::If);
}
@Override
public Choice> visitDoWhileLoop(DoWhileLoopTree node, State state) {
return chooseSubtrees(
state,
s -> unifyStatement(node.getStatement(), s),
s -> unifyExpression(node.getCondition(), s),
maker()::DoLoop);
}
@Override
public Choice> visitForLoop(ForLoopTree node, State state) {
return chooseSubtrees(
state,
s -> unifyStatements(node.getInitializer(), s),
s -> unifyExpression(node.getCondition(), s),
s -> unifyStatements(node.getUpdate(), s),
s -> unifyStatement(node.getStatement(), s),
(inits, cond, update, stmt) ->
maker().ForLoop(inits, cond, List.convert(JCExpressionStatement.class, update), stmt));
}
@Override
public Choice> visitLabeledStatement(
LabeledStatementTree node, State state) {
return chooseSubtrees(
state,
s -> unifyStatement(node.getStatement(), s),
stmt -> maker().Labelled((Name) node.getLabel(), stmt));
}
@Override
public Choice> visitVariable(VariableTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getInitializer(), s),
init ->
maker()
.VarDef(
(JCModifiers) node.getModifiers(),
(Name) node.getName(),
(JCExpression) node.getType(),
init));
}
@Override
public Choice> visitWhileLoop(WhileLoopTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getCondition(), s),
s -> unifyStatement(node.getStatement(), s),
maker()::WhileLoop);
}
@Override
public Choice> visitSynchronized(SynchronizedTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
s -> unifyStatement(node.getBlock(), s),
(expr, block) -> maker().Synchronized(expr, (JCBlock) block));
}
@Override
public Choice> visitReturn(ReturnTree node, State state) {
return chooseSubtrees(state, s -> unifyExpression(node.getExpression(), s), maker()::Return);
}
@Override
public Choice> visitTry(TryTree node, State state) {
return chooseSubtrees(
state,
s -> unify(node.getResources(), s),
s -> unifyStatement(node.getBlock(), s),
s -> unify(node.getCatches(), s),
s -> unifyStatement(node.getFinallyBlock(), s),
(resources, block, catches, finallyBlock) ->
maker()
.Try(
resources,
(JCBlock) block,
List.convert(JCCatch.class, catches),
(JCBlock) finallyBlock));
}
@Override
public Choice> visitCatch(CatchTree node, State state) {
return chooseSubtrees(
state,
s -> unifyStatement(node.getBlock(), s),
block -> maker().Catch((JCVariableDecl) node.getParameter(), (JCBlock) block));
}
@Override
public Choice> visitSwitch(SwitchTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getExpression(), s),
s -> unify(node.getCases(), s),
(expr, cases) -> maker().Switch(expr, List.convert(JCCase.class, cases)));
}
@Override
public Choice> visitCase(CaseTree node, State state) {
return chooseSubtrees(
state, s -> unifyStatements(node.getStatements(), s), stmts -> makeCase(node, stmts));
}
private JCCase makeCase(CaseTree node, List stmts) {
try {
if (RuntimeVersion.isAtLeast12()) {
Enum caseKind = (Enum) CaseTree.class.getMethod("getCaseKind").invoke(node);
checkState(
caseKind.name().contentEquals("STATEMENT"),
"expression switches are not supported yet");
return (JCCase)
TreeMaker.class
.getMethod(
"Case",
Class.forName("com.sun.source.tree.CaseTree.CaseKind"),
List.class,
List.class,
JCTree.class)
.invoke(
maker(),
caseKind,
List.of((JCExpression) node.getExpression()),
stmts,
/* body */ null);
} else {
return (JCCase)
TreeMaker.class
.getMethod("Case", JCExpression.class, List.class)
.invoke(maker(), node.getExpression(), stmts);
}
} catch (ReflectiveOperationException e) {
throw new LinkageError(e.getMessage(), e);
}
}
@Override
public Choice> visitLambdaExpression(LambdaExpressionTree node, State state) {
return chooseSubtrees(
state,
s -> unify(node.getBody(), s),
body ->
maker()
.Lambda(
List.convert(
JCVariableDecl.class, (List) node.getParameters()),
body));
}
@Override
public Choice> visitMemberReference(
MemberReferenceTree node, State state) {
return chooseSubtrees(
state,
s -> unifyExpression(node.getQualifierExpression(), s),
expr ->
maker()
.Reference(
node.getMode(),
(Name) node.getName(),
expr,
List.convert(
JCExpression.class,
(List) node.getTypeArguments())));
}
}