/*
* Copyright 2017 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.bugpatterns;
import static com.google.errorprone.BugPattern.Category.JDK;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Description.NO_MATCH;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.DoWhileLoopTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.ForLoopTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.IfTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.WhileLoopTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.DoWhileLoopTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.tools.javac.tree.JCTree;
/** @author cushon@google.com (Liam Miller-Cushon) */
@BugPattern(
name = "LogicalAssignment",
category = JDK,
summary =
"Assignment where a boolean expression was expected;"
+ " use == if this assignment wasn't expected or add parentheses for clarity.",
severity = WARNING
)
public class LogicalAssignment extends BugChecker
implements IfTreeMatcher, WhileLoopTreeMatcher, DoWhileLoopTreeMatcher, ForLoopTreeMatcher {
@Override
public Description matchIf(IfTree tree, VisitorState state) {
return checkCondition(skipOneParen(tree.getCondition()), state);
}
@Override
public Description matchDoWhileLoop(DoWhileLoopTree tree, VisitorState state) {
return checkCondition(skipOneParen(tree.getCondition()), state);
}
@Override
public Description matchForLoop(ForLoopTree tree, VisitorState state) {
// for loop condition expressions don't have an extra ParenthesizedTree
return checkCondition(tree.getCondition(), state);
}
@Override
public Description matchWhileLoop(WhileLoopTree tree, VisitorState state) {
return checkCondition(skipOneParen(tree.getCondition()), state);
}
private static ExpressionTree skipOneParen(ExpressionTree tree) {
// javac includes a ParenthesizedTree for the mandatory parens in if statement and loop
// conditions, e.g. in `if (true) {}` the condition is a paren tree containing a literal.
return tree instanceof ParenthesizedTree ? ((ParenthesizedTree) tree).getExpression() : tree;
}
private Description checkCondition(ExpressionTree condition, VisitorState state) {
if (!(condition instanceof AssignmentTree)) {
return NO_MATCH;
}
AssignmentTree assign = (AssignmentTree) condition;
return buildDescription(condition)
.addFix(
SuggestedFix.builder().prefixWith(condition, "(").postfixWith(condition, ")").build())
.addFix(
SuggestedFix.replace(
/*startPos=*/ state.getEndPosition(assign.getVariable()),
/*endPos=*/ ((JCTree) assign.getExpression()).getStartPosition(),
" == "))
.build();
}
}