-
Notifications
You must be signed in to change notification settings - Fork 724
SONARJAVA-6825: Implemented rule S9360 - Comparisons should not use Yoda conditions #6022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5a4b3d4
36d0e82
dadeeeb
de6bbda
3d56621
3c2e41f
f045bee
a7e75c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "commons-beanutils:commons-beanutils:src/main/java/org/apache/commons/beanutils2/ConstructorUtils.java": [ | ||
| 106, | ||
| 154, | ||
| 218, | ||
| 267 | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| { | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java": [ | ||
| 343, | ||
| 358, | ||
| 373, | ||
| 388, | ||
| 403 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java": [ | ||
| 1028 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java": [ | ||
| 431, | ||
| 436, | ||
| 445, | ||
| 453, | ||
| 455, | ||
| 459, | ||
| 465, | ||
| 471, | ||
| 477, | ||
| 483, | ||
| 489, | ||
| 495, | ||
| 497, | ||
| 501, | ||
| 503, | ||
| 514, | ||
| 514, | ||
| 515, | ||
| 575, | ||
| 615, | ||
| 617, | ||
| 622, | ||
| 624, | ||
| 628, | ||
| 634, | ||
| 640, | ||
| 646, | ||
| 652, | ||
| 658, | ||
| 664, | ||
| 666, | ||
| 670, | ||
| 677, | ||
| 684 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java": [ | ||
| 93, | ||
| 414 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java": [ | ||
| 94 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java": [ | ||
| 744, | ||
| 835 | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| { | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java": [ | ||
| 343, | ||
| 358, | ||
| 373, | ||
| 388, | ||
| 403 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java": [ | ||
| 1028 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java": [ | ||
| 431, | ||
| 436, | ||
| 445, | ||
| 453, | ||
| 455, | ||
| 459, | ||
| 465, | ||
| 471, | ||
| 477, | ||
| 483, | ||
| 489, | ||
| 495, | ||
| 497, | ||
| 501, | ||
| 503, | ||
| 514, | ||
| 514, | ||
| 515, | ||
| 575, | ||
| 615, | ||
| 617, | ||
| 622, | ||
| 624, | ||
| 628, | ||
| 634, | ||
| 640, | ||
| 646, | ||
| 652, | ||
| 658, | ||
| 664, | ||
| 666, | ||
| 670, | ||
| 677, | ||
| 684 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java": [ | ||
| 93, | ||
| 414 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-io/src/test/java/org/eclipse/jetty/io/ArrayByteBufferPoolTest.java": [ | ||
| 94 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java": [ | ||
| 744, | ||
| 835 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/AsyncJSON.java": [ | ||
| 1275 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java": [ | ||
| 901, | ||
| 907, | ||
| 925, | ||
| 942, | ||
| 957, | ||
| 962, | ||
| 978 | ||
| ], | ||
| "org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java": [ | ||
| 117, | ||
| 132 | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "com.google.guava:guava:src/com/google/common/base/SmallCharMatcher.java": [ | ||
| 61 | ||
| ], | ||
| "com.google.guava:guava:src/com/google/common/io/LittleEndianDataInputStream.java": [ | ||
| 82 | ||
| ], | ||
| "com.google.guava:guava:src/com/google/common/net/MediaType.java": [ | ||
| 628, | ||
| 631, | ||
| 632 | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package checks; | ||
|
|
||
| class YodaConditionCheckSample { | ||
|
|
||
| void unknownLiteralType() { | ||
| Object x = new Object(); | ||
| if (UNKNOWN_LITERAL == x) { } // Compliant - UNKNOWN_LITERAL is not a valid literal | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package checks; | ||
|
|
||
| class YodaConditionCheckSample { | ||
|
|
||
| void testIntLiteral() { | ||
| int count = 0; | ||
| int x = 5; | ||
| if (0 == count) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^ | ||
| if (5 != x) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^ | ||
| if (count == 0) { } // Compliant | ||
| if (x != 5) { } // Compliant | ||
| } | ||
|
|
||
| void testNullLiteral() { | ||
| Object obj = null; | ||
| Object myObject = null; | ||
| if (null == obj) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^ | ||
| if (null != myObject) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^ | ||
| if (obj == null) { } // Compliant | ||
| if (myObject != null) { } // Compliant | ||
| if (null == null) { } // Compliant | ||
| } | ||
|
|
||
| void testBooleanLiteral() { | ||
| boolean flag = true; | ||
| boolean result = false; | ||
| if (true == flag) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^ | ||
| if (false != result) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^^ | ||
| if (flag == true) { } // Compliant | ||
| if (result != false) { } // Compliant | ||
| } | ||
|
|
||
| void testStringLiteral() { | ||
| String str = "hello"; | ||
| String value = ""; | ||
| if ("hello" == str) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^^^^ | ||
| if ("" != value) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^ | ||
| if (str == "hello") { } // Compliant | ||
| if (value != "") { } // Compliant | ||
| } | ||
|
|
||
| void testCharLiteral() { | ||
| char ch = 'a'; | ||
| if ('a' == ch) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^ | ||
| if (ch == 'a') { } // Compliant | ||
| } | ||
|
|
||
| void testFloatingPointLiteral() { | ||
| double doubleValue = 0.0; | ||
| if (0.0 == doubleValue) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^ | ||
| if (doubleValue == 0.0) { } // Compliant | ||
| } | ||
|
|
||
| void testNestedParentheses() { | ||
| int count = 0; | ||
| Object obj = null; | ||
| if ((0) == count) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| if (((null)) == obj) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^^^^ | ||
| } | ||
|
|
||
| void testLessThanGreaterThan() { | ||
| int count = 0; | ||
| int x = 5; | ||
| if (0 < count) { } // Noncompliant {{Put the variable on the left side of this comparison and reverse the operator.}} | ||
| // ^ | ||
| if (5 > x) { } // Noncompliant {{Put the variable on the left side of this comparison and reverse the operator.}} | ||
| // ^ | ||
| if (count > 0) { } // Compliant | ||
| if (x < 5) { } // Compliant | ||
| } | ||
|
|
||
| void testLessThanOrEqualGreaterThanOrEqual() { | ||
| int count = 0; | ||
| int x = 5; | ||
| if (0 <= count) { } // Noncompliant {{Put the variable on the left side of this comparison and reverse the operator.}} | ||
| // ^ | ||
| if (5 >= x) { } // Noncompliant {{Put the variable on the left side of this comparison and reverse the operator.}} | ||
| // ^ | ||
| if (count >= 0) { } // Compliant | ||
| if (x <= 5) { } // Compliant | ||
| } | ||
|
|
||
| void testNonComparisonContexts() { | ||
| int count = 0; | ||
| int a = 1; | ||
| int b = 2; | ||
| count = 0; // Compliant - assignment | ||
| int sum = a + 5; // Compliant - arithmetic | ||
| int product = 5 * b; // Compliant - arithmetic | ||
| Object obj = Math.max(5, 10); // Compliant - method call | ||
| } | ||
|
|
||
| void testTernaryOperator() { | ||
| boolean condition = true; | ||
| int result = condition ? 5 : 10; // Compliant | ||
| if (condition) { } // Compliant | ||
| } | ||
|
|
||
| void testArrayAccess() { | ||
| int[] array = {1, 2, 3}; | ||
| if (0 == array[0]) { } // Noncompliant {{Put the variable on the left side of this comparison.}} | ||
| // ^ | ||
| if (array[0] == 0) { } // Compliant | ||
| } | ||
|
|
||
| void testBothLiterals() { | ||
| if (0 == 0) { } // Compliant - both sides are literals | ||
| if (5 != 10) { } // Compliant - both sides are literals | ||
| if (true == false) { } // Compliant - both sides are literals | ||
| } | ||
|
|
||
| void testBothVariables() { | ||
| int count = 0; | ||
| int otherCount = 0; | ||
| Object obj1 = null; | ||
| Object obj2 = null; | ||
| if (count == otherCount) { } // Compliant - both are variables | ||
| if (obj1 == obj2) { } // Compliant - both are variables | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* | ||
| * SonarQube Java | ||
| * Copyright (C) SonarSource Sàrl | ||
| * mailto:info AT sonarsource DOT com | ||
| * | ||
| * You can redistribute and/or modify this program under the terms of | ||
| * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| * See the Sonar Source-Available License for more details. | ||
| * | ||
| * You should have received a copy of the Sonar Source-Available License | ||
| * along with this program; if not, see https://sonarsource.com/license/ssal/ | ||
| */ | ||
| package org.sonar.java.checks; | ||
|
|
||
| import java.util.List; | ||
| import org.sonar.check.Rule; | ||
| import org.sonar.java.model.ExpressionUtils; | ||
| import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; | ||
| import org.sonar.plugins.java.api.tree.BinaryExpressionTree; | ||
| import org.sonar.plugins.java.api.tree.ExpressionTree; | ||
| import org.sonar.plugins.java.api.tree.Tree; | ||
|
|
||
| @Rule(key = "S9360") | ||
| public class YodaConditionCheck extends IssuableSubscriptionVisitor { | ||
|
|
||
| private static final String MESSAGE = "Put the variable on the left side of this comparison."; | ||
| private static final String MESSAGE_WITH_REVERSE = "Put the variable on the left side of this comparison and reverse the operator."; | ||
|
|
||
| @Override | ||
| public List<Tree.Kind> nodesToVisit() { | ||
| return List.of( | ||
| Tree.Kind.EQUAL_TO, | ||
| Tree.Kind.NOT_EQUAL_TO, | ||
| Tree.Kind.LESS_THAN, | ||
| Tree.Kind.GREATER_THAN, | ||
| Tree.Kind.LESS_THAN_OR_EQUAL_TO, | ||
| Tree.Kind.GREATER_THAN_OR_EQUAL_TO | ||
| ); | ||
| } | ||
|
gitar-bot[bot] marked this conversation as resolved.
|
||
|
|
||
| @Override | ||
| public void visitNode(Tree tree) { | ||
| BinaryExpressionTree binaryExpression = (BinaryExpressionTree) tree; | ||
| ExpressionTree left = ExpressionUtils.skipParentheses(binaryExpression.leftOperand()); | ||
| ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpression.rightOperand()); | ||
|
|
||
| if (isLiteral(left) && !isLiteral(right)) { | ||
| reportIssue(left, isRelationalOperator(tree) ? MESSAGE_WITH_REVERSE : MESSAGE); | ||
| } | ||
| } | ||
|
gitar-bot[bot] marked this conversation as resolved.
|
||
|
|
||
| private static boolean isRelationalOperator(Tree tree) { | ||
| return tree.is( | ||
| Tree.Kind.LESS_THAN, | ||
| Tree.Kind.GREATER_THAN, | ||
| Tree.Kind.LESS_THAN_OR_EQUAL_TO, | ||
| Tree.Kind.GREATER_THAN_OR_EQUAL_TO | ||
| ); | ||
| } | ||
|
|
||
| private static boolean isLiteral(ExpressionTree tree) { | ||
| return tree.is( | ||
|
Comment on lines
+65
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This recognizes only bare literal AST nodes, not Java constant expressions. For example, |
||
| Tree.Kind.INT_LITERAL, | ||
| Tree.Kind.LONG_LITERAL, | ||
| Tree.Kind.FLOAT_LITERAL, | ||
| Tree.Kind.DOUBLE_LITERAL, | ||
| Tree.Kind.BOOLEAN_LITERAL, | ||
| Tree.Kind.CHAR_LITERAL, | ||
| Tree.Kind.STRING_LITERAL, | ||
|
Comment on lines
+69
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These literal kinds cause verified duplicate findings for equality operators. I ran focused |
||
| Tree.Kind.NULL_LITERAL | ||
| ); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.