-
Notifications
You must be signed in to change notification settings - Fork 27
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
fix: mark MathOnFloatProcessor
as incomplete
#866
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
72ff6b6
Fix repair for S2164: Math should not be performed on floats
algomaster99 79cc78f
Update generated field
algomaster99 6a2987a
Update repair description
algomaster99 429bedd
Verify repair for field
algomaster99 8f4ce5c
Improve coverage
algomaster99 901c897
tests: add configuration to verify that MathOnFloatProcessor does ski…
algomaster99 7468bbd
Assume that the parent of CtBinaryOperator will always be a CtTypedEl…
algomaster99 8d25a9c
Remove redundant methods
algomaster99 a0768e2
Simplify canRepairInternal
algomaster99 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 34 additions & 51 deletions
85
sorald/src/main/java/sorald/processor/MathOnFloatProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,62 @@ | ||
package sorald.processor; | ||
|
||
import java.util.List; | ||
import sorald.Constants; | ||
import sorald.annotations.IncompleteProcessor; | ||
import sorald.annotations.ProcessorAnnotation; | ||
import spoon.reflect.code.BinaryOperatorKind; | ||
import spoon.reflect.code.CtBinaryOperator; | ||
import spoon.reflect.code.CtCodeSnippetExpression; | ||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.declaration.CtTypedElement; | ||
import spoon.reflect.visitor.filter.TypeFilter; | ||
|
||
@IncompleteProcessor( | ||
description = | ||
"does not cast the operands to double when the expected type of the result is float.") | ||
@ProcessorAnnotation(key = "S2164", description = "Math should not be performed on floats") | ||
public class MathOnFloatProcessor extends SoraldAbstractProcessor<CtBinaryOperator> { | ||
|
||
@Override | ||
protected boolean canRepairInternal(CtBinaryOperator candidate) { | ||
List<CtBinaryOperator> binaryOperatorChildren = | ||
candidate.getElements(new TypeFilter<>(CtBinaryOperator.class)); | ||
if (binaryOperatorChildren.size() | ||
== 1) { // in a nested binary operator expression, only one will be processed. | ||
if (isArithmeticOperation(candidate) | ||
&& isOperationBetweenFloats(candidate) | ||
&& !withinStringConcatenation(candidate)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
return !((CtTypedElement<?>) candidate.getParent()) | ||
.getType() | ||
.equals(getFactory().Type().floatPrimitiveType()); | ||
} | ||
|
||
@Override | ||
protected void repairInternal(CtBinaryOperator element) { | ||
CtCodeSnippetExpression newLeftHandOperand = | ||
element.getFactory() | ||
.createCodeSnippetExpression("(double) " + element.getLeftHandOperand()); | ||
element.setLeftHandOperand(newLeftHandOperand); | ||
CtCodeSnippetExpression newRightHandOperand = | ||
element.getFactory() | ||
.createCodeSnippetExpression("(double) " + element.getRightHandOperand()); | ||
element.setRightHandOperand(newRightHandOperand); | ||
} | ||
List<CtBinaryOperator> binaryOperatorChildren = | ||
element.getElements(new TypeFilter<>(CtBinaryOperator.class)); | ||
for (CtBinaryOperator binaryOperator : binaryOperatorChildren) { | ||
if (binaryOperator.getLeftHandOperand() instanceof CtBinaryOperator<?>) { | ||
repairInternal((CtBinaryOperator<?>) binaryOperator.getLeftHandOperand()); | ||
} | ||
if (binaryOperator.getRightHandOperand() instanceof CtBinaryOperator<?>) { | ||
repairInternal((CtBinaryOperator<?>) binaryOperator.getRightHandOperand()); | ||
} else { | ||
if (isOperationBetweenFloats(binaryOperator)) { | ||
binaryOperator | ||
.getLeftHandOperand() | ||
.setTypeCasts(List.of(getFactory().Type().doublePrimitiveType())); | ||
|
||
private boolean isArithmeticOperation(CtBinaryOperator ctBinaryOperator) { | ||
return ctBinaryOperator.getKind().compareTo(BinaryOperatorKind.PLUS) == 0 | ||
|| ctBinaryOperator.getKind().compareTo(BinaryOperatorKind.MINUS) == 0 | ||
|| ctBinaryOperator.getKind().compareTo(BinaryOperatorKind.MUL) == 0 | ||
|| ctBinaryOperator.getKind().compareTo(BinaryOperatorKind.DIV) == 0; | ||
/** | ||
* We also set the type so that the other operand is not explicitly cast as JVM | ||
* implicitly does that For example, `(double) a + (double) b` is equivalent to | ||
* `(double) a + b`. Thus, we provide a cleaner repair. | ||
*/ | ||
binaryOperator.setType(getFactory().Type().doublePrimitiveType()); | ||
} | ||
// We do not need to cast the type of the right hand operand as it is already a | ||
// double | ||
} | ||
} | ||
} | ||
|
||
private boolean isOperationBetweenFloats(CtBinaryOperator ctBinaryOperator) { | ||
return ctBinaryOperator | ||
.getLeftHandOperand() | ||
.getType() | ||
.getSimpleName() | ||
.equals(Constants.FLOAT) | ||
.equals(getFactory().Type().floatPrimitiveType()) | ||
&& ctBinaryOperator | ||
.getRightHandOperand() | ||
.getType() | ||
.getSimpleName() | ||
.equals(Constants.FLOAT); | ||
} | ||
|
||
private boolean withinStringConcatenation(CtBinaryOperator ctBinaryOperator) { | ||
CtElement parent = ctBinaryOperator; | ||
while (parent.getParent() instanceof CtBinaryOperator) { | ||
parent = parent.getParent(); | ||
} | ||
return ((CtBinaryOperator) parent).getKind().compareTo(BinaryOperatorKind.PLUS) == 0 | ||
&& (((CtBinaryOperator) parent) | ||
.getLeftHandOperand() | ||
.getType() | ||
.getQualifiedName() | ||
.equals(Constants.STRING_QUALIFIED_NAME) | ||
|| ((CtBinaryOperator) parent) | ||
.getRightHandOperand() | ||
.getType() | ||
.getQualifiedName() | ||
.equals(Constants.STRING_QUALIFIED_NAME)); | ||
.equals(getFactory().Type().floatPrimitiveType()); | ||
} | ||
} |
16 changes: 15 additions & 1 deletion
16
sorald/src/main/java/sorald/processor/MathOnFloatProcessor.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,23 @@ | ||
In arithmetic expressions between two `float`s, both left and right operands are cast to `double`. | ||
In arithmetic expressions between two `float`s, one of the operands are cast to `double`. | ||
|
||
Example: | ||
```diff | ||
float a = 16777216.0f; | ||
float b = 1.0f; | ||
- double d1 = a + b; | ||
+ double d1 = (double) a + b; | ||
``` | ||
|
||
Note that this processor is incomplete as it does not perform the following | ||
repair even though it is recommended by SonarSource in their | ||
[documentation](https://rules.sonarsource.com/java/RSPEC-2164): | ||
```diff | ||
float a = 16777216.0f; | ||
float b = 1.0f; | ||
- float c = a + b; // Noncompliant, yields 1.6777216E7 not 1.6777217E7 | ||
+ float c = (double) a + (double) b; | ||
``` | ||
|
||
The reason we do not perform this repair is that it produces a non-compilable | ||
code. See [#570](https://github.com/SpoonLabs/sorald/issues/570) for more | ||
details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
.../src/test/resources/processor_test_files/S2164_MathOnFloat/INCOMPLETE_DoNotCastFloat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class DoNoCastFloat { | ||
float a = 16777216.0f; | ||
float b = 1.0f; | ||
final float c = a + b; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
sorald/src/test/resources/processor_test_files/S2164_MathOnFloat/MathOnFloat.java.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
|
||
// Test for rule s2164 | ||
|
||
class MathOnFloat { | ||
|
||
float e = 2.71f; | ||
float pi = 3.14f; | ||
double c = (double) e * pi; | ||
|
||
// Tests from https://github.com/SonarSource/sonar-java/blob/master/java-checks-test-sources/src/main/java/checks/MathOnFloatCheck.java | ||
void myMethod() { | ||
float a = 16777216.0f; | ||
float b = 1.0f; | ||
|
||
double d1 = ((double) (a)) + b; | ||
double d2 = ((double) (a)) - b; | ||
double d3 = ((double) (a)) * b; | ||
double d4 = ((double) (a)) / b; | ||
double d5 = ((((double) (a)) / b)) + b; | ||
|
||
double d6 = a + d1; | ||
|
||
double d7 = a + ((double) (a)) / b; | ||
|
||
double d8 = (((double) (a)) + b) + (((double) (e)) * pi); | ||
|
||
int i = 16777216; | ||
int j = 1; | ||
int k = i + j; | ||
System.out.println("[Max time to retrieve connection:"+(a/1000f/1000f)+" ms."); | ||
System.out.println("[Max time to retrieve connection:"+a/1000f/1000f); | ||
} | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@monperrus not related to autocompletion, but when I deleted the above code, the inline comment was suggested by co-pilot. 🤌🏼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unbelievable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
crazy