diff --git a/java-symbolic-execution/java-symbolic-execution-checks-test-sources/src/main/java/symbolicexecution/checks/MinMaxRangeCheckSample.java b/java-symbolic-execution/java-symbolic-execution-checks-test-sources/src/main/java/symbolicexecution/checks/MinMaxRangeCheckSample.java index ba93e791e..dbea26e6f 100644 --- a/java-symbolic-execution/java-symbolic-execution-checks-test-sources/src/main/java/symbolicexecution/checks/MinMaxRangeCheckSample.java +++ b/java-symbolic-execution/java-symbolic-execution-checks-test-sources/src/main/java/symbolicexecution/checks/MinMaxRangeCheckSample.java @@ -51,14 +51,14 @@ public double doRangeCheckNOK7(double num) { double upper = 18.37; double lower = -4.96; double result = Math.min(lower, num); - return Math.max(result, upper); // Compliant - FN + return Math.max(result, upper); // Noncompliant } public double doRangeCheckNOK8(float num) { float upper = 18.37f; float lower = -4.96f; float result = Math.min(lower, num); - return Math.max(result, upper); // Compliant - FN + return Math.max(result, upper); // Noncompliant } public double doRangeCheckNOK9(long num) { @@ -98,11 +98,11 @@ public int doRangeCheckOK5(int num) { // using both time same range return Math.max(LOWER_INT, result); } - public int doRangeCheckOK6(int num) { // do not handle arithmetic + public int doRangeCheckOK6(int num) { int upper = 1837 + 14; int lower = -496 * 42; int result = Math.min(lower, num); - return Math.max(result, upper); + return Math.max(result, upper); // Noncompliant } public float doRangeCheckIntFloatNOK(int num) { diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SEExpressionUtils.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SEExpressionUtils.java index fbeb00c16..5344f6ae6 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SEExpressionUtils.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SEExpressionUtils.java @@ -17,26 +17,16 @@ package org.sonar.java.model; import java.util.Optional; -import java.util.function.BiFunction; -import javax.annotation.CheckForNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; -import org.sonar.plugins.java.api.tree.BinaryExpressionTree; import org.sonar.plugins.java.api.tree.ExpressionTree; import org.sonar.plugins.java.api.tree.IdentifierTree; -import org.sonar.plugins.java.api.tree.LiteralTree; import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.ParenthesizedTree; import org.sonar.plugins.java.api.tree.Tree; -import org.sonar.plugins.java.api.tree.UnaryExpressionTree; public final class SEExpressionUtils { - private static final Logger LOG = LoggerFactory.getLogger(SEExpressionUtils.class); - private SEExpressionUtils() { } @@ -125,71 +115,6 @@ public static boolean isNullLiteral(ExpressionTree tree) { return skipParentheses(tree).is(Tree.Kind.NULL_LITERAL); } - @CheckForNull - public static Object resolveAsConstant(ExpressionTree tree) { - ExpressionTree expression = tree; - while (expression.is(Tree.Kind.PARENTHESIZED_EXPRESSION)) { - expression = ((ParenthesizedTree) expression).expression(); - } - if (expression.is(Tree.Kind.MEMBER_SELECT)) { - expression = ((MemberSelectExpressionTree) expression).identifier(); - } - if (expression.is(Tree.Kind.IDENTIFIER)) { - return resolveIdentifier((IdentifierTree) expression); - } - if (expression.is(Tree.Kind.BOOLEAN_LITERAL)) { - return Boolean.parseBoolean(((LiteralTree) expression).value()); - } - if (expression.is(Tree.Kind.STRING_LITERAL, Tree.Kind.TEXT_BLOCK)) { - return SELiteralUtils.getAsStringValue((LiteralTree) expression); - } - if (expression instanceof UnaryExpressionTree unaryExpressionTree) { - return resolveUnaryExpression(unaryExpressionTree); - } - if (expression.is(Tree.Kind.INT_LITERAL)) { - return SELiteralUtils.intLiteralValue(expression); - } - if (expression.is(Tree.Kind.LONG_LITERAL)) { - return SELiteralUtils.longLiteralValue(expression); - } - if (expression.is(Tree.Kind.PLUS)) { - return resolvePlus((BinaryExpressionTree) expression); - } - if (expression.is(Tree.Kind.OR)) { - return resolveOr((BinaryExpressionTree) expression); - } - if (expression.is(Tree.Kind.MINUS)) { - return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a - b, (a, b) -> a - b); - } - if (expression.is(Tree.Kind.MULTIPLY)) { - return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a * b, (a, b) -> a * b); - } - if (expression.is(Tree.Kind.DIVIDE)) { - return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a / b, (a, b) -> a / b); - } - if (expression.is(Tree.Kind.REMAINDER)) { - return resolveArithmeticOperation((BinaryExpressionTree) expression, (a, b) -> a % b, (a, b) -> a % b); - } - return null; - } - - @CheckForNull - private static Object resolveIdentifier(IdentifierTree tree) { - Symbol symbol = tree.symbol(); - if (!symbol.isVariableSymbol()) { - return null; - } - Symbol owner = symbol.owner(); - if (owner.isTypeSymbol() && owner.type().is("java.lang.Boolean")) { - if ("TRUE".equals(symbol.name())) { - return Boolean.TRUE; - } else if ("FALSE".equals(symbol.name())) { - return Boolean.FALSE; - } - } - return ((Symbol.VariableSymbol) symbol).constantValue().orElse(null); - } - public static IdentifierTree extractIdentifier(AssignmentExpressionTree tree) { Optional identifier = extractIdentifier(tree.variable()); @@ -212,85 +137,4 @@ public static boolean isThis(ExpressionTree expression) { return newExpression.is(Tree.Kind.IDENTIFIER) && "this".equals(((IdentifierTree) newExpression).name()); } - @CheckForNull - private static Object resolveArithmeticOperation(Object left, Object right, BiFunction longOperation, BiFunction intOperation) { - try { - if (left instanceof Integer leftInt && right instanceof Integer rightInt) { - return intOperation.apply(leftInt, rightInt); - } else if ((left instanceof Long || right instanceof Long) && (left instanceof Integer || right instanceof Integer)) { - return longOperation.apply(((Number) left).longValue(), ((Number) right).longValue()); - } - } catch (ArithmeticException e) { - LOG.debug("Arithmetic exception while resolving arithmetic operation value", e); - } - return null; - } - - @CheckForNull - private static Object resolveUnaryExpression(UnaryExpressionTree unaryExpression) { - Object value = resolveAsConstant(unaryExpression.expression()); - if (unaryExpression.is(Tree.Kind.UNARY_PLUS)) { - return value; - } else if (unaryExpression.is(Tree.Kind.UNARY_MINUS)) { - if (value instanceof Long longValue) { - return -longValue; - } else if (value instanceof Integer intValue) { - return -intValue; - } - } else if (unaryExpression.is(Tree.Kind.BITWISE_COMPLEMENT)) { - if (value instanceof Long longValue) { - return ~longValue; - } else if (value instanceof Integer intValue) { - return ~intValue; - } - } else if (unaryExpression.is(Tree.Kind.LOGICAL_COMPLEMENT) && value instanceof Boolean bool) { - return !bool; - } - return null; - } - - @CheckForNull - private static Object resolvePlus(BinaryExpressionTree binaryExpression) { - Object left = resolveAsConstant(binaryExpression.leftOperand()); - Object right = resolveAsConstant(binaryExpression.rightOperand()); - if (left == null || right == null) { - return null; - } else if (left instanceof String leftString) { - return leftString + right; - } else if (right instanceof String rightString) { - return left + rightString; - } - return resolveArithmeticOperation(left, right, Long::sum, Integer::sum); - } - - @CheckForNull - private static Object resolveArithmeticOperation(BinaryExpressionTree binaryExpression, - BiFunction longOperation, - BiFunction intOperation) { - Object left = resolveAsConstant(binaryExpression.leftOperand()); - Object right = resolveAsConstant(binaryExpression.rightOperand()); - if (left == null || right == null) { - return null; - } - return resolveArithmeticOperation(left, right, longOperation, intOperation); - } - - @CheckForNull - private static Object resolveOr(BinaryExpressionTree binaryExpression) { - Object left = resolveAsConstant(binaryExpression.leftOperand()); - Object right = resolveAsConstant(binaryExpression.rightOperand()); - if (left == null || right == null) { - return null; - } else if (left instanceof Long leftLong && right instanceof Long rightLong) { - return leftLong | rightLong; - } else if (left instanceof Long leftLong && right instanceof Integer rightInt) { - return leftLong | rightInt; - } else if (left instanceof Integer leftInt && right instanceof Long rightLong) { - return leftInt | rightLong; - } else if (left instanceof Integer leftInt && right instanceof Integer rightInt) { - return leftInt | rightInt; - } - return null; - } - } diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SELiteralUtils.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SELiteralUtils.java index 59c167086..1df62e0cc 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SELiteralUtils.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/model/SELiteralUtils.java @@ -33,63 +33,6 @@ private SELiteralUtils() { // This class only contains static methods } - @CheckForNull - public static Long longLiteralValue(ExpressionTree tree) { - ExpressionTree expression = tree; - - int sign = tree.is(Kind.UNARY_MINUS) ? -1 : 1; - if (tree.is(Kind.UNARY_MINUS, Kind.UNARY_PLUS)) { - expression = ((UnaryExpressionTree) tree).expression(); - } - - if (expression.is(Kind.INT_LITERAL, Kind.LONG_LITERAL)) { - String value = trimLongSuffix(((LiteralTree) expression).value()); - // long as hexadecimal can be written using underscore to separate groups - value = value.replace("_", ""); - try { - if (value.startsWith("0b") || value.startsWith("0B")) { - return sign * Long.valueOf(value.substring(2), 2); - } - return sign * Long.decode(value); - } catch (NumberFormatException e) { - // Long.decode() may fail in case of very large long number written in hexadecimal. In such situation, we ignore the number. - // Note that Long.MAX_VALUE = "0x7FFF_FFFF_FFFF_FFFFL", but it is possible to write larger numbers in hexadecimal - // to be used as mask in bitwise operation. For instance: - // 0x8000_0000_0000_0000L (MAX_VALUE + 1), - // 0xFFFF_FFFF_FFFF_FFFFL (only ones), - // 0xFFFF_FFFF_FFFF_FFFEL (only ones except least significant bit), ... - } - } - return null; - } - - private static Integer intLiteralValue(LiteralTree literal) { - String literalValue = literal.value().replace("_", ""); - if (literalValue.startsWith("0b") || literalValue.startsWith("0B")) { - // assume it is used as bit mask - return Integer.parseUnsignedInt(literalValue.substring(2), 2); - } - return Long.decode(literalValue).intValue(); - } - - @CheckForNull - public static Integer intLiteralValue(ExpressionTree expression) { - if (expression.is(Kind.INT_LITERAL)) { - return intLiteralValue((LiteralTree) expression); - } - if (expression.is(Kind.UNARY_MINUS, Kind.UNARY_PLUS)) { - UnaryExpressionTree unaryExp = (UnaryExpressionTree) expression; - Integer subExpressionIntValue = intLiteralValue(unaryExp.expression()); - return expression.is(Kind.UNARY_MINUS) ? minus(subExpressionIntValue) : subExpressionIntValue; - } - return null; - } - - @CheckForNull - private static Integer minus(@Nullable Integer nullableInteger) { - return nullableInteger == null ? null : -nullableInteger; - } - public static boolean isTrue(Tree tree) { return tree.is(Kind.BOOLEAN_LITERAL) && "true".equals(((LiteralTree) tree).value()); } @@ -98,72 +41,4 @@ public static boolean isFalse(Tree tree) { return tree.is(Kind.BOOLEAN_LITERAL) && "false".equals(((LiteralTree) tree).value()); } - public static String trimQuotes(String value) { - int delimiterLength = isTextBlock(value) ? 3 : 1; - return value.substring(delimiterLength, value.length() - delimiterLength); - } - - public static boolean isTextBlock(String value) { - return value.startsWith("\"\"\""); - } - - public static String trimLongSuffix(String longString) { - if (StringUtils.isBlank(longString)) { - return longString; - } - int lastCharPosition = longString.length() - 1; - char lastChar = longString.charAt(lastCharPosition); - String value = longString; - if (lastChar == 'L' || lastChar == 'l') { - value = longString.substring(0, lastCharPosition); - } - return value; - } - - public static int indentationOfTextBlock(String[] lines) { - return Arrays.stream(lines).skip(1) - .filter(SELiteralUtils::isNonEmptyLine) - .mapToInt(SELiteralUtils::getIndentation) - .min().orElse(0); - } - - private static String stripIndent(int indent, String s) { - return s.isEmpty() ? s : s.substring(indent); - } - - public static String getAsStringValue(LiteralTree tree) { - if (!tree.is(Kind.TEXT_BLOCK)) { - return tree.is(Kind.STRING_LITERAL) ? trimQuotes(tree.value()) : tree.value(); - } - String[] lines = tree.value().split("\r?\n"); - int indent = indentationOfTextBlock(lines); - - return Arrays.stream(lines) - .skip(1) - .map(String::stripTrailing) - .map(s -> stripIndent(indent, s)) - .collect(Collectors.joining("\n")) - .replaceAll("\"\"\"$", ""); - } - - private static int getIndentation(String line) { - for (int i = 0; i < line.length(); ++i) { - if (isNotWhiteSpace(line.charAt(i))) { - return i; - } - } - return line.length(); - } - - /** - * @return Whether c is not a white space character according to the space-stripping rules of text blocks, i.e. whether - * it's a space, tab or form feed - */ - private static boolean isNotWhiteSpace(int c) { - return c != ' ' && c != '\t' && c != '\f'; - } - - private static boolean isNonEmptyLine(String line) { - return line.chars().anyMatch(SELiteralUtils::isNotWhiteSpace); - } } diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/DivisionByZeroCheck.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/DivisionByZeroCheck.java index ff2058338..c0d5534ac 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/DivisionByZeroCheck.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/DivisionByZeroCheck.java @@ -395,28 +395,27 @@ public void visitIdentifier(IdentifierTree identifier) { } Type type = identifier.symbolType(); if (type.isPrimitive() || type.isPrimitiveWrapper()) { - ((Symbol.VariableSymbol) symbol).constantValue() - .filter(Number.class::isInstance) - .map(Number.class::cast) - .ifPresent(num -> { - if (isZero(num, symbol.type().fullyQualifiedName())) { - addZeroConstraint(sv, ZeroConstraint.ZERO); - } else { - addZeroConstraint(sv, ZeroConstraint.NON_ZERO); - } - }); + Object constantValue = ((Symbol.VariableSymbol) symbol).constantValue().orElse(null); + if (constantValue instanceof Character ch) { + constantValue = (int) ch.charValue(); + } + if (constantValue instanceof Number num) { + if (isZero(num, symbol.type().fullyQualifiedName())) { + addZeroConstraint(sv, ZeroConstraint.ZERO); + } else { + addZeroConstraint(sv, ZeroConstraint.NON_ZERO); + } + } } } private static boolean isZero(Number number, String type) { switch (type) { - case "char": - return number.intValue() == 0; case "byte": return number.byteValue() == 0; case "short": return number.shortValue() == 0; - case "int": + case "int", "char": return number.intValue() == 0; case "long": return number.longValue() == 0L; diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/MinMaxRangeCheck.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/MinMaxRangeCheck.java index cb4b79ce1..0d10a970f 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/MinMaxRangeCheck.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/MinMaxRangeCheck.java @@ -23,7 +23,6 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.java.model.SELiteralUtils; import org.sonar.java.se.CheckerContext; import org.sonar.java.se.Flow; import org.sonar.java.se.ProgramState; @@ -32,14 +31,9 @@ import org.sonar.java.se.symbolicvalues.SymbolicValue; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.semantic.MethodMatchers; -import org.sonar.plugins.java.api.semantic.Symbol; -import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.ExpressionTree; -import org.sonar.plugins.java.api.tree.IdentifierTree; -import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; -import org.sonar.plugins.java.api.tree.UnaryExpressionTree; import static org.sonar.plugins.java.api.semantic.MethodMatchers.ANY; @@ -138,45 +132,20 @@ public ProgramState checkPreStatement(CheckerContext context, Tree syntaxNode) { @Override public ProgramState checkPostStatement(CheckerContext context, Tree syntaxNode) { - // TODO handle float and double - switch (syntaxNode.kind()) { - case INT_LITERAL: - return handleNumericalLiteral(context, SELiteralUtils.intLiteralValue((ExpressionTree) syntaxNode)); - case LONG_LITERAL: - return handleNumericalLiteral(context, SELiteralUtils.longLiteralValue((ExpressionTree) syntaxNode)); - case UNARY_MINUS, - UNARY_PLUS: - return handleNumericalLiteral(context, (UnaryExpressionTree) syntaxNode); - case IDENTIFIER: - return handleNumericalConstant(context, (IdentifierTree) syntaxNode); - case MEMBER_SELECT: - return handleNumericalConstant(context, ((MemberSelectExpressionTree) syntaxNode).identifier()); - case METHOD_INVOCATION: - return handleMinMaxInvocation(context, (MethodInvocationTree) syntaxNode); - default: - return context.getState(); - } - } - - private static ProgramState handleNumericalConstant(CheckerContext context, IdentifierTree syntaxNode) { - ProgramState programState = context.getState(); - Symbol identifier = syntaxNode.symbol(); - if (!isNumericalConstant(identifier)) { - return programState; - } - SymbolicValue constant = programState.getValue(identifier); - if (constant == null) { - return programState; + if (syntaxNode instanceof ExpressionTree expressionTree) { + Object constant = expressionTree.asConstant().orElse(null); + if (constant instanceof Character ch) { + constant = (int) ch; + } + if (constant instanceof Number number) { + return handleNumericalLiteral(context, number); + } } - NumericalConstraint numericalConstraint = programState.getConstraint(constant, NumericalConstraint.class); - if (numericalConstraint == null) { - return ((Symbol.VariableSymbol) identifier).constantValue() - .filter(Number.class::isInstance) - .map(Number.class::cast) - .map(value -> programState.addConstraint(constant, new NumericalConstraint(value))) - .orElse(programState); + if (syntaxNode instanceof MethodInvocationTree methodInvocation) { + return handleMinMaxInvocation(context, methodInvocation); + } else { + return context.getState(); } - return programState; } private ProgramState handleMinMaxInvocation(CheckerContext context, MethodInvocationTree syntaxNode) { @@ -243,41 +212,4 @@ private static ProgramState handleNumericalLiteral(CheckerContext context, @Null return programState.addConstraint(programState.peekValue(), new NumericalConstraint(value)); } - private static ProgramState handleNumericalLiteral(CheckerContext context, UnaryExpressionTree syntaxNode) { - ProgramState previousPS = context.getNode().programState; - NumericalConstraint knownNumericalConstraint = previousPS.getConstraint(previousPS.peekValue(), NumericalConstraint.class); - - ProgramState programState = context.getState(); - if (knownNumericalConstraint == null) { - return programState; - } - if (syntaxNode.is(Tree.Kind.UNARY_PLUS)) { - return programState.addConstraint(programState.peekValue(), knownNumericalConstraint); - } - Number value = knownNumericalConstraint.value; - if (value instanceof Integer) { - value = -1 * value.intValue(); - } else if (value instanceof Long) { - value = -1L * value.longValue(); - } else if (value instanceof Float) { - value = -1.0f * value.floatValue(); - } else { - value = -1.0 * value.doubleValue(); - } - return programState.addConstraint(programState.peekValue(), new NumericalConstraint(value)); - } - - private static boolean isNumericalConstant(@Nullable Symbol symbol) { - return symbol != null && isConstant(symbol) && isNumericalPrimitive(symbol); - } - - private static boolean isNumericalPrimitive(Symbol symbol) { - Type type = symbol.type(); - return type.isPrimitive() && type.isNumerical(); - } - - private static boolean isConstant(Symbol symbol) { - return symbol.isVariableSymbol() && symbol.isStatic() && symbol.isFinal(); - } - } diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SEExpressionUtilsTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SEExpressionUtilsTest.java index 786139a29..f358ae081 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SEExpressionUtilsTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SEExpressionUtilsTest.java @@ -172,8 +172,15 @@ void resolve_as_string_constant() { } @Test - void resolve_as_constant_not_yet_supported() { - assertResolveAsConstant("true || true", null); + void resolve_as_constant_conditional_expression() { + assertResolveAsConstant("true || true", true); + assertResolveAsConstant("false || true", true); + assertResolveAsConstant("true || false", true); + assertResolveAsConstant("false || false", false); + assertResolveAsConstant("true && true", true); + assertResolveAsConstant("false && true", false); + assertResolveAsConstant("true && false", false); + assertResolveAsConstant("false && false", false); } @Test @@ -192,15 +199,15 @@ void resolve_as_constant_arithmetic_operations() { void resolve_as_constant_division_by_zero() { assertResolveAsConstant("5 / 0", null); assertResolveAsConstant("5L / 0", null); - assertResolveAsConstant("5D / 0", null); + assertResolveAsConstant("5D / 0", Double.POSITIVE_INFINITY); assertResolveAsConstant("5 / 0L", null); assertResolveAsConstant("5L / 0L", null); - assertResolveAsConstant("5D / 0L", null); + assertResolveAsConstant("5D / 0L", Double.POSITIVE_INFINITY); - assertResolveAsConstant("5 / 0D", null); - assertResolveAsConstant("5L / 0D", null); - assertResolveAsConstant("5D / 0D", null); + assertResolveAsConstant("5 / 0D", Double.POSITIVE_INFINITY); + assertResolveAsConstant("5L / 0D", Double.POSITIVE_INFINITY); + assertResolveAsConstant("5D / 0D", Double.POSITIVE_INFINITY); } @Test @@ -216,7 +223,7 @@ void resolve_as_constant_unknown_symbol() { private void assertResolveAsConstant(String code, @Nullable Object expected) { CompilationUnitTree unit = JParserTestUtils.parse("class A { Object f = " + code + "; }"); ExpressionTree expression = ((VariableTree) ((ClassTree) unit.types().get(0)).members().get(0)).initializer(); - Object actual = SEExpressionUtils.resolveAsConstant(expression); + Object actual = expression.asConstant().orElse(null); if (expected == null) { assertThat(actual).isNull(); } else { diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SELiteralUtilsTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SELiteralUtilsTest.java index 7882b0e61..f2e7fa772 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SELiteralUtilsTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/model/SELiteralUtilsTest.java @@ -102,16 +102,21 @@ void private_constructor() throws Exception { @Test void test_int_and_long_value() { Integer[] expectedIntegerValues = {42, -7, 3, null, null, 0xff, 0b0100, 5678, 0xFF, 0b1100110, 0xff000000}; - Long[] expectedLongValues = {42L, 42L, -7L, -7L, +3L, +3L, null, null, 0xFFL, null, null, null, - Long.MAX_VALUE, Long.MAX_VALUE, 0b11010010_01101001_10010100_10010010L, 10010L, 0xFFL, 0b1100110L}; + Long[] expectedLongValues = {42L, 42L, -7L, -7L, +3L, +3L, null, null, 0xFFL, -1L, -2L, -9223372036854775808L, + Long.MAX_VALUE, Long.MAX_VALUE, (long) 0b11010010_01101001_10010100_10010010, 10010L, 0xFFL, 0b1100110L}; int i = 0; int j = 0; for (VariableTree variableTree : variables) { - if (variableTree.simpleName().name().startsWith("x")) { - assertThat(SELiteralUtils.intLiteralValue(variableTree.initializer())).isEqualTo(expectedIntegerValues[i++]); - } else if (variableTree.simpleName().name().startsWith("y")) { - assertThat(SELiteralUtils.longLiteralValue(variableTree.initializer())).isEqualTo(expectedLongValues[j++]); + String variableName = variableTree.simpleName().name(); + if (variableName.startsWith("x")) { + assertThat(variableTree.initializer().asConstant().orElse(null)) + .describedAs(variableName) + .isEqualTo(expectedIntegerValues[i++]); + } else if (variableName.startsWith("y")) { + assertThat(variableTree.initializer().asConstant().map(y -> ((Number) y).longValue()).orElse(null)) + .describedAs(variableName) + .isEqualTo(expectedLongValues[j++]); } } } @@ -122,43 +127,22 @@ void test_int_and_long_value() { @Test void testLargeBinary() { // 32 bit masks - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0b1111_1111_1111_1111_0000_0000_0000_0000"))).isEqualTo(0b1111_1111_1111_1111_0000_0000_0000_0000); - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0b0111_1111_1111_1111_0000_0000_0000_0000"))).isEqualTo(0b0111_1111_1111_1111_0000_0000_0000_0000); + assertThat(getIntLiteral("0b1111_1111_1111_1111_0000_0000_0000_0000").asConstant().orElse(null)).isEqualTo(0b1111_1111_1111_1111_0000_0000_0000_0000); + assertThat(getIntLiteral("0b0111_1111_1111_1111_0000_0000_0000_0000").asConstant().orElse(null)).isEqualTo(0b0111_1111_1111_1111_0000_0000_0000_0000); // 32 bits numbers padded with zeros - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0b0000_1111_1111_1111_1111_0000_0000_0000_0000"))).isEqualTo(0b0000_1111_1111_1111_1111_0000_0000_0000_0000); - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0x00FFF0000"))).isEqualTo(0x00FFF0000); + assertThat(getIntLiteral("0b0000_1111_1111_1111_1111_0000_0000_0000_0000").asConstant().orElse(null)).isEqualTo(0b0000_1111_1111_1111_1111_0000_0000_0000_0000); + assertThat(getIntLiteral("0x00FFF0000").asConstant().orElse(null)).isEqualTo(0x00FFF0000); // hexa - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0xFFFF0000"))).isEqualTo(0xFFFF0000); - assertThat(SELiteralUtils.intLiteralValue(getIntLiteral("0x7FFF0000"))).isEqualTo(0x7FFF0000); + assertThat(getIntLiteral("0xFFFF0000").asConstant().orElse(null)).isEqualTo(0xFFFF0000); + assertThat(getIntLiteral("0x7FFF0000").asConstant().orElse(null)).isEqualTo(0x7FFF0000); } private ExpressionTree getIntLiteral(String intLiteral) { return ((BinaryExpressionTree) getReturnExpression("int foo() { return " + intLiteral + " & 42; }")).leftOperand(); } - @Test - void testTrimLongSuffix() { - assertThat(SELiteralUtils.trimLongSuffix("")).isEmpty(); - String longValue = "12345"; - assertThat(SELiteralUtils.trimLongSuffix(longValue)).isEqualTo(longValue); - assertThat(SELiteralUtils.trimLongSuffix(longValue + "l")).isEqualTo(longValue); - assertThat(SELiteralUtils.trimLongSuffix(longValue + "L")).isEqualTo(longValue); - } - - @Test - void testTrimQuotes() { - assertThat(SELiteralUtils.trimQuotes("\"test\"")).isEqualTo("test"); - assertThat(SELiteralUtils.trimQuotes("\"\"\"test\"\"\"")).isEqualTo("test"); - } - - @Test - void testIsTextBlock() { - assertThat(SELiteralUtils.isTextBlock("\"test\"")).isFalse(); - assertThat(SELiteralUtils.isTextBlock("\"\"\"test\"\"\"")).isTrue(); - } - @Test void isTrue_withNonBooleanLiteral_returnsFalse() { ExpressionTree tree = getFirstExpression("void foo(java.util.Properties props){ props.setProperty(\"myKey\", \"myValue\"); }"); @@ -195,62 +179,6 @@ void isFalse_withExpectedValue_returnsTrue() { assertThat(SELiteralUtils.isFalse(falseTree)).isTrue(); } - @Test - void getAsStringValue_for_string() { - - LiteralTree intLiteral = getLiteral("123"); - assertThat(SELiteralUtils.getAsStringValue(intLiteral)).isEqualTo("123"); - - LiteralTree stringLiteral = getLiteral("\"ABC\""); - assertThat(SELiteralUtils.getAsStringValue(stringLiteral)).isEqualTo("ABC"); - - LiteralTree textBlock = getLiteral("\"\"\"\nABC\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlock)).isEqualTo("ABC"); - - LiteralTree multilineString = getLiteral("\"ABC\\nABC\""); - assertThat(SELiteralUtils.getAsStringValue(multilineString)).isEqualTo("ABC\\nABC"); - - LiteralTree multilineTB = getLiteral("\"\"\"\n ABC\n ABC\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(multilineTB)).isEqualTo("ABC\nABC"); - - LiteralTree multilineIndentInTB = getLiteral("\"\"\"\n ABC\n ABC\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(multilineIndentInTB)).isEqualTo(" ABC\nABC"); - - LiteralTree textBlockWithTab = getLiteral("\"\"\"\n \tABC\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithTab)).isEqualTo("ABC"); - - LiteralTree textBlockWithEmptyLines = getLiteral("\"\"\"\n\n\n \tABC\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithEmptyLines)).isEqualTo("\n\nABC"); - - LiteralTree textBlockWithNewLines = getLiteral("\"\"\"\n\n\n \tABC\\n\"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithNewLines)).isEqualTo("\n\nABC\\n"); - - LiteralTree textBlockWithTrailingSpaces = getLiteral("\"\"\"\n\n\n \tABC \"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithTrailingSpaces)).isEqualTo("\n\nABC "); - - LiteralTree textBlockWithTrailingAndLeadingSpaces = getLiteral("\"\"\"\n\n\n \tABC \n ABC ABC \"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithTrailingAndLeadingSpaces)).isEqualTo("\n\nABC\nABC ABC "); - - LiteralTree textBlockWithQuotesOnNewLine = getLiteral("\"\"\"\n ABC\n ABC\n \"\"\""); - assertThat(SELiteralUtils.getAsStringValue(textBlockWithQuotesOnNewLine)).isEqualTo(" ABC\n ABC\n"); - } - - @Test - void indentationOfTextBlock() { - String[] noIndentation = {"\"\"\"", "abc", "\"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(noIndentation)).isZero(); - String[] lastLineNotIndented = {"\"\"\"", " abc", "\"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(lastLineNotIndented)).isZero(); - String[] indented = {"\"\"\"", " abc", " \"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(indented)).isEqualTo(4); - String[] tabsAndFormFeeds = {"\"\"\"", "\t\tabc", "\f\f\"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(tabsAndFormFeeds)).isEqualTo(2); - String[] withEmptyLine = {"\"\"\"", " abc", "", " \"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(withEmptyLine)).isEqualTo(4); - String[] withIndentedEmptyLine = {"\"\"\"", " abc", " \t\f", " \"\"\""}; - assertThat(SELiteralUtils.indentationOfTextBlock(withIndentedEmptyLine)).isEqualTo(4); - } - private ExpressionTree getFirstExpression(String code) { ClassTree firstType = getClassTree(code); StatementTree firstStatement = ((MethodTree) firstType.members().get(0)).block().body().get(0); @@ -268,9 +196,4 @@ private ClassTree getClassTree(String code) { return (ClassTree) compilationUnitTree.types().get(0); } - private LiteralTree getLiteral(String code) { - ClassTree classTree = getClassTree("Object o = " + code + ";"); - return (LiteralTree) ((VariableTree) classTree.members().get(0)).initializer(); - } - } diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java index 1ac459e2d..1574b0408 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java @@ -168,7 +168,7 @@ void switchWithPatterns() { .onFile(TestUtils.nonCompilingTestSourcesPath("symbolicexecution/engine/SwitchWithPatterns.java")) .withChecks(seChecks()) .withClassPath(SETestUtils.CLASS_PATH) - .withJavaVersion(23, true) + .withJavaVersion(24, true) .verifyNoIssues(); } diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/JavaFrontendIntegrationTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/JavaFrontendIntegrationTest.java index a84b2361b..efd9a7df7 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/JavaFrontendIntegrationTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/JavaFrontendIntegrationTest.java @@ -55,6 +55,8 @@ import org.sonar.java.se.checks.SECheck; import org.sonar.java.se.utils.JParserTestUtils; import org.sonar.java.se.utils.SETestUtils; +import org.sonar.java.telemetry.NoOpTelemetry; +import org.sonar.java.telemetry.TelemetryKey; import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.cfg.ControlFlowGraph; @@ -125,7 +127,7 @@ void DefaultJavaFileScannerContext_should_report_se_issue_with_flow() { void JavaAstScanner_should_swallow_log_and_report_checks_exceptions_for_symbolic_execution() { VisitorsBridge visitorsBridge = visitorsBridgeWithSymbolicExecution(new SE3_ThrowingNPEInit()); - JavaAstScanner scanner = new JavaAstScanner(sonarComponents); + JavaAstScanner scanner = new JavaAstScanner(sonarComponents, new NoOpTelemetry(), TelemetryKey.JAVA_ANALYSIS_MAIN); scanner.setVisitorBridge(visitorsBridge); scanner.scan(Collections.singletonList(inputFile)); diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/filters/SuppressWarningFilterSeTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/filters/SuppressWarningFilterSeTest.java index b37f5a5bb..5ae27eca3 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/filters/SuppressWarningFilterSeTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/filters/SuppressWarningFilterSeTest.java @@ -109,7 +109,12 @@ public static void verify(String filename, JavaIssueFilter filter, SECheck... ex projectClasspath.add(new File("target/test-classes")); InputFile inputFile = TestUtils.inputFile(TEST_SOURCES_PATH + filename); - VisitorsBridgeForTests visitorsBridge = new VisitorsBridgeForTests(visitors, projectClasspath, sonarComponents(inputFile), new JavaVersionImpl()); + VisitorsBridgeForTests visitorsBridge = new VisitorsBridgeForTests.Builder(visitors) + .enableSemanticWithProjectClasspath(projectClasspath) + .withSonarComponents(sonarComponents(inputFile)) + .withJavaVersion(new JavaVersionImpl()) + .build(); + JavaAstScanner.scanSingleFileForTests(inputFile, visitorsBridge); JavaFileScannerContextForTests testJavaFileScannerContext = visitorsBridge.lastCreatedTestContext(); diff --git a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/symbolicvalues/RelationalSymbolicValueTest.java b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/symbolicvalues/RelationalSymbolicValueTest.java index 6bb8188dc..9a9cf988b 100644 --- a/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/symbolicvalues/RelationalSymbolicValueTest.java +++ b/java-symbolic-execution/java-symbolic-execution-plugin/src/test/java/org/sonar/java/se/symbolicvalues/RelationalSymbolicValueTest.java @@ -107,8 +107,8 @@ private RelationalSymbolicValue relationalSV(Tree.Kind kind, SymbolicValue... co List computedFromSymbols = Arrays.stream(computedFrom).map(sv -> new ProgramState.SymbolicValueSymbol(sv, null)) .toList(); return (RelationalSymbolicValue) constraintManager - .createBinarySymbolicValue(new BinaryExpressionTreeImpl(kind, mock(ExpressionTree.class), mock(InternalSyntaxToken.class), mock(ExpressionTree.class)), - computedFromSymbols); + .createBinarySymbolicValue(new BinaryExpressionTreeImpl(kind, mock(ExpressionTree.class), + mock(InternalSyntaxToken.class), mock(ExpressionTree.class), null), computedFromSymbols); } @Test diff --git a/pom.xml b/pom.xml index 60b269a9b..b33f06bc6 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ true - 8.15.0.39343 + 8.19.0.40185 25.1.0.102122