diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S5778.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S5778.json index 725e6ad2614..9fc6644b170 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S5778.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S5778.json @@ -1,6 +1,6 @@ { "ruleKey": "S5778", "hasTruePositives": false, - "falseNegatives": 32, + "falseNegatives": 44, "falsePositives": 0 } diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S5961.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S5961.json index 39329ff6bae..078899b8899 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S5961.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S5961.json @@ -1,6 +1,6 @@ { "ruleKey": "S5961", "hasTruePositives": false, - "falseNegatives": 5, + "falseNegatives": 6, "falsePositives": 0 } \ No newline at end of file diff --git a/its/ruling/src/test/resources/sonar-server/java-S5778.json b/its/ruling/src/test/resources/sonar-server/java-S5778.json index 9913bd9b7d1..c06a0840e65 100644 --- a/its/ruling/src/test/resources/sonar-server/java-S5778.json +++ b/its/ruling/src/test/resources/sonar-server/java-S5778.json @@ -5,9 +5,6 @@ "org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java": [ 121 ], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoaderTest.java": [ -89 -], "org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepositoryTest.java": [ 187 ], diff --git a/java-checks-test-sources/default/src/test/java/checks/tests/OneExpectedRuntimeExceptionCheckSample.java b/java-checks-test-sources/default/src/test/java/checks/tests/OneExpectedRuntimeExceptionCheckSample.java index a660c099ce1..3eaa322c163 100644 --- a/java-checks-test-sources/default/src/test/java/checks/tests/OneExpectedRuntimeExceptionCheckSample.java +++ b/java-checks-test-sources/default/src/test/java/checks/tests/OneExpectedRuntimeExceptionCheckSample.java @@ -1,6 +1,18 @@ package checks.tests; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + import org.junit.Assert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -228,6 +240,48 @@ public void test_AssertJ() { org.assertj.core.api.Assertions.assertThat(thrown).isInstanceOf(IllegalStateException.class); } + @Test + void testAllowEmptyCollections() { + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1)); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, foo(2))); // Noncompliant + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.emptyList())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, foo(2), Collections.emptyList())); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.emptySet())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.emptyMap())); + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singleton(2))); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singleton(foo(2)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singletonList(2))); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singletonList(foo(2)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singletonMap(2, 3))); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Collections.singletonMap(foo(2), foo(3)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, List.of())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, List.of(foo(2)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Set.of())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Set.of(foo(2)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Map.of())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Map.of(foo(2), foo(3)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new ArrayList<>())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new LinkedList<>())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new HashSet<>())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new HashMap<>())); + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new ArrayList<>(getCollection()))); // Noncompliant + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new LinkedList<>(getCollection()))); // Noncompliant + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new HashSet<>(getCollection()))); // Noncompliant + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, new HashMap<>(foo(2), foo(3)))); // Noncompliant + + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Stream.empty())); + assertThrows(IllegalArgumentException.class, () -> objVarargMethod(1, Optional.empty())); + } + int foo(int x) { return x; @@ -255,4 +309,11 @@ void foo() { } } + void objVarargMethod(Object... args) { + throw new IllegalArgumentException(); + } + + Collection getCollection() { + return new ArrayList<>(); + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/OneExpectedRuntimeExceptionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/OneExpectedRuntimeExceptionCheck.java index 4e5d83ed865..785b151226c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/OneExpectedRuntimeExceptionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/OneExpectedRuntimeExceptionCheck.java @@ -33,15 +33,57 @@ public class OneExpectedRuntimeExceptionCheck extends AbstractOneExpectedExcepti .names("mock") .addParametersMatcher("java.lang.Class").addParametersMatcher("java.lang.Class", "java.lang.String") .build(); + private static final MethodMatchers ENUM_FINAL_METHODS = MethodMatchers.create() .ofSubTypes("java.lang.Enum") .names("name", "ordinal", "equals", "hashCode", "compareTo", "getDeclaringClass", "describeConstable") .withAnyParameters() .build(); + + private static final MethodMatchers COLLECTIONS_EMPTY = MethodMatchers.create() + .ofTypes("java.util.Collections") + .names("emptyList", "emptySet", "emptyMap") + .addWithoutParametersMatcher() + .build(); + + private static final MethodMatchers COLLECTIONS_SINGLETON = MethodMatchers.create() + .ofTypes("java.util.Collections") + .names("singleton", "singletonList", "singletonMap") + .withAnyParameters() + .build(); + + private static final MethodMatchers COLLECTION_OF = MethodMatchers.create() + .ofTypes("java.util.List", "java.util.Set", "java.util.Map") + .names("of") + .addWithoutParametersMatcher() + .build(); + + private static final MethodMatchers COLLECTION_CTOR = MethodMatchers.create() + .ofSubTypes("java.util.ArrayList", "java.util.LinkedList", "java.util.HashSet", "java.util.HashMap") + .constructor() + .addWithoutParametersMatcher() + .build(); + + private static final MethodMatchers EMPTY = MethodMatchers.create() + .ofTypes("java.util.Optional", "java.util.stream.Stream") + .names("empty") + .addWithoutParametersMatcher() + .build(); + + /** + * Frequently used methods known not to throw runtime exceptions. + */ + // If adding to this list, make sure that the methods do not call NullPointerExceptions + // (for example, `Arrays.asList(null)` throws). private static final MethodMatchers AUTHORIZED_METHODS = MethodMatchers.or( FAIL_METHOD_MATCHER, MOCKITO_MOCK_METHOD_MATCHERS, - ENUM_FINAL_METHODS + ENUM_FINAL_METHODS, + COLLECTIONS_EMPTY, + COLLECTIONS_SINGLETON, + COLLECTION_OF, + COLLECTION_CTOR, + EMPTY ); @Override