diff --git a/plugins/InspectionGadgets/src/META-INF/plugin.xml b/plugins/InspectionGadgets/src/META-INF/plugin.xml index 6a602254d819..0c720b913024 100644 --- a/plugins/InspectionGadgets/src/META-INF/plugin.xml +++ b/plugins/InspectionGadgets/src/META-INF/plugin.xml @@ -5,7 +5,6 @@ inspection 1.0 - com.siyeh.InspectionGadgetsBundle @@ -2219,6 +2218,11 @@ key="unqualified.static.usage.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.style.UnqualifiedStaticUsageInspection"/> + + #ref ignored.junit.test.method.problem.descriptor=Test method ''{0}()'' annotated with #ref +unclear.binary.expression.display.name=Unclear binary expression +unclear.binary.expression.problem.descriptor=Expression could use clarifying parentheses #loc +unclear.binary.expression.quickfix=Add clarifying parentheses diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnclearBinaryExpressionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnclearBinaryExpressionInspection.java new file mode 100644 index 000000000000..7e9ea155c11c --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnclearBinaryExpressionInspection.java @@ -0,0 +1,179 @@ +/* + * Copyright 2011 Bas Leijdekkers + * + * 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.siyeh.ig.style; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +public class UnclearBinaryExpressionInspection extends BaseInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("unclear.binary.expression.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("unclear.binary.expression.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new UnclearBinaryExpressionFix(); + } + + private static class UnclearBinaryExpressionFix extends InspectionGadgetsFix { + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("unclear.binary.expression.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiPolyadicExpression)) { + return; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)element; + final StringBuilder newExpressionText = createReplacementText(polyadicExpression, new StringBuilder()); + replaceExpression(polyadicExpression, newExpressionText.toString()); + } + + private static StringBuilder createReplacementText(PsiExpression expression, StringBuilder out) { + if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + final IElementType tokenType = polyadicExpression.getOperationTokenType(); + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression parentPolyadicExpression = (PsiPolyadicExpression)parent; + final IElementType parentOperationSign = parentPolyadicExpression.getOperationTokenType(); + if (!tokenType.equals(parentOperationSign)) { + out.append('('); + createText(polyadicExpression, out); + out.append(')'); + return out; + } + } else if (parent instanceof PsiConditionalExpression || parent instanceof PsiInstanceOfExpression) { + out.append('('); + createText(polyadicExpression, out); + out.append(')'); + return out; + } + createText(polyadicExpression, out); + } + else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; + final PsiExpression unwrappedExpression = parenthesizedExpression.getExpression(); + out.append('('); + createReplacementText(unwrappedExpression, out); + out.append(')'); + } + else if (expression instanceof PsiInstanceOfExpression) { + out.append('('); + out.append(expression.getText()); + out.append(')'); + } + else if (expression != null) { + out.append(expression.getText()); + } + return out; + } + + private static void createText(PsiPolyadicExpression polyadicExpression, StringBuilder out) { + final PsiExpression[] operands = polyadicExpression.getOperands(); + for (PsiExpression operand : operands) { + if (operand == null) { + continue; + } + if (operand.getType() == PsiType.VOID) { + throw new ProcessCanceledException(); + } + if (operands.length == 1) { + createReplacementText(operand, out); + } + final PsiJavaToken token = polyadicExpression.getTokenBeforeOperand(operand); + if (token != null) { + final PsiElement beforeToken = token.getPrevSibling(); + if (beforeToken instanceof PsiWhiteSpace) { + out.append(beforeToken.getText()); + } + out.append(token.getText()); + final PsiElement afterToken = token.getNextSibling(); + if (afterToken instanceof PsiWhiteSpace) { + out.append(afterToken.getText()); + } + } + if (operands.length != 1) { + createReplacementText(operand, out); + } + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnclearBinaryExpressionVisitor(); + } + + private static class UnclearBinaryExpressionVisitor extends BaseInspectionVisitor { + + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiInstanceOfExpression || + parent instanceof PsiConditionalExpression) { + registerError(expression); + return; + } + if (parent instanceof PsiPolyadicExpression) { + return; + } + final IElementType tokenType = expression.getOperationTokenType(); + final PsiExpression[] operands = expression.getOperands(); + for (PsiExpression operand : operands) { + if (operand instanceof PsiInstanceOfExpression) { + registerError(expression); + return; + } + if (!(operand instanceof PsiPolyadicExpression)) { + continue; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)operand; + final IElementType childTokenType = polyadicExpression.getOperationTokenType(); + if (!tokenType.equals(childTokenType)) { + registerError(expression); + return; + } + } + super.visitPolyadicExpression(expression); + } + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/UnclearBinaryExpression.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/UnclearBinaryExpression.java new file mode 100644 index 000000000000..fcecae9a4a95 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/UnclearBinaryExpression.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Bas Leijdekkers + * + * 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.siyeh.igtest.style.unclear_binary_expression; + +import java.util.ArrayList; +import java.util.List; + +public class UnclearBinaryExpression { + + void foo() { + boolean b2 = "asdf" + "asdf" instanceof String; + int i = true ? 1 + 2 * 7 : 2; + boolean j = true ? false : true; + System.out.println(3 + 1 + 2 * 9 * 8 + 1); + } + + boolean bar(String name, Condition condition, Operation operation) { + List values = new ArrayList(); + return name.equals(condition.name) + && values.contains(condition.value) == (operation == Operation.equals) + && name instanceof String; + } + + class Condition { + String name; + String value; + } + static class Operation { + static Operation equals; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/expected.xml new file mode 100644 index 000000000000..21dec4c3b280 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unclear_binary_expression/expected.xml @@ -0,0 +1,30 @@ + + + + UnclearBinaryExpression.java + 32 + Unclear binary expression + Expression could use clarifying parentheses #loc + + + + UnclearBinaryExpression.java + 24 + Unclear binary expression + Expression could use clarifying parentheses #loc + + + + UnclearBinaryExpression.java + 25 + Unclear binary expression + Expression could use clarifying parentheses #loc + + + + UnclearBinaryExpression.java + 27 + Unclear binary expression + Expression could use clarifying parentheses #loc + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnclearBinaryExpressionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnclearBinaryExpressionInspectionTest.java new file mode 100644 index 000000000000..fe0f4c7e1c53 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnclearBinaryExpressionInspectionTest.java @@ -0,0 +1,11 @@ +package com.siyeh.ig.style; + +import com.siyeh.ig.IGInspectionTestCase; + +public class UnclearBinaryExpressionInspectionTest extends IGInspectionTestCase { + + public void test() throws Exception { + doTest("com/siyeh/igtest/style/unclear_binary_expression", + new UnclearBinaryExpressionInspection()); + } +} \ No newline at end of file