From 71dab288b58282d68fd2b5f8bab2e8a7187ca7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 3 Aug 2026 15:50:00 +0200 Subject: [PATCH 1/3] Add a centralized list of class-like node kinds to the Tree type --- .../org/sonar/plugins/java/api/tree/Tree.java | 21 ++++++++++++++++++- .../sonar/plugins/java/api/tree/TreeTest.java | 9 ++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/tree/Tree.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/tree/Tree.java index 244b4c2959f..59fa705bca8 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/tree/Tree.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/tree/Tree.java @@ -16,8 +16,9 @@ */ package org.sonar.plugins.java.api.tree; -import org.sonar.java.annotations.Beta; +import java.util.List; import javax.annotation.Nullable; +import org.sonar.java.annotations.Beta; import org.sonar.sslr.grammar.GrammarRuleKey; /** @@ -28,6 +29,23 @@ @Beta public interface Tree { + /** + * The {@link Kind}s of all class-like type declarations: {@link Kind#CLASS}, {@link Kind#ENUM}, {@link Kind#INTERFACE}, + * {@link Kind#ANNOTATION_TYPE}, {@link Kind#RECORD} and {@link Kind#IMPLICIT_CLASS}. All of these are backed by {@link ClassTree}. + * + *

Use this as the single source of truth when a visitor or predicate must handle every class-like structure, instead of + * hand-listing the kinds. In particular, it can be returned directly from {@code nodesToVisit()} of a subscription visitor that + * needs to visit all class declarations.

+ */ + List CLASS_KINDS = List.of( + Kind.CLASS, + Kind.ENUM, + Kind.INTERFACE, + Kind.ANNOTATION_TYPE, + Kind.RECORD, + Kind.IMPLICIT_CLASS + ); + boolean is(Kind... kinds); void accept(TreeVisitor visitor); @@ -151,6 +169,7 @@ enum Kind implements GrammarRuleKey { /** * {@link SwitchExpressionTree} + * * @since SonarJava 5.12: Support of Java 12 */ SWITCH_EXPRESSION(SwitchExpressionTree.class), diff --git a/java-frontend/src/test/java/org/sonar/plugins/java/api/tree/TreeTest.java b/java-frontend/src/test/java/org/sonar/plugins/java/api/tree/TreeTest.java index 6670957227b..bf2417d58f6 100644 --- a/java-frontend/src/test/java/org/sonar/plugins/java/api/tree/TreeTest.java +++ b/java-frontend/src/test/java/org/sonar/plugins/java/api/tree/TreeTest.java @@ -16,6 +16,7 @@ */ package org.sonar.plugins.java.api.tree; +import java.util.Arrays; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -27,4 +28,12 @@ void test() { assertThat(Tree.Kind.values()).hasSize(129); } + @Test + void class_kinds_contains_exactly_all_kinds_backed_by_class_tree() { + Tree.Kind[] expected = Arrays.stream(Tree.Kind.values()) + .filter(kind -> kind.getAssociatedInterface() == ClassTree.class) + .toArray(Tree.Kind[]::new); + assertThat(Tree.CLASS_KINDS).containsExactlyInAnyOrder(expected); + } + } From 2a63740ad7a39abe4a369683582a8f08e1f339da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 3 Aug 2026 15:56:29 +0200 Subject: [PATCH 2/3] Replace explicit lists of class-like kinds with Tree.CLASS_KINDS in the Java frontend --- .../src/main/java/org/sonar/java/Measurer.java | 9 +-------- .../sonar/java/ast/visitors/ComplexityVisitor.java | 4 ++-- .../sonar/java/ast/visitors/PublicApiChecker.java | 13 +++---------- .../src/main/java/org/sonar/java/model/JUtils.java | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/java-frontend/src/main/java/org/sonar/java/Measurer.java b/java-frontend/src/main/java/org/sonar/java/Measurer.java index 977455037fc..615127b003f 100644 --- a/java-frontend/src/main/java/org/sonar/java/Measurer.java +++ b/java-frontend/src/main/java/org/sonar/java/Measurer.java @@ -39,14 +39,7 @@ public class Measurer extends SubscriptionVisitor { - private static final Tree.Kind[] CLASS_KINDS = new Tree.Kind[]{ - Tree.Kind.CLASS, - Tree.Kind.INTERFACE, - Tree.Kind.ENUM, - Tree.Kind.ANNOTATION_TYPE, - Tree.Kind.RECORD, - Tree.Kind.IMPLICIT_CLASS - }; + private static final Tree.Kind[] CLASS_KINDS = Tree.CLASS_KINDS.toArray(new Tree.Kind[0]); private final SensorContext sensorContext; private final NoSonarFilter noSonarFilter; diff --git a/java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java b/java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java index 874c128c523..8ae0fd9093f 100644 --- a/java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java +++ b/java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java @@ -57,14 +57,14 @@ public void visitMethod(MethodTree tree) { @Override public void visitClass(ClassTree tree) { - if(root.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.COMPILATION_UNIT)) { + if (Tree.CLASS_KINDS.contains(root.kind()) || root.is(Tree.Kind.COMPILATION_UNIT)) { super.visitClass(tree); } } @Override public void visitLambdaExpression(LambdaExpressionTree lambdaExpressionTree) { - if(root.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.COMPILATION_UNIT) || lambdaExpressionTree.equals(root)) { + if (Tree.CLASS_KINDS.contains(root.kind()) || root.is(Tree.Kind.COMPILATION_UNIT) || lambdaExpressionTree.equals(root)) { blame.add(lambdaExpressionTree.arrowToken()); super.visitLambdaExpression(lambdaExpressionTree); } diff --git a/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java b/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java index 29c45664087..525174a8b1d 100644 --- a/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java +++ b/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java @@ -21,16 +21,16 @@ import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; -import org.sonar.plugins.java.api.tree.SyntaxTrivia.CommentKind; -import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.ModifiersUtils; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Modifier; import org.sonar.plugins.java.api.tree.ModifiersTree; import org.sonar.plugins.java.api.tree.SyntaxTrivia; +import org.sonar.plugins.java.api.tree.SyntaxTrivia.CommentKind; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; public class PublicApiChecker { @@ -38,14 +38,7 @@ private PublicApiChecker() { // Utility class } - private static final Tree.Kind[] CLASS_KINDS = { - Tree.Kind.CLASS, - Tree.Kind.INTERFACE, - Tree.Kind.ENUM, - Tree.Kind.ANNOTATION_TYPE, - Tree.Kind.RECORD, - Tree.Kind.IMPLICIT_CLASS - }; + private static final Tree.Kind[] CLASS_KINDS = Tree.CLASS_KINDS.toArray(new Tree.Kind[0]); private static final Tree.Kind[] METHOD_KINDS = { Tree.Kind.METHOD, diff --git a/java-frontend/src/main/java/org/sonar/java/model/JUtils.java b/java-frontend/src/main/java/org/sonar/java/model/JUtils.java index 903f0048ef7..caf36b0c925 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JUtils.java @@ -115,7 +115,7 @@ public static Symbol enclosingClass(Tree t) { if (t == null) { return null; } - if (t.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE)) { + if (Tree.CLASS_KINDS.contains(t.kind())) { return ((ClassTree) t).symbol(); } t = t.parent(); From 2273fde8823ac8cfd0c0658563590655b65b07af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 3 Aug 2026 17:01:36 +0200 Subject: [PATCH 3/3] Replace explicit lists of class-like kinds with Tree.CLASS_KINDS in Java checks --- .../resources/autoscan/diffs/diff_S1172.json | 2 +- ...dantNullabilityAnnotationsCheckSample.java | 14 ++++++++ .../AbstractCallToDeprecatedCodeChecker.java | 6 ++-- .../checks/CallOuterPrivateMethodCheck.java | 3 +- .../CallSuperMethodFromInnerClassCheck.java | 3 +- .../java/checks/ClassFieldCountCheck.java | 6 ++-- .../java/checks/CollectionIsEmptyCheck.java | 8 +---- .../checks/ExpressionComplexityCheck.java | 23 ++++++------- .../sonar/java/checks/HiddenFieldCheck.java | 33 ++++++------------- .../checks/InnerClassTooManyLinesCheck.java | 6 ++-- .../InterfaceOrSuperclassShadowingCheck.java | 10 +++--- ...embersDifferOnlyByCapitalizationCheck.java | 3 +- .../checks/MultipleMainInstancesCheck.java | 2 +- .../checks/OneDeclarationPerLineCheck.java | 6 ++-- .../PublicStaticMutableMembersCheck.java | 7 ++-- .../java/checks/RedundantModifierCheck.java | 3 +- .../RedundantNullabilityAnnotationsCheck.java | 5 ++- .../checks/SynchronizedClassUsageCheck.java | 4 +-- .../java/checks/TooManyMethodsCheck.java | 3 +- .../checks/helpers/ClassPatternsUtils.java | 2 +- .../checks/naming/BadConstantNameCheck.java | 8 ++--- .../checks/spring/SpelExpressionCheck.java | 2 +- .../tests/MockitoStaticImportCheck.java | 7 ++-- .../unused/UnusedPrivateClassCheck.java | 4 +-- 24 files changed, 75 insertions(+), 95 deletions(-) diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1172.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1172.json index 322daa80d40..72a00406d35 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1172.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1172.json @@ -1,6 +1,6 @@ { "ruleKey": "S1172", "hasTruePositives": true, - "falseNegatives": 32, + "falseNegatives": 33, "falsePositives": 0 } diff --git a/java-checks-test-sources/default/src/main/java/checks/jspecify/nullmarked/RedundantNullabilityAnnotationsCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/jspecify/nullmarked/RedundantNullabilityAnnotationsCheckSample.java index 82e1f9b9d57..a072326f4e0 100644 --- a/java-checks-test-sources/default/src/main/java/checks/jspecify/nullmarked/RedundantNullabilityAnnotationsCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/jspecify/nullmarked/RedundantNullabilityAnnotationsCheckSample.java @@ -205,6 +205,20 @@ static interface InnerRecord { } + @NullMarked // Noncompliant {{Remove redundant annotation @NullMarked at class level as inside scope annotation @NullMarked at package level.}} + static @interface Annotation { + } + + @NullMarked // Noncompliant {{Remove redundant annotation @NullMarked at class level as inside scope annotation @NullMarked at package level.}} + enum InnerEnum { + A, + B; + + public void f(@NonNull String s) { // Noncompliant {{Remove redundant annotation @NonNull as inside scope annotation @NullMarked at class level.}} + // Do something + } + } + } enum TEST_COVERAGE { diff --git a/java-checks/src/main/java/org/sonar/java/checks/AbstractCallToDeprecatedCodeChecker.java b/java-checks/src/main/java/org/sonar/java/checks/AbstractCallToDeprecatedCodeChecker.java index 06a2aaf45d2..ca538207a4b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AbstractCallToDeprecatedCodeChecker.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AbstractCallToDeprecatedCodeChecker.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -29,6 +28,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; public abstract class AbstractCallToDeprecatedCodeChecker extends IssuableSubscriptionVisitor { @@ -41,7 +41,7 @@ public final void leaveFile(JavaFileScannerContext context) { @Override public final List nodesToVisit() { - return Arrays.asList(Tree.Kind.IDENTIFIER, Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Tree.Kind.IDENTIFIER, Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR)); } @Override @@ -120,7 +120,7 @@ private static boolean isDeprecatedMethod(Tree tree) { } private static boolean isDeprecatedClassTree(Tree tree) { - return tree.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE) && ((ClassTree) tree).symbol().isDeprecated(); + return Tree.CLASS_KINDS.contains(tree.kind()) && ((ClassTree) tree).symbol().isDeprecated(); } boolean isFlaggedForRemoval(Symbol deprecatedSymbol) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/CallOuterPrivateMethodCheck.java b/java-checks/src/main/java/org/sonar/java/checks/CallOuterPrivateMethodCheck.java index 6b309ade91c..5dec95bfaa6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/CallOuterPrivateMethodCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/CallOuterPrivateMethodCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -44,7 +43,7 @@ public class CallOuterPrivateMethodCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/CallSuperMethodFromInnerClassCheck.java b/java-checks/src/main/java/org/sonar/java/checks/CallSuperMethodFromInnerClassCheck.java index a99c01f3b17..7d4717c3855 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/CallSuperMethodFromInnerClassCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/CallSuperMethodFromInnerClassCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.List; import org.sonar.check.Rule; import org.sonar.java.model.ExpressionUtils; @@ -34,7 +33,7 @@ public class CallSuperMethodFromInnerClassCheck extends IssuableSubscriptionVisi @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/ClassFieldCountCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ClassFieldCountCheck.java index 45587a250f0..f57bc412d28 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/ClassFieldCountCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/ClassFieldCountCheck.java @@ -16,6 +16,7 @@ */ package org.sonar.java.checks; +import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; @@ -24,9 +25,6 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; -import java.util.Arrays; -import java.util.List; - import static org.sonar.java.checks.helpers.ExpressionsHelper.reportOnClassTree; @Rule(key = "S1820") @@ -43,7 +41,7 @@ public class ClassFieldCountCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ENUM); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/CollectionIsEmptyCheck.java b/java-checks/src/main/java/org/sonar/java/checks/CollectionIsEmptyCheck.java index 98f8146397f..ddc04a592d6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/CollectionIsEmptyCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/CollectionIsEmptyCheck.java @@ -63,13 +63,7 @@ private enum EmptyComparisonType { Tree.Kind.GREATER_THAN, Tree.Kind.GREATER_THAN_OR_EQUAL_TO }; - private static final Tree.Kind[] CLASS_TREES = { - Tree.Kind.CLASS, - Tree.Kind.ENUM, - Tree.Kind.INTERFACE, - Tree.Kind.RECORD, - Tree.Kind.ANNOTATION_TYPE - }; + private static final Tree.Kind[] CLASS_TREES = Tree.CLASS_KINDS.toArray(new Tree.Kind[0]); private static final Deque IS_COLLECTION_ENCLOSING_TYPES_STACK = new LinkedList<>(); @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/ExpressionComplexityCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ExpressionComplexityCheck.java index 02ba9aedd5c..d6affcd799b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/ExpressionComplexityCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/ExpressionComplexityCheck.java @@ -16,6 +16,10 @@ */ package org.sonar.java.checks; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.java.checks.helpers.MethodTreeUtils; @@ -24,12 +28,7 @@ import org.sonar.plugins.java.api.tree.LambdaExpressionTree; import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; +import org.sonarsource.analyzer.commons.collections.ListUtils; @Rule(key = "S1067") public class ExpressionComplexityCheck extends IssuableSubscriptionVisitor { @@ -56,9 +55,7 @@ public void setContext(JavaFileScannerContext context) { @Override public List nodesToVisit() { - return Arrays.asList( - Tree.Kind.CLASS, - Tree.Kind.RECORD, + return ListUtils.concat(Tree.CLASS_KINDS, List.of( Tree.Kind.POSTFIX_INCREMENT, Tree.Kind.POSTFIX_DECREMENT, Tree.Kind.PREFIX_INCREMENT, @@ -118,12 +115,12 @@ public List nodesToVisit() { Tree.Kind.IDENTIFIER, Tree.Kind.ARRAY_TYPE, Tree.Kind.LAMBDA_EXPRESSION, - Tree.Kind.PRIMITIVE_TYPE); + Tree.Kind.PRIMITIVE_TYPE)); } @Override public void visitNode(Tree tree) { - if (tree.is(Tree.Kind.CLASS, Tree.Kind.RECORD, Tree.Kind.NEW_ARRAY) || isLambdaWithBlock(tree)) { + if (Tree.CLASS_KINDS.contains(tree.kind()) || tree.is(Tree.Kind.NEW_ARRAY) || isLambdaWithBlock(tree)) { count.push(0); level.push(0); } else { @@ -136,7 +133,7 @@ public void visitNode(Tree tree) { @Override public void leaveNode(Tree tree) { - if (tree.is(Tree.Kind.CLASS, Tree.Kind.RECORD, Tree.Kind.NEW_ARRAY) || isLambdaWithBlock(tree)) { + if (Tree.CLASS_KINDS.contains(tree.kind()) || tree.is(Tree.Kind.NEW_ARRAY) || isLambdaWithBlock(tree)) { count.pop(); level.pop(); } else { @@ -155,7 +152,7 @@ public void leaveNode(Tree tree) { private static boolean isInsideEquals(Tree tree) { Tree parent = tree.parent(); - while (parent != null && !parent.is(Tree.Kind.CLASS, Tree.Kind.RECORD)) { + while (parent != null && !Tree.CLASS_KINDS.contains(parent.kind())) { if (parent.is(Tree.Kind.METHOD) && MethodTreeUtils.isEqualsMethod((MethodTree) parent)) { return true; } diff --git a/java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java b/java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java index 8bd9a261ac5..eeb5e72e0f2 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java @@ -17,7 +17,6 @@ package org.sonar.java.checks; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashSet; @@ -27,8 +26,6 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonarsource.analyzer.commons.collections.MapBuilder; -import org.sonarsource.analyzer.commons.collections.SetUtils; import org.sonar.java.model.JavaTree; import org.sonar.java.model.LineUtils; import org.sonar.java.model.ModifiersUtils; @@ -42,6 +39,9 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; +import org.sonarsource.analyzer.commons.collections.ListUtils; +import org.sonarsource.analyzer.commons.collections.MapBuilder; +import org.sonarsource.analyzer.commons.collections.SetUtils; @DeprecatedRuleKey(ruleKey = "HiddenFieldCheck", repositoryKey = "squid") @Rule(key = "S1117") @@ -53,18 +53,12 @@ public class HiddenFieldCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList( - Tree.Kind.CLASS, - Tree.Kind.ENUM, - Tree.Kind.INTERFACE, - Tree.Kind.ANNOTATION_TYPE, - Tree.Kind.RECORD, - Tree.Kind.IMPLICIT_CLASS, - Tree.Kind.VARIABLE, - Tree.Kind.METHOD, - Tree.Kind.CONSTRUCTOR, - Tree.Kind.STATIC_INITIALIZER - ); + return ListUtils.concat(Tree.CLASS_KINDS, List.of( + Tree.Kind.VARIABLE, + Tree.Kind.METHOD, + Tree.Kind.CONSTRUCTOR, + Tree.Kind.STATIC_INITIALIZER + )); } @Override @@ -131,14 +125,7 @@ private static boolean isInStaticInnerClass(VariableTree hiddenVariable, Variabl } private static boolean isClassTree(Tree tree) { - return tree.is( - Tree.Kind.CLASS, - Tree.Kind.ENUM, - Tree.Kind.INTERFACE, - Tree.Kind.ANNOTATION_TYPE, - Tree.Kind.RECORD, - Tree.Kind.IMPLICIT_CLASS - ); + return Tree.CLASS_KINDS.contains(tree.kind()); } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/InnerClassTooManyLinesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/InnerClassTooManyLinesCheck.java index 52c09481997..22b9b770a24 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/InnerClassTooManyLinesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/InnerClassTooManyLinesCheck.java @@ -16,6 +16,7 @@ */ package org.sonar.java.checks; +import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.java.checks.helpers.ExpressionsHelper; @@ -27,9 +28,6 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.Tree.Kind; -import java.util.Arrays; -import java.util.List; - @Rule(key = "S2972") public class InnerClassTooManyLinesCheck extends IssuableSubscriptionVisitor { @@ -42,7 +40,7 @@ public class InnerClassTooManyLinesCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Kind.CLASS, Kind.ENUM, Kind.INTERFACE, Kind.ANNOTATION_TYPE); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/InterfaceOrSuperclassShadowingCheck.java b/java-checks/src/main/java/org/sonar/java/checks/InterfaceOrSuperclassShadowingCheck.java index 6f4032afaad..e71c322ebbe 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/InterfaceOrSuperclassShadowingCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/InterfaceOrSuperclassShadowingCheck.java @@ -16,24 +16,22 @@ */ package org.sonar.java.checks; +import java.util.List; +import java.util.Locale; +import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; -import javax.annotation.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.Locale; @Rule(key = "S2176") public class InterfaceOrSuperclassShadowingCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.RECORD); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/MembersDifferOnlyByCapitalizationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/MembersDifferOnlyByCapitalizationCheck.java index d1881013ba7..a4bdeb0b126 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/MembersDifferOnlyByCapitalizationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/MembersDifferOnlyByCapitalizationCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -47,7 +46,7 @@ public class MembersDifferOnlyByCapitalizationCheck extends IssuableSubscription @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ENUM, Tree.Kind.RECORD); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/MultipleMainInstancesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/MultipleMainInstancesCheck.java index 88b1a10d9ae..38c5def7d16 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/MultipleMainInstancesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/MultipleMainInstancesCheck.java @@ -32,7 +32,7 @@ public class MultipleMainInstancesCheck extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor { @Override public List nodesToVisit() { - return List.of(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ENUM, Tree.Kind.RECORD, Tree.Kind.IMPLICIT_CLASS); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java index e164fc53938..17f01f16f4d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java @@ -17,7 +17,6 @@ package org.sonar.java.checks; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,6 +35,7 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.Tree.Kind; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; import static org.sonar.java.reporting.AnalyzerMessage.textSpanBetween; @@ -46,12 +46,12 @@ public class OneDeclarationPerLineCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Kind.INTERFACE, Kind.CLASS, Kind.ENUM, Kind.ANNOTATION_TYPE, Kind.BLOCK, Kind.STATIC_INITIALIZER, Kind.CASE_GROUP); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Kind.BLOCK, Kind.STATIC_INITIALIZER, Kind.CASE_GROUP)); } @Override public void visitNode(Tree tree) { - if (tree.is(Kind.INTERFACE, Kind.CLASS, Kind.ENUM, Kind.ANNOTATION_TYPE)) { + if (Tree.CLASS_KINDS.contains(tree.kind())) { // Field class declaration checkVariables(((ClassTree) tree).members()); } else if (tree.is(Kind.BLOCK, Kind.STATIC_INITIALIZER)) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/PublicStaticMutableMembersCheck.java b/java-checks/src/main/java/org/sonar/java/checks/PublicStaticMutableMembersCheck.java index db8186849b8..5bc35c060e1 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/PublicStaticMutableMembersCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/PublicStaticMutableMembersCheck.java @@ -44,6 +44,7 @@ import org.sonar.plugins.java.api.tree.NewArrayTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; @Rule(key = "S2386") public class PublicStaticMutableMembersCheck extends IssuableSubscriptionVisitor { @@ -115,7 +116,7 @@ public class PublicStaticMutableMembersCheck extends IssuableSubscriptionVisitor @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.INTERFACE, Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.ASSIGNMENT); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Tree.Kind.ASSIGNMENT)); } @Override @@ -170,8 +171,8 @@ private void checkAssignment(AssignmentExpressionTree node) { @Override public void leaveNode(Tree tree) { - // cleanup - if (tree.is(Tree.Kind.CLASS, Tree.Kind.ENUM)) { + // cleanup for every class-like declaration visited (i.e. all subscribed nodes except ASSIGNMENT) + if (!tree.is(Tree.Kind.ASSIGNMENT)) { IMMUTABLE_CANDIDATES.removeAll(CLASS_IMMUTABLE_CANDIDATES.getOrDefault(tree, Collections.emptyList())); } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/RedundantModifierCheck.java b/java-checks/src/main/java/org/sonar/java/checks/RedundantModifierCheck.java index 97bbce5461c..7af04d51b94 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/RedundantModifierCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/RedundantModifierCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.List; import org.sonar.check.Rule; import org.sonar.java.model.ModifiersUtils; @@ -34,7 +33,7 @@ public class RedundantModifierCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.RECORD); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java index fbe1163870d..a5d6d017c30 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -41,7 +40,7 @@ public class RedundantNullabilityAnnotationsCheck extends IssuableSubscriptionVi @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.INTERFACE, Tree.Kind.CLASS, Tree.Kind.RECORD); + return Tree.CLASS_KINDS; } @Override @@ -79,7 +78,7 @@ private void checkMembers(SymbolMetadata.NullabilityData classNullabilityData, } else if (member.is(Tree.Kind.METHOD)) { // check method checkMethod(classNullabilityData, (MethodTree) member, scope); - } else if (member.is(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.RECORD)) { + } else if (member.is(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.RECORD, Tree.Kind.ENUM, Tree.Kind.ANNOTATION_TYPE)) { // check inner class checkInnerClass(classNullabilityData, (ClassTree) member, scope); } diff --git a/java-checks/src/main/java/org/sonar/java/checks/SynchronizedClassUsageCheck.java b/java-checks/src/main/java/org/sonar/java/checks/SynchronizedClassUsageCheck.java index c2feeab25e3..14c983a56f9 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/SynchronizedClassUsageCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/SynchronizedClassUsageCheck.java @@ -18,7 +18,6 @@ import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashSet; @@ -40,6 +39,7 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TypeTree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonarsource.analyzer.commons.collections.MapBuilder; @Rule(key = "S1149") @@ -59,7 +59,7 @@ public class SynchronizedClassUsageCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { // We register on compilation units to clear the visited set when scanning a new file - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.COMPILATION_UNIT, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Tree.Kind.COMPILATION_UNIT)); } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/TooManyMethodsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TooManyMethodsCheck.java index 716675e5323..42016d8ecd3 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/TooManyMethodsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/TooManyMethodsCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks; -import java.util.Arrays; import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; @@ -49,7 +48,7 @@ public class TooManyMethodsCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.RECORD); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/ClassPatternsUtils.java b/java-checks/src/main/java/org/sonar/java/checks/helpers/ClassPatternsUtils.java index dc85c801744..7734c09e27a 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/ClassPatternsUtils.java +++ b/java-checks/src/main/java/org/sonar/java/checks/helpers/ClassPatternsUtils.java @@ -102,7 +102,7 @@ private static boolean isStatic(Tree member) { } private static boolean isClassTree(Tree member) { - return member.is(Tree.Kind.CLASS) || member.is(Tree.Kind.ANNOTATION_TYPE) || member.is(Tree.Kind.INTERFACE) || member.is(Tree.Kind.ENUM); + return Tree.CLASS_KINDS.contains(member.kind()); } private static boolean hasStaticModifier(ModifiersTree modifiers) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/naming/BadConstantNameCheck.java b/java-checks/src/main/java/org/sonar/java/checks/naming/BadConstantNameCheck.java index 0ba6e29cf6c..490b5dc76d1 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/naming/BadConstantNameCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/naming/BadConstantNameCheck.java @@ -16,6 +16,8 @@ */ package org.sonar.java.checks.naming; +import java.util.List; +import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.java.checks.serialization.SerializableContract; @@ -27,10 +29,6 @@ import org.sonar.plugins.java.api.tree.ModifierKeywordTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; - -import java.util.Arrays; -import java.util.List; -import java.util.regex.Pattern; import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; @DeprecatedRuleKey(ruleKey = "S00115", repositoryKey = "squid") @@ -48,7 +46,7 @@ public class BadConstantNameCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpelExpressionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpelExpressionCheck.java index 7146561d920..c302636b702 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpelExpressionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpelExpressionCheck.java @@ -70,7 +70,7 @@ public class SpelExpressionCheck extends IssuableSubscriptionVisitor { "[a-zA-Z0-9/_-]++(\\[\\d++])*+(\\.[a-zA-Z0-9/_-]++(\\[\\d++])*+)*+"); public List nodesToVisit() { - return List.of(Tree.Kind.CLASS, Tree.Kind.INTERFACE); + return Tree.CLASS_KINDS; } @Override diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java index 8ed2578983e..f09630621f6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java @@ -35,6 +35,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.collections.ListUtils; @Rule(key = "S8924") public class MockitoStaticImportCheck extends IssuableSubscriptionVisitor { @@ -53,14 +54,14 @@ public class MockitoStaticImportCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return List.of(Tree.Kind.COMPILATION_UNIT, Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.RECORD, Tree.Kind.METHOD_INVOCATION); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Tree.Kind.COMPILATION_UNIT, Tree.Kind.METHOD_INVOCATION)); } @Override public void visitNode(Tree tree) { switch (tree.kind()) { case COMPILATION_UNIT -> collectConflictingImports((CompilationUnitTree) tree); - case CLASS, ENUM, INTERFACE, RECORD -> pushClassMethods((ClassTree) tree); + case CLASS, ENUM, INTERFACE, RECORD, ANNOTATION_TYPE, IMPLICIT_CLASS -> pushClassMethods((ClassTree) tree); case METHOD_INVOCATION -> checkMethodInvocation((MethodInvocationTree) tree); default -> { /* not visited */ } } @@ -69,7 +70,7 @@ public void visitNode(Tree tree) { @Override public void leaveNode(Tree tree) { switch (tree.kind()) { - case CLASS, ENUM, INTERFACE, RECORD -> classMethodsStack.pop(); + case CLASS, ENUM, INTERFACE, RECORD, ANNOTATION_TYPE, IMPLICIT_CLASS -> classMethodsStack.pop(); default -> { /* nothing */ } } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/unused/UnusedPrivateClassCheck.java b/java-checks/src/main/java/org/sonar/java/checks/unused/UnusedPrivateClassCheck.java index f51749b0e35..141b967fc17 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/unused/UnusedPrivateClassCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/unused/UnusedPrivateClassCheck.java @@ -16,7 +16,6 @@ */ package org.sonar.java.checks.unused; -import java.util.Arrays; import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.UnresolvedIdentifiersVisitor; @@ -25,6 +24,7 @@ import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.IdentifierTree; import org.sonar.plugins.java.api.tree.Tree; +import org.sonarsource.analyzer.commons.collections.ListUtils; @Rule(key = "S3985") public class UnusedPrivateClassCheck extends IssuableSubscriptionVisitor { @@ -33,7 +33,7 @@ public class UnusedPrivateClassCheck extends IssuableSubscriptionVisitor { @Override public List nodesToVisit() { - return Arrays.asList(Tree.Kind.COMPILATION_UNIT, Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.ENUM); + return ListUtils.concat(Tree.CLASS_KINDS, List.of(Tree.Kind.COMPILATION_UNIT)); } @Override