Skip to content

Commit 4bceddd

Browse files
committed
SONARJAVA-5728 Remove SEExpressionUtils.resolveAsConstant
1 parent f66b07a commit 4bceddd

File tree

12 files changed

+73
-486
lines changed

12 files changed

+73
-486
lines changed

java-symbolic-execution/java-symbolic-execution-checks-test-sources/src/main/java/symbolicexecution/checks/MinMaxRangeCheckSample.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ public double doRangeCheckNOK7(double num) {
5151
double upper = 18.37;
5252
double lower = -4.96;
5353
double result = Math.min(lower, num);
54-
return Math.max(result, upper); // Compliant - FN
54+
return Math.max(result, upper); // Noncompliant
5555
}
5656

5757
public double doRangeCheckNOK8(float num) {
5858
float upper = 18.37f;
5959
float lower = -4.96f;
6060
float result = Math.min(lower, num);
61-
return Math.max(result, upper); // Compliant - FN
61+
return Math.max(result, upper); // Noncompliant
6262
}
6363

6464
public double doRangeCheckNOK9(long num) {
@@ -98,11 +98,11 @@ public int doRangeCheckOK5(int num) { // using both time same range
9898
return Math.max(LOWER_INT, result);
9999
}
100100

101-
public int doRangeCheckOK6(int num) { // do not handle arithmetic
101+
public int doRangeCheckOK6(int num) {
102102
int upper = 1837 + 14;
103103
int lower = -496 * 42;
104104
int result = Math.min(lower, num);
105-
return Math.max(result, upper);
105+
return Math.max(result, upper); // Noncompliant
106106
}
107107

108108
public float doRangeCheckIntFloatNOK(int num) {

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SEExpressionUtils.java

Lines changed: 0 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,16 @@
1717
package org.sonar.java.model;
1818

1919
import java.util.Optional;
20-
import java.util.function.BiFunction;
21-
import javax.annotation.CheckForNull;
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
24-
import org.sonar.plugins.java.api.semantic.Symbol;
2520
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
26-
import org.sonar.plugins.java.api.tree.BinaryExpressionTree;
2721
import org.sonar.plugins.java.api.tree.ExpressionTree;
2822
import org.sonar.plugins.java.api.tree.IdentifierTree;
29-
import org.sonar.plugins.java.api.tree.LiteralTree;
3023
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
3124
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
3225
import org.sonar.plugins.java.api.tree.ParenthesizedTree;
3326
import org.sonar.plugins.java.api.tree.Tree;
34-
import org.sonar.plugins.java.api.tree.UnaryExpressionTree;
3527

3628
public final class SEExpressionUtils {
3729

38-
private static final Logger LOG = LoggerFactory.getLogger(SEExpressionUtils.class);
39-
4030
private SEExpressionUtils() {
4131
}
4232

@@ -125,71 +115,6 @@ public static boolean isNullLiteral(ExpressionTree tree) {
125115
return skipParentheses(tree).is(Tree.Kind.NULL_LITERAL);
126116
}
127117

128-
@CheckForNull
129-
public static Object resolveAsConstant(ExpressionTree tree) {
130-
ExpressionTree expression = tree;
131-
while (expression.is(Tree.Kind.PARENTHESIZED_EXPRESSION)) {
132-
expression = ((ParenthesizedTree) expression).expression();
133-
}
134-
if (expression.is(Tree.Kind.MEMBER_SELECT)) {
135-
expression = ((MemberSelectExpressionTree) expression).identifier();
136-
}
137-
if (expression.is(Tree.Kind.IDENTIFIER)) {
138-
return resolveIdentifier((IdentifierTree) expression);
139-
}
140-
if (expression.is(Tree.Kind.BOOLEAN_LITERAL)) {
141-
return Boolean.parseBoolean(((LiteralTree) expression).value());
142-
}
143-
if (expression.is(Tree.Kind.STRING_LITERAL, Tree.Kind.TEXT_BLOCK)) {
144-
return SELiteralUtils.getAsStringValue((LiteralTree) expression);
145-
}
146-
if (expression instanceof UnaryExpressionTree unaryExpressionTree) {
147-
return resolveUnaryExpression(unaryExpressionTree);
148-
}
149-
if (expression.is(Tree.Kind.INT_LITERAL)) {
150-
return SELiteralUtils.intLiteralValue(expression);
151-
}
152-
if (expression.is(Tree.Kind.LONG_LITERAL)) {
153-
return SELiteralUtils.longLiteralValue(expression);
154-
}
155-
if (expression.is(Tree.Kind.PLUS)) {
156-
return resolvePlus((BinaryExpressionTree) expression);
157-
}
158-
if (expression.is(Tree.Kind.OR)) {
159-
return resolveOr((BinaryExpressionTree) expression);
160-
}
161-
if (expression.is(Tree.Kind.MINUS)) {
162-
return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a - b, (a, b) -> a - b);
163-
}
164-
if (expression.is(Tree.Kind.MULTIPLY)) {
165-
return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a * b, (a, b) -> a * b);
166-
}
167-
if (expression.is(Tree.Kind.DIVIDE)) {
168-
return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a / b, (a, b) -> a / b);
169-
}
170-
if (expression.is(Tree.Kind.REMAINDER)) {
171-
return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a % b, (a, b) -> a % b);
172-
}
173-
return null;
174-
}
175-
176-
@CheckForNull
177-
private static Object resolveIdentifier(IdentifierTree tree) {
178-
Symbol symbol = tree.symbol();
179-
if (!symbol.isVariableSymbol()) {
180-
return null;
181-
}
182-
Symbol owner = symbol.owner();
183-
if (owner.isTypeSymbol() && owner.type().is("java.lang.Boolean")) {
184-
if ("TRUE".equals(symbol.name())) {
185-
return Boolean.TRUE;
186-
} else if ("FALSE".equals(symbol.name())) {
187-
return Boolean.FALSE;
188-
}
189-
}
190-
return ((Symbol.VariableSymbol) symbol).constantValue().orElse(null);
191-
}
192-
193118
public static IdentifierTree extractIdentifier(AssignmentExpressionTree tree) {
194119
Optional<IdentifierTree> identifier = extractIdentifier(tree.variable());
195120

@@ -212,85 +137,4 @@ public static boolean isThis(ExpressionTree expression) {
212137
return newExpression.is(Tree.Kind.IDENTIFIER) && "this".equals(((IdentifierTree) newExpression).name());
213138
}
214139

215-
@CheckForNull
216-
private static Object resolveArithmeticOperation(Object left, Object right, BiFunction<Long, Long, Object> longOperation, BiFunction<Integer, Integer, Object> intOperation) {
217-
try {
218-
if (left instanceof Integer leftInt && right instanceof Integer rightInt) {
219-
return intOperation.apply(leftInt, rightInt);
220-
} else if ((left instanceof Long || right instanceof Long) && (left instanceof Integer || right instanceof Integer)) {
221-
return longOperation.apply(((Number) left).longValue(), ((Number) right).longValue());
222-
}
223-
} catch (ArithmeticException e) {
224-
LOG.debug("Arithmetic exception while resolving arithmetic operation value", e);
225-
}
226-
return null;
227-
}
228-
229-
@CheckForNull
230-
private static Object resolveUnaryExpression(UnaryExpressionTree unaryExpression) {
231-
Object value = resolveAsConstant(unaryExpression.expression());
232-
if (unaryExpression.is(Tree.Kind.UNARY_PLUS)) {
233-
return value;
234-
} else if (unaryExpression.is(Tree.Kind.UNARY_MINUS)) {
235-
if (value instanceof Long longValue) {
236-
return -longValue;
237-
} else if (value instanceof Integer intValue) {
238-
return -intValue;
239-
}
240-
} else if (unaryExpression.is(Tree.Kind.BITWISE_COMPLEMENT)) {
241-
if (value instanceof Long longValue) {
242-
return ~longValue;
243-
} else if (value instanceof Integer intValue) {
244-
return ~intValue;
245-
}
246-
} else if (unaryExpression.is(Tree.Kind.LOGICAL_COMPLEMENT) && value instanceof Boolean bool) {
247-
return !bool;
248-
}
249-
return null;
250-
}
251-
252-
@CheckForNull
253-
private static Object resolvePlus(BinaryExpressionTree binaryExpression) {
254-
Object left = resolveAsConstant(binaryExpression.leftOperand());
255-
Object right = resolveAsConstant(binaryExpression.rightOperand());
256-
if (left == null || right == null) {
257-
return null;
258-
} else if (left instanceof String leftString) {
259-
return leftString + right;
260-
} else if (right instanceof String rightString) {
261-
return left + rightString;
262-
}
263-
return resolveArithmeticOperation(left, right, Long::sum, Integer::sum);
264-
}
265-
266-
@CheckForNull
267-
private static Object resolveArithmeticOperation(BinaryExpressionTree binaryExpression,
268-
BiFunction<Long, Long, Object> longOperation,
269-
BiFunction<Integer, Integer, Object> intOperation) {
270-
Object left = resolveAsConstant(binaryExpression.leftOperand());
271-
Object right = resolveAsConstant(binaryExpression.rightOperand());
272-
if (left == null || right == null) {
273-
return null;
274-
}
275-
return resolveArithmeticOperation(left, right, longOperation, intOperation);
276-
}
277-
278-
@CheckForNull
279-
private static Object resolveOr(BinaryExpressionTree binaryExpression) {
280-
Object left = resolveAsConstant(binaryExpression.leftOperand());
281-
Object right = resolveAsConstant(binaryExpression.rightOperand());
282-
if (left == null || right == null) {
283-
return null;
284-
} else if (left instanceof Long leftLong && right instanceof Long rightLong) {
285-
return leftLong | rightLong;
286-
} else if (left instanceof Long leftLong && right instanceof Integer rightInt) {
287-
return leftLong | rightInt;
288-
} else if (left instanceof Integer leftInt && right instanceof Long rightLong) {
289-
return leftInt | rightLong;
290-
} else if (left instanceof Integer leftInt && right instanceof Integer rightInt) {
291-
return leftInt | rightInt;
292-
}
293-
return null;
294-
}
295-
296140
}

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SELiteralUtils.java

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -33,63 +33,6 @@ private SELiteralUtils() {
3333
// This class only contains static methods
3434
}
3535

36-
@CheckForNull
37-
public static Long longLiteralValue(ExpressionTree tree) {
38-
ExpressionTree expression = tree;
39-
40-
int sign = tree.is(Kind.UNARY_MINUS) ? -1 : 1;
41-
if (tree.is(Kind.UNARY_MINUS, Kind.UNARY_PLUS)) {
42-
expression = ((UnaryExpressionTree) tree).expression();
43-
}
44-
45-
if (expression.is(Kind.INT_LITERAL, Kind.LONG_LITERAL)) {
46-
String value = trimLongSuffix(((LiteralTree) expression).value());
47-
// long as hexadecimal can be written using underscore to separate groups
48-
value = value.replace("_", "");
49-
try {
50-
if (value.startsWith("0b") || value.startsWith("0B")) {
51-
return sign * Long.valueOf(value.substring(2), 2);
52-
}
53-
return sign * Long.decode(value);
54-
} catch (NumberFormatException e) {
55-
// Long.decode() may fail in case of very large long number written in hexadecimal. In such situation, we ignore the number.
56-
// Note that Long.MAX_VALUE = "0x7FFF_FFFF_FFFF_FFFFL", but it is possible to write larger numbers in hexadecimal
57-
// to be used as mask in bitwise operation. For instance:
58-
// 0x8000_0000_0000_0000L (MAX_VALUE + 1),
59-
// 0xFFFF_FFFF_FFFF_FFFFL (only ones),
60-
// 0xFFFF_FFFF_FFFF_FFFEL (only ones except least significant bit), ...
61-
}
62-
}
63-
return null;
64-
}
65-
66-
private static Integer intLiteralValue(LiteralTree literal) {
67-
String literalValue = literal.value().replace("_", "");
68-
if (literalValue.startsWith("0b") || literalValue.startsWith("0B")) {
69-
// assume it is used as bit mask
70-
return Integer.parseUnsignedInt(literalValue.substring(2), 2);
71-
}
72-
return Long.decode(literalValue).intValue();
73-
}
74-
75-
@CheckForNull
76-
public static Integer intLiteralValue(ExpressionTree expression) {
77-
if (expression.is(Kind.INT_LITERAL)) {
78-
return intLiteralValue((LiteralTree) expression);
79-
}
80-
if (expression.is(Kind.UNARY_MINUS, Kind.UNARY_PLUS)) {
81-
UnaryExpressionTree unaryExp = (UnaryExpressionTree) expression;
82-
Integer subExpressionIntValue = intLiteralValue(unaryExp.expression());
83-
return expression.is(Kind.UNARY_MINUS) ? minus(subExpressionIntValue) : subExpressionIntValue;
84-
}
85-
return null;
86-
}
87-
88-
@CheckForNull
89-
private static Integer minus(@Nullable Integer nullableInteger) {
90-
return nullableInteger == null ? null : -nullableInteger;
91-
}
92-
9336
public static boolean isTrue(Tree tree) {
9437
return tree.is(Kind.BOOLEAN_LITERAL) && "true".equals(((LiteralTree) tree).value());
9538
}
@@ -98,72 +41,4 @@ public static boolean isFalse(Tree tree) {
9841
return tree.is(Kind.BOOLEAN_LITERAL) && "false".equals(((LiteralTree) tree).value());
9942
}
10043

101-
public static String trimQuotes(String value) {
102-
int delimiterLength = isTextBlock(value) ? 3 : 1;
103-
return value.substring(delimiterLength, value.length() - delimiterLength);
104-
}
105-
106-
public static boolean isTextBlock(String value) {
107-
return value.startsWith("\"\"\"");
108-
}
109-
110-
public static String trimLongSuffix(String longString) {
111-
if (StringUtils.isBlank(longString)) {
112-
return longString;
113-
}
114-
int lastCharPosition = longString.length() - 1;
115-
char lastChar = longString.charAt(lastCharPosition);
116-
String value = longString;
117-
if (lastChar == 'L' || lastChar == 'l') {
118-
value = longString.substring(0, lastCharPosition);
119-
}
120-
return value;
121-
}
122-
123-
public static int indentationOfTextBlock(String[] lines) {
124-
return Arrays.stream(lines).skip(1)
125-
.filter(SELiteralUtils::isNonEmptyLine)
126-
.mapToInt(SELiteralUtils::getIndentation)
127-
.min().orElse(0);
128-
}
129-
130-
private static String stripIndent(int indent, String s) {
131-
return s.isEmpty() ? s : s.substring(indent);
132-
}
133-
134-
public static String getAsStringValue(LiteralTree tree) {
135-
if (!tree.is(Kind.TEXT_BLOCK)) {
136-
return tree.is(Kind.STRING_LITERAL) ? trimQuotes(tree.value()) : tree.value();
137-
}
138-
String[] lines = tree.value().split("\r?\n");
139-
int indent = indentationOfTextBlock(lines);
140-
141-
return Arrays.stream(lines)
142-
.skip(1)
143-
.map(String::stripTrailing)
144-
.map(s -> stripIndent(indent, s))
145-
.collect(Collectors.joining("\n"))
146-
.replaceAll("\"\"\"$", "");
147-
}
148-
149-
private static int getIndentation(String line) {
150-
for (int i = 0; i < line.length(); ++i) {
151-
if (isNotWhiteSpace(line.charAt(i))) {
152-
return i;
153-
}
154-
}
155-
return line.length();
156-
}
157-
158-
/**
159-
* @return Whether c is not a white space character according to the space-stripping rules of text blocks, i.e. whether
160-
* it's a space, tab or form feed
161-
*/
162-
private static boolean isNotWhiteSpace(int c) {
163-
return c != ' ' && c != '\t' && c != '\f';
164-
}
165-
166-
private static boolean isNonEmptyLine(String line) {
167-
return line.chars().anyMatch(SELiteralUtils::isNotWhiteSpace);
168-
}
16944
}

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/DivisionByZeroCheck.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -395,28 +395,27 @@ public void visitIdentifier(IdentifierTree identifier) {
395395
}
396396
Type type = identifier.symbolType();
397397
if (type.isPrimitive() || type.isPrimitiveWrapper()) {
398-
((Symbol.VariableSymbol) symbol).constantValue()
399-
.filter(Number.class::isInstance)
400-
.map(Number.class::cast)
401-
.ifPresent(num -> {
402-
if (isZero(num, symbol.type().fullyQualifiedName())) {
403-
addZeroConstraint(sv, ZeroConstraint.ZERO);
404-
} else {
405-
addZeroConstraint(sv, ZeroConstraint.NON_ZERO);
406-
}
407-
});
398+
Object constantValue = ((Symbol.VariableSymbol) symbol).constantValue().orElse(null);
399+
if (constantValue instanceof Character ch) {
400+
constantValue = (int) ch.charValue();
401+
}
402+
if (constantValue instanceof Number num) {
403+
if (isZero(num, symbol.type().fullyQualifiedName())) {
404+
addZeroConstraint(sv, ZeroConstraint.ZERO);
405+
} else {
406+
addZeroConstraint(sv, ZeroConstraint.NON_ZERO);
407+
}
408+
}
408409
}
409410
}
410411

411412
private static boolean isZero(Number number, String type) {
412413
switch (type) {
413-
case "char":
414-
return number.intValue() == 0;
415414
case "byte":
416415
return number.byteValue() == 0;
417416
case "short":
418417
return number.shortValue() == 0;
419-
case "int":
418+
case "int", "char":
420419
return number.intValue() == 0;
421420
case "long":
422421
return number.longValue() == 0L;

0 commit comments

Comments
 (0)