use of com.sun.source.tree.BinaryTree in project checker-framework by typetools.
the class InterningVisitor method suppressInsideComparison.
/**
* Pattern matches particular comparisons to avoid common false positives in the {@link
* Comparable#compareTo(Object)} and {@link Object#equals(Object)}.
*
* <p>Specifically, this method tests if: the comparison is a == comparison, it is the test of an
* if statement that's the first statement in the method, and one of the following is true:
*
* <ol>
* <li>the method overrides {@link Comparator#compare}, the "then" branch of the if statement
* returns zero, and the comparison tests equality of the method's two parameters
* <li>the method overrides {@link Object#equals(Object)} and the comparison tests "this"
* against the method's parameter
* <li>the method overrides {@link Comparable#compareTo(Object)}, the "then" branch of the if
* statement returns zero, and the comparison tests "this" against the method's parameter
* </ol>
*
* @param node the comparison to check
* @return true if one of the supported heuristics is matched, false otherwise
*/
// TODO: handle != comparisons too!
// TODO: handle more methods, such as early return from addAll when this == arg
private boolean suppressInsideComparison(final BinaryTree node) {
// Only handle == binary trees
if (node.getKind() != Tree.Kind.EQUAL_TO) {
return false;
}
Tree left = node.getLeftOperand();
Tree right = node.getRightOperand();
// Only valid if we're comparing identifiers.
if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) {
return false;
}
TreePath path = getCurrentPath();
TreePath parentPath = path.getParentPath();
Tree parent = parentPath.getLeaf();
// statement in the method.
if (parent.getKind() == Tree.Kind.RETURN) {
// ensure the return statement is the first statement in the method
if (parentPath.getParentPath().getParentPath().getLeaf().getKind() != Tree.Kind.METHOD) {
return false;
}
// maybe set some variables??
} else if (Heuristics.matchParents(getCurrentPath(), Tree.Kind.IF, Tree.Kind.METHOD)) {
// Ensure the if statement is the first statement in the method
// Retrieve the enclosing if statement tree and method tree
Tree ifStatementTree = null;
MethodTree methodTree = null;
// Set ifStatementTree and methodTree
{
TreePath ppath = parentPath;
Tree tree;
while ((tree = ppath.getLeaf()) != null) {
if (tree.getKind() == Tree.Kind.IF) {
ifStatementTree = tree;
} else if (tree.getKind() == Tree.Kind.METHOD) {
methodTree = (MethodTree) tree;
break;
}
ppath = ppath.getParentPath();
}
}
assert ifStatementTree != null;
assert methodTree != null;
StatementTree firstStmnt = methodTree.getBody().getStatements().get(0);
assert firstStmnt != null;
// comparing AST nodes
@SuppressWarnings("interning:not.interned") boolean notSameNode = firstStmnt != ifStatementTree;
if (notSameNode) {
// The if statement is not the first statement in the method.
return false;
}
} else {
return false;
}
ExecutableElement enclosingMethod = TreeUtils.elementFromDeclaration(methodTree);
assert enclosingMethod != null;
final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);
// Matcher to check for if statement that returns zero
Heuristics.Matcher matcherIfReturnsZero = new Heuristics.Matcher() {
@Override
public Boolean visitIf(IfTree tree, Void p) {
return visit(tree.getThenStatement(), p);
}
@Override
public Boolean visitBlock(BlockTree tree, Void p) {
if (tree.getStatements().isEmpty()) {
return false;
}
return visit(tree.getStatements().get(0), p);
}
@Override
public Boolean visitReturn(ReturnTree tree, Void p) {
ExpressionTree expr = tree.getExpression();
return (expr != null && expr.getKind() == Tree.Kind.INT_LITERAL && ((LiteralTree) expr).getValue().equals(0));
}
};
boolean hasCompareToMethodAnno = atypeFactory.getDeclAnnotation(enclosingMethod, CompareToMethod.class) != null;
boolean hasEqualsMethodAnno = atypeFactory.getDeclAnnotation(enclosingMethod, EqualsMethod.class) != null;
int params = enclosingMethod.getParameters().size();
// "return 0" statement (for the Comparator.compare heuristic).
if (overrides(enclosingMethod, Comparator.class, "compare") || (hasCompareToMethodAnno && params == 2)) {
final boolean returnsZero = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
if (!returnsZero) {
return false;
}
assert params == 2;
Element p1 = enclosingMethod.getParameters().get(0);
Element p2 = enclosingMethod.getParameters().get(1);
return (p1.equals(lhs) && p2.equals(rhs)) || (p1.equals(rhs) && p2.equals(lhs));
} else if (overrides(enclosingMethod, Object.class, "equals") || (hasEqualsMethodAnno && params == 1)) {
assert params == 1;
Element param = enclosingMethod.getParameters().get(0);
Element thisElt = getThis(trees.getScope(getCurrentPath()));
assert thisElt != null;
return (thisElt.equals(lhs) && param.equals(rhs)) || (thisElt.equals(rhs) && param.equals(lhs));
} else if (hasEqualsMethodAnno && params == 2) {
Element p1 = enclosingMethod.getParameters().get(0);
Element p2 = enclosingMethod.getParameters().get(1);
return (p1.equals(lhs) && p2.equals(rhs)) || (p1.equals(rhs) && p2.equals(lhs));
} else if (overrides(enclosingMethod, Comparable.class, "compareTo") || (hasCompareToMethodAnno && params == 1)) {
final boolean returnsZero = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
if (!returnsZero) {
return false;
}
assert params == 1;
Element param = enclosingMethod.getParameters().get(0);
Element thisElt = getThis(trees.getScope(getCurrentPath()));
assert thisElt != null;
return (thisElt.equals(lhs) && param.equals(rhs)) || (thisElt.equals(rhs) && param.equals(lhs));
}
return false;
}
use of com.sun.source.tree.BinaryTree in project checker-framework by typetools.
the class InterningVisitor method suppressEarlyCompareTo.
/**
* Pattern matches to prevent false positives of the form {@code (a == b || a.compareTo(b) == 0)}.
* Returns true iff the given node fits this pattern.
*
* @return true iff the node fits the pattern (a == b || a.compareTo(b) == 0)
*/
private boolean suppressEarlyCompareTo(final BinaryTree node) {
// Only handle == binary trees
if (node.getKind() != Tree.Kind.EQUAL_TO) {
return false;
}
Tree left = TreeUtils.withoutParens(node.getLeftOperand());
Tree right = TreeUtils.withoutParens(node.getRightOperand());
// Only valid if we're comparing identifiers.
if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) {
return false;
}
final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);
// looking for ((a == b || a.compareTo(b) == 0)
Heuristics.Matcher matcherEqOrCompareTo = new Heuristics.Matcher() {
@Override
public Boolean visitBinary(BinaryTree tree, Void p) {
if (tree.getKind() == Tree.Kind.EQUAL_TO) {
// a.compareTo(b) == 0
// looking for a.compareTo(b) or
ExpressionTree leftTree = tree.getLeftOperand();
// b.compareTo(a)
// looking for 0
ExpressionTree rightTree = tree.getRightOperand();
if (rightTree.getKind() != Tree.Kind.INT_LITERAL) {
return false;
}
LiteralTree rightLiteral = (LiteralTree) rightTree;
if (!rightLiteral.getValue().equals(0)) {
return false;
}
return visit(leftTree, p);
} else {
// a == b || a.compareTo(b) == 0
@// AST node comparisons
SuppressWarnings(// AST node comparisons
"interning:assignment") @InternedDistinct ExpressionTree // looking for a==b
leftTree = tree.getLeftOperand();
// looking for a.compareTo(b) == 0
ExpressionTree rightTree = tree.getRightOperand();
// or b.compareTo(a) == 0
if (leftTree != node) {
return false;
}
if (rightTree.getKind() != Tree.Kind.EQUAL_TO) {
return false;
}
return visit(rightTree, p);
}
}
@Override
public Boolean visitMethodInvocation(MethodInvocationTree tree, Void p) {
if (!TreeUtils.isMethodInvocation(tree, comparableCompareTo, checker.getProcessingEnvironment())) {
return false;
}
List<? extends ExpressionTree> args = tree.getArguments();
if (args.size() != 1) {
return false;
}
ExpressionTree arg = args.get(0);
if (arg.getKind() != Tree.Kind.IDENTIFIER) {
return false;
}
Element argElt = TreeUtils.elementFromUse(arg);
ExpressionTree exp = tree.getMethodSelect();
if (exp.getKind() != Tree.Kind.MEMBER_SELECT) {
return false;
}
MemberSelectTree member = (MemberSelectTree) exp;
if (member.getExpression().getKind() != Tree.Kind.IDENTIFIER) {
return false;
}
Element refElt = TreeUtils.elementFromUse(member.getExpression());
if (!((refElt.equals(lhs) && argElt.equals(rhs)) || (refElt.equals(rhs) && argElt.equals(lhs)))) {
return false;
}
return true;
}
};
boolean okay = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.CONDITIONAL_OR, matcherEqOrCompareTo)).match(getCurrentPath());
return okay;
}
use of com.sun.source.tree.BinaryTree in project error-prone by google.
the class ShortCircuitBoolean method matchBinary.
@Override
public Description matchBinary(BinaryTree tree, VisitorState state) {
switch(tree.getKind()) {
case AND:
case OR:
break;
default:
return NO_MATCH;
}
if (!isSameType(getType(tree), state.getSymtab().booleanType, state)) {
return NO_MATCH;
}
Iterator<Tree> stateIterator = state.getPath().getParentPath().iterator();
Tree parent = stateIterator.next();
if (parent instanceof BinaryTree && (parent.getKind() == Kind.AND || parent.getKind() == Kind.OR)) {
return NO_MATCH;
} else {
SuggestedFix.Builder fix = SuggestedFix.builder();
new TreeScannerBinary(state).scan(tree, fix);
return describeMatch(tree, fix.build());
}
}
use of com.sun.source.tree.BinaryTree in project error-prone by google.
the class PreconditionsCheckNotNullPrimitive method describe.
/**
* If the call to Preconditions.checkNotNull is part of an expression (assignment, return, etc.),
* we substitute the argument for the method call. E.g.: {@code bar =
* Preconditions.checkNotNull(foo); ==> bar = foo;}
*
* <p>If the argument to Preconditions.checkNotNull is a comparison using == or != and one of the
* operands is null, we call checkNotNull on the non-null operand. E.g.: {@code checkNotNull(a ==
* null); ==> checkNotNull(a);}
*
* <p>If the argument is a method call or binary tree and its return type is boolean, change it to
* a checkArgument/checkState. E.g.: {@code Preconditions.checkNotNull(foo.hasFoo()) ==>
* Preconditions.checkArgument(foo.hasFoo())}
*
* <p>Otherwise, delete the checkNotNull call. E.g.: {@code Preconditions.checkNotNull(foo); ==>
* [delete the line]}
*/
public Description describe(MethodInvocationTree methodInvocationTree, VisitorState state) {
ExpressionTree arg1 = methodInvocationTree.getArguments().get(0);
Tree parent = state.getPath().getParentPath().getLeaf();
// Assignment, return, etc.
if (parent.getKind() != Kind.EXPRESSION_STATEMENT) {
return describeMatch(arg1, SuggestedFix.replace(methodInvocationTree, arg1.toString()));
}
// Comparison to null
if (arg1.getKind() == Kind.EQUAL_TO || arg1.getKind() == Kind.NOT_EQUAL_TO) {
BinaryTree binaryExpr = (BinaryTree) arg1;
if (binaryExpr.getLeftOperand().getKind() == Kind.NULL_LITERAL) {
return describeMatch(arg1, SuggestedFix.replace(arg1, binaryExpr.getRightOperand().toString()));
}
if (binaryExpr.getRightOperand().getKind() == Kind.NULL_LITERAL) {
return describeMatch(arg1, SuggestedFix.replace(arg1, binaryExpr.getLeftOperand().toString()));
}
}
if ((arg1 instanceof BinaryTree || arg1.getKind() == Kind.METHOD_INVOCATION || arg1.getKind() == Kind.LOGICAL_COMPLEMENT) && ((JCExpression) arg1).type == state.getSymtab().booleanType) {
return describeMatch(arg1, createCheckArgumentOrStateCall(methodInvocationTree, state, arg1));
}
return describeMatch(arg1, SuggestedFix.delete(parent));
}
use of com.sun.source.tree.BinaryTree in project error-prone by google.
the class AbstractReferenceEquality method addFixes.
protected void addFixes(Description.Builder builder, BinaryTree tree, VisitorState state) {
ExpressionTree lhs = tree.getLeftOperand();
ExpressionTree rhs = tree.getRightOperand();
Optional<Fix> fixToReplaceOrStatement = inOrStatementWithEqualsCheck(state, tree);
if (fixToReplaceOrStatement.isPresent()) {
builder.addFix(fixToReplaceOrStatement.get());
return;
}
// Swap the order (e.g. rhs.equals(lhs) if the rhs is a non-null constant, and the lhs is not
if (ASTHelpers.constValue(lhs) == null && ASTHelpers.constValue(rhs) != null) {
ExpressionTree tmp = lhs;
lhs = rhs;
rhs = tmp;
}
String prefix = tree.getKind() == Kind.NOT_EQUAL_TO ? "!" : "";
String lhsSource = state.getSourceForNode(lhs);
String rhsSource = state.getSourceForNode(rhs);
Nullness nullness = getNullness(lhs, state);
// If the lhs is possibly-null, provide both options.
if (nullness != NONNULL) {
if (state.isAndroidCompatible()) {
builder.addFix(SuggestedFix.builder().replace(tree, String.format("%sObjects.equal(%s, %s)", prefix, lhsSource, rhsSource)).addImport("com.google.common.base.Objects").build());
} else {
builder.addFix(SuggestedFix.builder().replace(tree, String.format("%sObjects.equals(%s, %s)", prefix, lhsSource, rhsSource)).addImport("java.util.Objects").build());
}
}
if (nullness != NULL) {
builder.addFix(SuggestedFix.replace(tree, String.format("%s%s.equals(%s)", prefix, lhs instanceof BinaryTree ? String.format("(%s)", lhsSource) : lhsSource, rhsSource)));
}
}
Aggregations