package quickfix; import static edu.umd.cs.findbugs.plugin.eclipse.quickfix.util.ASTUtil.getASTNode; import java.util.List; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.plugin.eclipse.quickfix.BugResolution; import edu.umd.cs.findbugs.plugin.eclipse.quickfix.exception.BugResolutionException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Assignment.Operator; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import util.TraversalUtil; public class UnnecessaryStoreResolution extends BugResolution { @Override protected boolean resolveBindings() { return false; // don't need type bindings } @Override protected void repairBug(ASTRewrite rewrite, CompilationUnit workingUnit, BugInstance bug) throws BugResolutionException { ASTNode node = getASTNode(workingUnit, bug.getPrimarySourceLineAnnotation()); USBRVisitor visitor = new USBRVisitor(); node.accept(visitor); if (visitor.unnecessaryStoreStatement != null && visitor.unnecessaryStoreExpression != null && visitor.originalReturn != null) { ReturnStatement newReturnStatement = makeNewReturnStatement(rewrite, visitor); rewrite.remove(visitor.unnecessaryStoreStatement, null); rewrite.replace(visitor.originalReturn, newReturnStatement, null); } } private ReturnStatement makeNewReturnStatement(ASTRewrite rewrite, USBRVisitor visitor) { AST ast = rewrite.getAST(); ReturnStatement retVal = ast.newReturnStatement(); Expression baseExpression = (Expression) rewrite.createCopyTarget(visitor.unnecessaryStoreExpression); Operator assignOperator = visitor.unnecessaryStoreOperator; if (assignOperator != null && assignOperator != Operator.ASSIGN) { InfixExpression infixExpression = ast.newInfixExpression(); infixExpression.setLeftOperand((Expression) rewrite.createCopyTarget(visitor.storedVariable)); infixExpression.setRightOperand(baseExpression); infixExpression.setOperator(convertAssignOperatorToInfixOperator(assignOperator)); baseExpression = infixExpression; } retVal.setExpression(baseExpression); return retVal; } private InfixExpression.Operator convertAssignOperatorToInfixOperator(Operator assignOperator) { if (assignOperator == Operator.PLUS_ASSIGN) { return InfixExpression.Operator.PLUS; } else if (assignOperator == Operator.TIMES_ASSIGN) { return InfixExpression.Operator.TIMES; } else if (assignOperator == Operator.MINUS_ASSIGN) { return InfixExpression.Operator.MINUS; } else if (assignOperator == Operator.DIVIDE_ASSIGN) { return InfixExpression.Operator.DIVIDE; } else if (assignOperator == Operator.REMAINDER_ASSIGN) { return InfixExpression.Operator.REMAINDER; } else if (assignOperator == Operator.LEFT_SHIFT_ASSIGN) { return InfixExpression.Operator.LEFT_SHIFT; } else if (assignOperator == Operator.RIGHT_SHIFT_SIGNED_ASSIGN) { return InfixExpression.Operator.RIGHT_SHIFT_SIGNED; } else if (assignOperator == Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN) { return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED; } else if (assignOperator == Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN) { return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED; } else if (assignOperator == Operator.BIT_AND_ASSIGN) { return InfixExpression.Operator.AND; } else if (assignOperator == Operator.BIT_OR_ASSIGN) { return InfixExpression.Operator.OR; } else if (assignOperator == Operator.BIT_XOR_ASSIGN) { return InfixExpression.Operator.XOR; } return null; } private static class USBRVisitor extends ASTVisitor { public Statement unnecessaryStoreStatement; public Expression unnecessaryStoreExpression; public ReturnStatement originalReturn; public Operator unnecessaryStoreOperator; private Expression storedVariable; @Override public boolean visit(ReturnStatement node) { if (originalReturn != null) { return false; } if (node.getExpression() instanceof SimpleName) { this.originalReturn = node; findUnnecessaryStore(node); } return true; } private void findUnnecessaryStore(ReturnStatement node) { Block block = TraversalUtil.findClosestAncestor(node, Block.class); @SuppressWarnings("unchecked") List<Statement> blockStatements = block.statements(); for (int i = 1; i < blockStatements.size(); i++) { Statement statement = blockStatements.get(i); if (statement == this.originalReturn) { for (int j = i - 1; j >= 0; j--) { Statement storeStatement = blockStatements.get(j); if (storeStatement instanceof VariableDeclarationStatement) { splitStatementAndInitializer((VariableDeclarationStatement) storeStatement); return; } else if (storeStatement instanceof ExpressionStatement) { if (splitStatementAndInitializer((ExpressionStatement) storeStatement)) { // we found our extra storage statement return; } } } } } } private boolean splitStatementAndInitializer(ExpressionStatement storeStatement) { Expression storeExpression = storeStatement.getExpression(); if (storeExpression instanceof Assignment) { Assignment assignment = (Assignment) storeExpression; this.unnecessaryStoreStatement = storeStatement; this.storedVariable = assignment.getLeftHandSide(); this.unnecessaryStoreOperator = assignment.getOperator(); this.unnecessaryStoreExpression = assignment.getRightHandSide(); return true; } return false; } private void splitStatementAndInitializer(VariableDeclarationStatement statement) { this.unnecessaryStoreStatement = statement; VariableDeclarationFragment fragment = (VariableDeclarationFragment) statement.fragments().get(0); this.unnecessaryStoreExpression = fragment.getInitializer(); } } }