| /* |
| * 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); |
| } |
| } |