From 197e5bde5579834b90f89a9b3fb1cba99b199bf5 Mon Sep 17 00:00:00 2001 From: Alban Auzeill Date: Fri, 25 Jun 2021 11:32:20 +0200 Subject: [PATCH] Fix IndexOutOfBoundsException in MethodYield.getConstraint (#3669) --- .../java/org/sonar/java/model/JSymbol.java | 1 + .../org/sonar/java/model/JSymbolTest.java | 17 ++++++++++++++++ .../sonar/java/se/ExplodedGraphWalker.java | 11 +++++++++- .../files/se/RecordConstructorParameters.java | 20 +++++++++++++++++++ .../java/se/ExplodedGraphWalkerTest.java | 9 +++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 java-symbolic-execution/src/test/files/se/RecordConstructorParameters.java diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java index c0b8ea5f730..30f34d648d6 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java @@ -411,6 +411,7 @@ private TypeSymbol variableEnclosingClass(IVariableBinding variableBinding) { node = node.parent(); switch (node.kind()) { case CLASS: + case RECORD: case ENUM: // variable declaration in a static or instance initializer // or local variable declaration in recovered method diff --git a/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java b/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java index 0f5409ba1cb..594eef230c8 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java @@ -126,6 +126,23 @@ void enclosingClass() { .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); } + @Test + void record_enclosingClass() { + JavaTree.CompilationUnitTreeImpl cu = test("record C1(int f) { record C2(int p) { } }"); + ClassTreeImpl c1 = (ClassTreeImpl) cu.types().get(0); + VariableTreeImpl f = (VariableTreeImpl) c1.recordComponents().get(0); + ClassTreeImpl c2 = (ClassTreeImpl) c1.members().get(0); + VariableTreeImpl p = (VariableTreeImpl) c2.recordComponents().get(0); + + assertThat(cu.sema.variableSymbol(f.variableBinding)) + .as("of field") + .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + + assertThat(cu.sema.variableSymbol(p.variableBinding)) + .as("of method parameter") + .hasEnclosingClass(cu.sema.typeSymbol(c2.typeBinding)); + } + @Test void variable_in_class_initializer() { JavaTree.CompilationUnitTreeImpl cu = test("enum E { C; { int i; } }"); diff --git a/java-symbolic-execution/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java b/java-symbolic-execution/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java index 83a9f8ade30..0d725925175 100644 --- a/java-symbolic-execution/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java +++ b/java-symbolic-execution/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java @@ -72,6 +72,7 @@ import org.sonar.plugins.java.api.tree.BlockTree; import org.sonar.plugins.java.api.tree.CaseGroupTree; import org.sonar.plugins.java.api.tree.CaseLabelTree; +import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.ConditionalExpressionTree; import org.sonar.plugins.java.api.tree.DoWhileStatementTree; import org.sonar.plugins.java.api.tree.ExpressionTree; @@ -361,7 +362,7 @@ private Iterable startingStates(MethodTree tree, ProgramState curr boolean nullableParameters = isGloballyAnnotatedParameterNullable(methodTree.symbol()); boolean hasMethodBehavior = methodBehavior != null; - for (final VariableTree variableTree : tree.parameters()) { + for (final VariableTree variableTree : methodOrRecordConstructorParameters(tree)) { final SymbolicValue sv = constraintManager.createSymbolicValue(variableTree); Symbol variableSymbol = variableTree.symbol(); if (hasMethodBehavior) { @@ -386,6 +387,14 @@ private Iterable startingStates(MethodTree tree, ProgramState curr return stateStream.collect(Collectors.toList()); } + private static List methodOrRecordConstructorParameters(MethodTree methodTree) { + Tree parent = methodTree.parent(); + if (parent.kind() == Tree.Kind.RECORD && methodTree.openParenToken() == null) { + return ((ClassTree) parent).recordComponents(); + } + return methodTree.parameters(); + } + private static void throwMaximumStartingStates(MethodTree tree) { String message = String.format("reached maximum number of %d starting states for method %s in class %s", MAX_STARTING_STATES, tree.simpleName().name(), tree.symbol().owner().name()); diff --git a/java-symbolic-execution/src/test/files/se/RecordConstructorParameters.java b/java-symbolic-execution/src/test/files/se/RecordConstructorParameters.java new file mode 100644 index 00000000000..db4815a6bfc --- /dev/null +++ b/java-symbolic-execution/src/test/files/se/RecordConstructorParameters.java @@ -0,0 +1,20 @@ +package org.sonar.java.checks; + +public record RecordConstructorParameters(List messages) { + + public RecordConstructorParameters { + requireNonNull(messages == null && messages.isEmtpy()); // Noncompliant {{A "NullPointerException" could be thrown; "messages" is nullable here.}} + } + + public RecordConstructorParameters() { + this(new ArrayList<>()); + } + + public static RecordConstructorParameters create(List messages) { + if (messages == null) { + return new RecordConstructorParameters(); + } + return new RecordConstructorParameters(messages); + } + +} diff --git a/java-symbolic-execution/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java b/java-symbolic-execution/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java index 7569ef4abcd..0a18892e6cd 100644 --- a/java-symbolic-execution/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java +++ b/java-symbolic-execution/src/test/java/org/sonar/java/se/ExplodedGraphWalkerTest.java @@ -133,6 +133,15 @@ void exception_catched_in_loop() throws Exception { .verifyIssues(); } + @Test + void record_constructor_parameters() throws Exception { + SECheckVerifier.newVerifier() + .onFile("src/test/files/se/RecordConstructorParameters.java") + .withChecks(seChecks()) + .withClassPath(SETestUtils.CLASS_PATH) + .verifyIssues(); + } + @Test void constraints_on_fields() throws Exception { SECheckVerifier.newVerifier()