private static boolean checkBinaryOp(CompilationInfo ci, TreePath expr, Tree prev) { BinaryTree bt = (BinaryTree)expr.getLeaf(); Tree other = prev == bt.getLeftOperand() ? bt.getRightOperand() : bt.getLeftOperand(); Boolean b = checkTwoArguments(ci, expr, other, prev); if (Boolean.TRUE == b) { return true; } if (b == null) { return false; } TypeMirror tm = ci.getTrees().getTypeMirror(new TreePath(expr, other)); if (tm != null && tm.getKind() == TypeKind.DECLARED) { Element el = ((DeclaredType)tm).asElement(); if (el != null && el.getKind() == ElementKind.CLASS) { return ((TypeElement)el).getQualifiedName().contentEquals("java.lang.String"); // NOI18N } } return false; }
@Override protected void performRewrite(TransformationContext ctx) throws Exception { TreePath p = ctx.getPath(); if (p.getLeaf().getKind() != Tree.Kind.EQUAL_TO && p.getLeaf().getKind() != Tree.Kind.NOT_EQUAL_TO) { // TODO - report ? return; } BinaryTree bt = (BinaryTree)p.getLeaf(); TreeMaker mk = ctx.getWorkingCopy().getTreeMaker(); ExpressionTree replace = mk.MethodInvocation( Collections.<ExpressionTree>emptyList(), mk.MemberSelect( mk.QualIdent(JU_OBJECTS), "equals" // NOI18N ), Arrays.asList(bt.getLeftOperand(), bt.getRightOperand()) ); if (bt.getKind() == Tree.Kind.NOT_EQUAL_TO) { replace = mk.Unary(Tree.Kind.LOGICAL_COMPLEMENT, replace); } ctx.getWorkingCopy().rewrite(bt, replace); }
@Override protected void performRewrite(TransformationContext ctx) throws Exception { TypeMirror resolvedTargetType = targetType.resolve(ctx.getWorkingCopy()); if (resolvedTargetType == null) { //cannot resolve anymore: return; } TreePath resolvedIdealTypeTree = idealTypeTree != null ? idealTypeTree.resolve(ctx.getWorkingCopy()) : null; TreeMaker make = ctx.getWorkingCopy().getTreeMaker(); ExpressionTree toCast = (ExpressionTree) ctx.getPath().getLeaf(); Class interf = toCast.getKind().asInterface(); boolean wrapWithBrackets = interf == BinaryTree.class || interf == ConditionalExpressionTree.class; if (/*TODO: replace with JavaFixUtilities.requiresparenthesis*/wrapWithBrackets) { toCast = make.Parenthesized(toCast); } ExpressionTree cast = make.TypeCast(resolvedIdealTypeTree != null ? resolvedIdealTypeTree.getLeaf() : make.Type(resolvedTargetType), toCast); ctx.getWorkingCopy().rewrite(ctx.getPath().getLeaf(), cast); }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { ExpressionTree leftOperand = tree.getLeftOperand(); ExpressionTree rightOperand = tree.getRightOperand(); Type leftType = ASTHelpers.getType(leftOperand); Type rightType = ASTHelpers.getType(rightOperand); if (leftType == null || rightType == null) { throw new RuntimeException(); } if (leftType.isPrimitive() && !rightType.isPrimitive()) { return doUnboxingCheck(state, rightOperand); } if (rightType.isPrimitive() && !leftType.isPrimitive()) { return doUnboxingCheck(state, leftOperand); } return Description.NO_MATCH; }
/** * Accumulate the operands and operators. */ private static void walkInfix( int precedence, ExpressionTree expression, List<ExpressionTree> operands, List<String> operators) { if (expression instanceof BinaryTree) { BinaryTree binaryTree = (BinaryTree) expression; if (precedence(binaryTree) == precedence) { walkInfix(precedence, binaryTree.getLeftOperand(), operands, operators); operators.add(operatorName(expression)); walkInfix(precedence, binaryTree.getRightOperand(), operands, operators); } else { operands.add(expression); } } else { operands.add(expression); } }
@Override public Void visitBinary(BinaryTree node, Void unused) { sync(node); /* * Collect together all operators with same precedence to clean up indentation. Eclipse's * extended operands help a little (to collect together the same operator), but they're applied * inconsistently, and don't apply to other operators of the same precedence. */ List<ExpressionTree> operands = new ArrayList<>(); List<String> operators = new ArrayList<>(); walkInfix(precedence(node), node, operands, operators); FillMode fillMode = hasOnlyShortItems(operands) ? INDEPENDENT : UNIFIED; builder.open(plusFour); scan(operands.get(0), null); int operatorsN = operators.size(); for (int i = 0; i < operatorsN; i++) { builder.breakOp(fillMode, " ", ZERO); builder.op(operators.get(i)); builder.space(); scan(operands.get(i + 1), null); } builder.close(); return null; }
@Test void testPreferredPositionForBinaryOp() throws IOException { String code = "package test; public class Test {" + "private void test() {" + "Object o = null; boolean b = o != null && o instanceof String;" + "} private Test() {}}"; CompilationUnitTree cut = getCompilationUnitTree(code); ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); MethodTree method = (MethodTree) clazz.getMembers().get(0); VariableTree condSt = (VariableTree) method.getBody().getStatements().get(1); BinaryTree cond = (BinaryTree) condSt.getInitializer(); JCTree condJC = (JCTree) cond; int condStartPos = code.indexOf("&&"); assertEquals("testPreferredPositionForBinaryOp", condStartPos, condJC.pos); }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { if (!(tree.getLeftOperand() instanceof JCLiteral)) { return Description.NO_MATCH; } if (!(tree.getRightOperand() instanceof JCLiteral)) { return Description.NO_MATCH; } Boolean constValue = ASTHelpers.constValue(tree, Boolean.class); if (constValue == null) { return Description.NO_MATCH; } return buildDescription(tree) .addFix(SuggestedFix.replace(tree, constValue.toString())) .setMessage( String.format( "This expression always evalutes to `%s`, prefer a boolean literal for clarity.", constValue)) .build(); }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { TreePath path = state.getPath().getParentPath(); while (path != null && path.getLeaf() instanceof ExpressionTree) { if (path.getLeaf() instanceof BinaryTree) { // only match on the outermost nested binary expression return NO_MATCH; } path = path.getParentPath(); } try { tree.accept(CONSTANT_VISITOR, null); return NO_MATCH; } catch (ArithmeticException e) { Description.Builder description = buildDescription(tree); Fix longFix = longFix(tree, state); if (longFix != null) { description.addFix(longFix); } return description.build(); } }
/** Handles the case "expr1 == expr2" */ private static void suggestFixForSameReference( SuggestedFix.Builder fix, AssertTree foundAssert, VisitorState state, boolean isEqual) { BinaryTree equalityTree = (BinaryTree) TreeInfo.skipParens((JCTree) foundAssert.getCondition()); ExpressionTree expr1 = equalityTree.getLeftOperand(); ExpressionTree expr2 = equalityTree.getRightOperand(); if (expr1.getKind() == NULL_LITERAL) { // case: "assert null [op] expr" addFix(fix, (JCExpression) expr2, foundAssert, state, isEqual ? IS_NULL : IS_NOT_NULL); } else if (expr2.getKind() == NULL_LITERAL) { // case: "assert expr [op] null" addFix(fix, (JCExpression) expr1, foundAssert, state, isEqual ? IS_NULL : IS_NOT_NULL); } else { // case: "assert expr1 [op] expr2" addFix( fix, (JCExpression) expr1, foundAssert, state, String.format(isEqual ? IS_SAME_AS : IS_NOT_SAME_AS, expr2)); } }
/** * Matches if this is a narrowing integral cast between signed types where the expression is a * subtract. */ private boolean matches(TypeCastTree tree, VisitorState state) { Type treeType = ASTHelpers.getType(tree.getType()); // If the cast isn't narrowing to an int then don't implicate it in the bug pattern. if (treeType.getTag() != TypeTag.INT) { return false; } // The expression should be a subtract but remove parentheses. ExpressionTree expression = ASTHelpers.stripParentheses(tree.getExpression()); if (expression.getKind() != Kind.MINUS) { return false; } // Ensure the expression type is wider and signed (ie a long) than the cast type ignoring // boxing. Type expressionType = getTypeOfSubtract((BinaryTree) expression); TypeTag expressionTypeTag = state.getTypes().unboxedTypeOrType(expressionType).getTag(); return (expressionTypeTag == TypeTag.LONG); }
@Override public boolean matches(BinaryTree tree, VisitorState state) { Type leftType = ((JCTree) tree.getLeftOperand()).type; Types types = state.getTypes(); Symtab symtab = state.getSymtab(); if (!(types.isSameType(leftType, symtab.intType)) && !(types.isSameType(leftType, symtab.byteType)) && !(types.isSameType(leftType, symtab.shortType)) && !(types.isSameType(leftType, symtab.charType))) { return false; } ExpressionTree rightOperand = tree.getRightOperand(); if (rightOperand instanceof LiteralTree) { Object rightValue = ((LiteralTree) rightOperand).getValue(); if (rightValue instanceof Number) { int intValue = ((Number) rightValue).intValue(); return intValue < 0 || intValue > 31; } } return false; }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { if (!BINARY_TREE_MATCHER.matches(tree, state)) { return Description.NO_MATCH; } /* * For shift amounts in [32, 63], cast the left operand to long. Otherwise change the shift * amount to whatever would actually be used. */ int intValue = ((Number) ((LiteralTree) tree.getRightOperand()).getValue()).intValue(); Fix fix; if (intValue >= 32 && intValue <= 63) { if (tree.getLeftOperand().getKind() == Kind.INT_LITERAL) { fix = SuggestedFix.postfixWith(tree.getLeftOperand(), "L"); } else { fix = SuggestedFix.prefixWith(tree, "(long) "); } } else { // This is the equivalent shift distance according to JLS 15.19. String actualShiftDistance = Integer.toString(intValue & 0x1f); fix = SuggestedFix.replace(tree.getRightOperand(), actualShiftDistance); } return describeMatch(tree, fix); }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { switch (tree.getKind()) { case EQUAL_TO: case NOT_EQUAL_TO: break; default: return NO_MATCH; } ExpressionTree lhs = tree.getLeftOperand(); ExpressionTree rhs = tree.getRightOperand(); if (match(lhs, rhs, state) || match(rhs, lhs, state)) { String result = String.format("%s.equals(%s)", state.getSourceForNode(lhs), state.getSourceForNode(rhs)); if (tree.getKind() == Kind.NOT_EQUAL_TO) { result = "!" + result; } return describeMatch(tree, SuggestedFix.replace(tree, result)); } return NO_MATCH; }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { if (tree.getKind() == Kind.REMAINDER && tree.getLeftOperand() instanceof MethodInvocationTree && RANDOM_NEXT_INT.matches(tree.getLeftOperand(), state)) { ExpressionTree randomExpr = ASTHelpers.getReceiver(tree.getLeftOperand()); ExpressionTree modulus = tree.getRightOperand(); return describeMatch( tree, SuggestedFix.replace( tree, String.format( "%s.nextInt(%s)", state.getSourceForNode(randomExpr), state.getSourceForNode(modulus)))); } return Description.NO_MATCH; }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { Tree parent = state.getPath().getParentPath().getLeaf(); if (!(parent instanceof BinaryTree)) { return NO_MATCH; } if (TreeInfo.opPrec(((JCBinary) tree).getTag()) == TreeInfo.opPrec(((JCBinary) parent).getTag())) { return NO_MATCH; } if (!isConfusing(tree.getKind(), parent.getKind())) { return NO_MATCH; } return describeMatch( tree, SuggestedFix.builder().prefixWith(tree, "(").postfixWith(tree, ")").build()); }
private static Optional<String> isNanReplacement(BinaryTree tree, VisitorState state) { Types types = state.getTypes(); Symtab symtab = state.getSymtab(); Type type = getType(tree.getLeftOperand()); if (type == null) { return Optional.empty(); } type = types.unboxedTypeOrType(type); String name; if (isSameType(type, symtab.floatType, state)) { name = "Float"; } else if (isSameType(type, symtab.doubleType, state)) { name = "Double"; } else { return Optional.empty(); } return Optional.of( String.format("%s.isNaN(%s)", name, state.getSourceForNode(tree.getLeftOperand()))); }
/** * Matches comparisons to null (e.g. {@code foo == null}) and returns the expression being tested. */ private static ExpressionTree getNullCheckedExpression(ExpressionTree condition) { condition = stripParentheses(condition); if (!(condition instanceof BinaryTree)) { return null; } BinaryTree bin = (BinaryTree) condition; ExpressionTree other; if (bin.getLeftOperand().getKind() == Kind.NULL_LITERAL) { other = bin.getRightOperand(); } else if (bin.getRightOperand().getKind() == Kind.NULL_LITERAL) { other = bin.getLeftOperand(); } else { return null; } return other; }
private Description provideReplacementForMethodInvocation( BinaryTree tree, MethodInvocationTree leftOperand, VisitorState state, ExpressionType expressionType) { ExpressionTree collection = ASTHelpers.getReceiver(leftOperand); if (HAS_EMPTY_METHOD.matches(collection, state)) { return describeMatch( tree, SuggestedFix.replace( tree, "!" + state.getSourceForNode((JCTree) collection) + ".isEmpty()")); } else { return removeEqualsFromComparison(tree, state, expressionType); } }
private ExpressionType isGreaterThanEqualToZero(BinaryTree tree) { ExpressionTree literalOperand; ExpressionType returnType; switch (tree.getKind()) { case GREATER_THAN_EQUAL: literalOperand = tree.getRightOperand(); returnType = ExpressionType.GREATER_THAN_EQUAL; break; case LESS_THAN_EQUAL: literalOperand = tree.getLeftOperand(); returnType = ExpressionType.LESS_THAN_EQUAL; break; default: return ExpressionType.MISMATCH; } if (literalOperand.getKind() != Kind.INT_LITERAL) { return ExpressionType.MISMATCH; } if (!((LiteralTree) literalOperand).getValue().equals(0)) { return ExpressionType.MISMATCH; } return returnType; }
@Override public final Description matchBinary(BinaryTree tree, VisitorState state) { switch (tree.getKind()) { case EQUAL_TO: case NOT_EQUAL_TO: break; default: return Description.NO_MATCH; } if (tree.getLeftOperand().getKind() == Kind.NULL_LITERAL || !matchArgument(tree.getLeftOperand(), state)) { return Description.NO_MATCH; } if (tree.getRightOperand().getKind() == Kind.NULL_LITERAL || !matchArgument(tree.getRightOperand(), state)) { return Description.NO_MATCH; } Description.Builder builder = buildDescription(tree); addFixes(builder, tree, state); return builder.build(); }
@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()); } }
public void testPreferredPositionForBinaryOp() throws IOException { String code = "package test; public class Test {" + "private void test() {" + "Object o = null; boolean b = o != null && o instanceof String;" + "} private Test() {}}"; JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null, null, Arrays.asList(new MyFileObject(code))); CompilationUnitTree cut = ct.parse().iterator().next(); ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0); MethodTree method = (MethodTree) clazz.getMembers().get(0); VariableTree condSt = (VariableTree) method.getBody().getStatements().get(1); BinaryTree cond = (BinaryTree) condSt.getInitializer(); JCTree condJC = (JCTree) cond; assertEquals("testNewClassWithEnclosing", 117 - 24, condJC.pos); }
@Override public boolean matches(BinaryTree tree, VisitorState state) { Type stringType = state.getSymtab().stringType; ExpressionTree leftOperand = tree.getLeftOperand(); Type leftType = ((JCTree.JCExpression) leftOperand).type; // The left operand is not a String (ex. null) so no match if (!state.getTypes().isSameType(leftType, stringType)) { return false; } ExpressionTree rightOperand = tree.getRightOperand(); Type rightType = ((JCTree.JCExpression) rightOperand).type; // We know that both operands are String objects if (state.getTypes().isSameType(rightType, stringType)) { return true; } return false; }
@Override public Description matchBinary(BinaryTree tree, VisitorState state) { if (tree.getKind() != Kind.EQUAL_TO && tree.getKind() != Kind.NOT_EQUAL_TO) { return Description.NO_MATCH; } String leftOperand = state.getSourceForNode((JCTree) tree.getLeftOperand()).toString(); String rightOperand = state.getSourceForNode((JCTree) tree.getRightOperand()).toString(); if ((PROTO_STRING_METHOD.matches(tree.getLeftOperand(), state) && tree.getRightOperand().getKind() != Kind.NULL_LITERAL) || (PROTO_STRING_METHOD.matches(tree.getRightOperand(), state) && tree.getLeftOperand().getKind() != Kind.NULL_LITERAL)) { String result = leftOperand + ".equals(" + rightOperand + ")"; if (tree.getKind() == Kind.NOT_EQUAL_TO) { result = "!" + result; } return describeMatch(tree, new SuggestedFix().replace(tree, result)); } else { return Description.NO_MATCH; } }
/** * Matches patterns like i = i + 1 and i = i - 1 in which i is volatile, and the pattern is not * enclosed by a synchronized block. */ @SuppressWarnings("unchecked") private static Matcher<AssignmentTree> assignmentIncrementDecrementMatcher( ExpressionTree variable) { return allOf( variableFromAssignmentTree( Matchers.<ExpressionTree>hasModifier(Modifier.VOLATILE)), not(inSynchronized()), expressionFromAssignmentTree(adaptMatcherType(ExpressionTree.class, BinaryTree.class, allOf( anyOf( kindIs(Kind.PLUS), kindIs(Kind.MINUS)), binaryTree( sameVariable(variable), Matchers.<ExpressionTree>anything()))))); }
/** * Matches strings added with arrays. */ @Override public Description matchBinary(BinaryTree t, VisitorState state) { if (!concatenationMatcher.matches(t, state)) { return Description.NO_MATCH; } /* * Replace instances of implicit array toString() calls due to string concatenation with * Arrays.toString(array). Also adds the necessary import statement for java.util.Arrays. */ final String replacement; String leftOperand = t.getLeftOperand().toString(); String rightOperand = t.getRightOperand().toString(); if (arrayMatcher.matches(t.getLeftOperand(), state)) { replacement = "Arrays.toString(" + leftOperand + ") + " + rightOperand; } else { replacement = leftOperand + " + Arrays.toString(" + rightOperand + ")"; } Fix fix = new SuggestedFix() .replace(t, replacement) .addImport("java.util.Arrays"); return describeMatch(t, fix); }
/** * Returns true if the node is a constant-time expression. * * A tree is a constant-time expression if it is: * <ol> * <li>a literal tree * <li>a reference to a final variable initialized with a compile time * constant * <li>a String concatenation of two compile time constants * </ol> */ public static boolean isCompileTimeString(ExpressionTree node) { ExpressionTree tree = TreeUtils.skipParens(node); if (tree instanceof LiteralTree) return true; if (TreeUtils.isUseOfElement(tree)) { Element elt = TreeUtils.elementFromUse(tree); return ElementUtils.isCompileTimeConstant(elt); } else if (TreeUtils.isStringConcatenation(tree)) { BinaryTree binOp = (BinaryTree) tree; return isCompileTimeString(binOp.getLeftOperand()) && isCompileTimeString(binOp.getRightOperand()); } else { return false; } }
private boolean isCheckOfGet(Element key, VariableElement map, ExpressionTree tree) { tree = TreeUtils.skipParens(tree); if (tree.getKind() != Tree.Kind.NOT_EQUAL_TO || ((BinaryTree)tree).getRightOperand().getKind() != Tree.Kind.NULL_LITERAL) return false; Tree right = TreeUtils.skipParens(((BinaryTree)tree).getLeftOperand()); if (right instanceof MethodInvocationTree) { MethodInvocationTree invok = (MethodInvocationTree)right; if (TreeUtils.isMethodInvocation(invok, mapGet, processingEnv)) { Element containsArgument = InternalUtils.symbol(invok.getArguments().get(0)); if (key.equals(containsArgument) && map.equals(getReceiver(invok))) return true; } } return false; }
/** * Returns true if the node is a constant-time expression. * * <p>A tree is a constant-time expression if it is: * * <ol> * <li>a literal tree * <li>a reference to a final variable initialized with a compile time constant * <li>a String concatenation of two compile time constants * </ol> */ public static boolean isCompileTimeString(ExpressionTree node) { ExpressionTree tree = TreeUtils.skipParens(node); if (tree instanceof LiteralTree) { return true; } if (TreeUtils.isUseOfElement(tree)) { Element elt = TreeUtils.elementFromUse(tree); return ElementUtils.isCompileTimeConstant(elt); } else if (TreeUtils.isStringConcatenation(tree)) { BinaryTree binOp = (BinaryTree) tree; return isCompileTimeString(binOp.getLeftOperand()) && isCompileTimeString(binOp.getRightOperand()); } else { return false; } }
@Override public Tree visitBinary(BinaryTree tree, Void p) { BinaryTree n = make.Binary(tree.getKind(), tree.getLeftOperand(), tree.getRightOperand()); model.setType(n, model.getType(tree)); comments.copyComments(tree, n); model.setPos(n, model.getPos(tree)); return n; }
public Boolean visitBinary(BinaryTree node, TreePath p) { if (p == null) { super.visitBinary(node, p); return false; } BinaryTree bt = (BinaryTree) p.getLeaf(); boolean result = scan(node.getLeftOperand(), bt.getLeftOperand(), p); return result && scan(node.getRightOperand(), bt.getRightOperand(), p); }
private ExpressionTree negateBinaryOperator(Tree original, Kind newKind, boolean negateOperands) { BinaryTree bt = (BinaryTree) original; BinaryTree nonNegated = make.Binary(newKind, bt.getLeftOperand(), bt.getRightOperand()); if (negateOperands) { ExpressionTree lo = negate(bt.getLeftOperand(), nonNegated, false); ExpressionTree ro = negate(bt.getRightOperand(), nonNegated, false); return make.Binary(newKind, lo != null ? lo : bt.getLeftOperand(), ro != null ? ro : bt.getRightOperand()); } return nonNegated; }
@Override public Object visitBinary(BinaryTree node, Object p) { if (node.getKind() == Tree.Kind.NOT_EQUAL_TO) { negationsCount++; } return super.visitBinary(node, p); }