blob: 4771ed01181b3cdf4010ceaa963461778fb02cc1 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.plugins.groovy.codeInspection.confusing;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.codeInspection.GroovyFix;
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
public class GroovyPointlessArithmeticInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return "Pointless arithmetic expression";
}
@Override
@NotNull
public String getGroupDisplayName() {
return CONFUSING_CODE_CONSTRUCTS;
}
@Override
public boolean isEnabledByDefault() {
return false;
}
@NotNull
@Override
public BaseInspectionVisitor buildVisitor() {
return new PointlessArithmeticVisitor();
}
@Override
public String buildErrorString(Object... args) {
return GroovyInspectionBundle.message("pointless.arithmetic.error.message", calculateReplacementExpression((GrExpression) args[0]));
}
private static String calculateReplacementExpression(GrExpression expression) {
final GrBinaryExpression exp = (GrBinaryExpression)expression;
final IElementType sign = exp.getOperationTokenType();
final GrExpression lhs = exp.getLeftOperand();
final GrExpression rhs = exp.getRightOperand();
assert rhs != null;
if (GroovyTokenTypes.mPLUS == sign) {
if (isZero(lhs)) {
return rhs.getText();
}
else {
return lhs.getText();
}
}
if (GroovyTokenTypes.mMINUS == sign) {
return lhs.getText();
}
if (GroovyTokenTypes.mSTAR == sign) {
if (isOne(lhs)) {
return rhs.getText();
}
else if (isOne(rhs)) {
return lhs.getText();
}
else {
return "0";
}
}
if (GroovyTokenTypes.mDIV == sign) {
return lhs.getText();
}
return "";
}
@Override
public GroovyFix buildFix(@NotNull PsiElement location) {
return new PointlessArithmeticFix();
}
private static class PointlessArithmeticFix extends GroovyFix {
@Override
@NotNull
public String getName() {
return "Simplify";
}
@Override
public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
final GrExpression expression = (GrExpression) descriptor.getPsiElement();
final String newExpression = calculateReplacementExpression(expression);
replaceExpression(expression, newExpression);
}
}
private static class PointlessArithmeticVisitor extends BaseInspectionVisitor {
private final TokenSet arithmeticTokens = TokenSet.create(GroovyTokenTypes.mPLUS, GroovyTokenTypes.mMINUS, GroovyTokenTypes.mSTAR,
GroovyTokenTypes.mDIV);
@Override
public void visitBinaryExpression(@NotNull GrBinaryExpression expression) {
super.visitBinaryExpression(expression);
final GrExpression rhs = expression.getRightOperand();
if (rhs == null) return;
final IElementType sign = expression.getOperationTokenType();
if (!arithmeticTokens.contains(sign)) return;
final GrExpression lhs = expression.getLeftOperand();
final boolean isPointless;
if (sign.equals(GroovyTokenTypes.mPLUS)) {
isPointless = additionExpressionIsPointless(lhs, rhs);
}
else if (sign.equals(GroovyTokenTypes.mMINUS)) {
isPointless = subtractionExpressionIsPointless(rhs);
}
else if (sign.equals(GroovyTokenTypes.mSTAR)) {
isPointless = multiplyExpressionIsPointless(lhs, rhs);
}
else if (sign.equals(GroovyTokenTypes.mDIV)) {
isPointless = divideExpressionIsPointless(rhs);
}
else {
isPointless = false;
}
if (!isPointless) {
return;
}
registerError(expression);
}
}
private static boolean subtractionExpressionIsPointless(GrExpression rhs) {
return isZero(rhs);
}
private static boolean additionExpressionIsPointless(GrExpression lhs,
GrExpression rhs) {
return isZero(lhs) || isZero(rhs);
}
private static boolean multiplyExpressionIsPointless(GrExpression lhs,
GrExpression rhs) {
return isZero(lhs) || isZero(rhs) || isOne(lhs) || isOne(rhs);
}
private static boolean divideExpressionIsPointless(GrExpression rhs) {
return isOne(rhs);
}
/**
* @noinspection FloatingPointEquality
*/
private static boolean isZero(GrExpression expression) {
final PsiElement inner = PsiUtil.skipParentheses(expression, false);
if (inner == null) return false;
@NonNls final String text = inner.getText();
return "0".equals(text) ||
"0x0".equals(text) ||
"0X0".equals(text) ||
"0.0".equals(text) ||
"0L".equals(text) ||
"0l".equals(text) ||
"0b0".equals(text) ||
"0B0".equals(text);
}
/**
* @noinspection FloatingPointEquality
*/
private static boolean isOne(GrExpression expression) {
final PsiElement inner = PsiUtil.skipParentheses(expression, false);
if (inner == null) return false;
@NonNls final String text = inner.getText();
return "1".equals(text) ||
"0x1".equals(text) ||
"0X1".equals(text) ||
"1.0".equals(text) ||
"1L".equals(text) ||
"1l".equals(text) ||
"0b0".equals(text) ||
"0B0".equals(text);
}
}