Skip to content

SONARJAVA-6430 Centralize class-like tree kinds - #5869

Draft
aurelien-coet-sonarsource wants to merge 3 commits into
masterfrom
ac/SONARJAVA-6430
Draft

SONARJAVA-6430 Centralize class-like tree kinds#5869
aurelien-coet-sonarsource wants to merge 3 commits into
masterfrom
ac/SONARJAVA-6430

Conversation

@aurelien-coet-sonarsource

@aurelien-coet-sonarsource aurelien-coet-sonarsource commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary by Gitar

  • API additions:
    • Added centralized Tree.CLASS_KINDS list containing all class-like node kinds in Tree.java
    • Added unit test in TreeTest.java to verify CLASS_KINDS matches all kinds backed by ClassTree

This will update automatically on new commits.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6430

Comment on lines 42 to 44
public List<Tree.Kind> nodesToVisit() {
return Arrays.asList(Tree.Kind.INTERFACE, Tree.Kind.CLASS, Tree.Kind.RECORD);
return Tree.CLASS_KINDS;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: RedundantNullability rule scope expands to enums/annotations

Previously nodesToVisit() returned only {INTERFACE, CLASS, RECORD} and checkMembers only recursed into {CLASS, INTERFACE, RECORD} members. This commit switches nodesToVisit() to Tree.CLASS_KINDS (adding ENUM, ANNOTATION_TYPE, IMPLICIT_CLASS) and line 81 manually adds ENUM/ANNOTATION_TYPE to the inner-class member check. This is not a behavior-preserving refactor: S6665 will now analyze top-level and nested enums and annotation types for redundant nullability annotations, which can raise new (possibly false-positive) issues on user code. Confirm this expansion is intended and covered by tests; if the rule should keep its original scope, restrict nodesToVisit() to the original kinds rather than CLASS_KINDS.

Was this helpful? React with 👍 / 👎

Comment on lines 164 to 169
public Set<Tree.Kind> excludedNodes() {
return SetUtils.immutableSetOf(Tree.Kind.METHOD, Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.NEW_CLASS);
Set<Tree.Kind> excluded = new HashSet<>(Tree.CLASS_KINDS);
excluded.add(Tree.Kind.METHOD);
excluded.add(Tree.Kind.NEW_CLASS);
return excluded;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: HiddenFieldCheck excludedNodes() now also excludes RECORD/ANN/IMPLICIT

VariableList.excludedNodes() previously stopped recursion at {METHOD, CLASS, ENUM, INTERFACE, NEW_CLASS}; it now uses Tree.CLASS_KINDS plus METHOD/NEW_CLASS, additionally excluding RECORD, ANNOTATION_TYPE and IMPLICIT_CLASS. This changes which variables inside static blocks/method bodies are collected as excluded when a local record (or annotation type) is present, altering hidden-field detection in that edge case. Verify this matches the intended behavior or that it is exercised by tests.

Was this helpful? React with 👍 / 👎

Comment on lines 56 to +57
public List<Tree.Kind> 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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: MockitoStaticImportCheck switch can drift from CLASS_KINDS

nodesToVisit() now derives from Tree.CLASS_KINDS, but the visitNode()/leaveNode() switch statements still hand-list the class-like kinds (CLASS, ENUM, INTERFACE, RECORD, ANNOTATION_TYPE, IMPLICIT_CLASS). If Tree.CLASS_KINDS gains a new kind later, this check would subscribe to it but silently ignore it in the switch (falling into the default branch), leaving the classMethodsStack unbalanced. Consider centralizing the handling to avoid divergence from CLASS_KINDS.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown
CI failed: Test failures in SanityTest caused by a NullPointerException in RightCurlyBraceStartLineCheck when visiting tokens affected by the centralized class-like tree kinds refactoring.

Overview

1 test failure pattern found across 1 job analysis, directly caused by changes in tree kind representation introduced in the PR.

Failures

SanityTest Failures in RightCurlyBraceStartLineCheck (confidence: high)

  • Type: test
  • Affected jobs: 91726499520
  • Related to change: yes
  • Root cause: The RightCurlyBraceStartLineCheck attempts to invoke range() on a SyntaxToken that is null (java.lang.NullPointerException: Cannot invoke "org.sonar.plugins.java.api.tree.SyntaxToken.range()" because "token" is null) while scanning source file samples during SanityTest.scan_compiling_files and SanityTest.scan_non_compiling_files, likely due to updated node kinds or tree structures from the class-like tree kinds centralization.
  • Suggested fix: Add a null check for the SyntaxToken before calling range() or handling its position in RightCurlyBraceStartLineCheck.java (e.g. in checkBlockBody or visitNode) to robustly handle cases where tokens are absent or null in modified tree structures.

Summary

  • Change-related failures: 1 test failure (SanityTest.scan_compiling_files and SanityTest.scan_non_compiling_files) due to NPE in RightCurlyBraceStartLineCheck following tree kind changes.
  • Infrastructure/flaky failures: 0
  • Recommended action: Update RightCurlyBraceStartLineCheck.java to guard against null syntax tokens when evaluating block bodies and tree node locations.
Code Review ⚠️ Changes requested 1 resolved / 4 findings

Centralizes class-like tree kinds into Tree.CLASS_KINDS, but the refactor unintentionally expands the RedundantNullability rule scope to enums and annotations, and risks switch drift in MockitoStaticImportCheck.

⚠️ Bug: RedundantNullability rule scope expands to enums/annotations

📄 java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java:42-44 📄 java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java:81

Previously nodesToVisit() returned only {INTERFACE, CLASS, RECORD} and checkMembers only recursed into {CLASS, INTERFACE, RECORD} members. This commit switches nodesToVisit() to Tree.CLASS_KINDS (adding ENUM, ANNOTATION_TYPE, IMPLICIT_CLASS) and line 81 manually adds ENUM/ANNOTATION_TYPE to the inner-class member check. This is not a behavior-preserving refactor: S6665 will now analyze top-level and nested enums and annotation types for redundant nullability annotations, which can raise new (possibly false-positive) issues on user code. Confirm this expansion is intended and covered by tests; if the rule should keep its original scope, restrict nodesToVisit() to the original kinds rather than CLASS_KINDS.

💡 Quality: HiddenFieldCheck excludedNodes() now also excludes RECORD/ANN/IMPLICIT

📄 java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java:164-169

VariableList.excludedNodes() previously stopped recursion at {METHOD, CLASS, ENUM, INTERFACE, NEW_CLASS}; it now uses Tree.CLASS_KINDS plus METHOD/NEW_CLASS, additionally excluding RECORD, ANNOTATION_TYPE and IMPLICIT_CLASS. This changes which variables inside static blocks/method bodies are collected as excluded when a local record (or annotation type) is present, altering hidden-field detection in that edge case. Verify this matches the intended behavior or that it is exercised by tests.

💡 Quality: MockitoStaticImportCheck switch can drift from CLASS_KINDS

📄 java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:56-57 📄 java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:63-64 📄 java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:72-73

nodesToVisit() now derives from Tree.CLASS_KINDS, but the visitNode()/leaveNode() switch statements still hand-list the class-like kinds (CLASS, ENUM, INTERFACE, RECORD, ANNOTATION_TYPE, IMPLICIT_CLASS). If Tree.CLASS_KINDS gains a new kind later, this check would subscribe to it but silently ignore it in the switch (falling into the default branch), leaving the classMethodsStack unbalanced. Consider centralizing the handling to avoid divergence from CLASS_KINDS.

✅ 1 resolved
Bug: Refactor changes behavior for RECORD/IMPLICIT_CLASS in two spots

📄 java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java:60 📄 java-frontend/src/main/java/org/sonar/java/ast/visitors/ComplexityVisitor.java:67 📄 java-frontend/src/main/java/org/sonar/java/model/JUtils.java:118
Tree.CLASS_KINDS includes RECORD and IMPLICIT_CLASS, but the two hand-written lists replaced here did not. In ComplexityVisitor.visitClass/visitLambdaExpression (previously CLASS, ENUM, INTERFACE, ANNOTATION_TYPE, COMPILATION_UNIT) and JUtils.enclosingClass (previously CLASS, ENUM, INTERFACE, ANNOTATION_TYPE), records and implicit classes now match where they were skipped before. This alters complexity computation for record roots and makes enclosingClass return the record/implicit-class symbol instead of walking up to an outer class — a semantic change hidden inside a 'centralize kinds' refactor. The cast to ClassTree is safe (both kinds are backed by ClassTree), so this is likely a correct improvement, but it is a behavior change, not a pure refactor. Confirm it is intended and covered by tests; otherwise keep the original narrower kind sets in these two locations. Note Measurer and PublicApiChecker already used the identical 6-kind list, so those replacements are true no-ops.

🤖 Prompt for agents
Code Review: Centralizes class-like tree kinds into Tree.CLASS_KINDS, but the refactor unintentionally expands the RedundantNullability rule scope to enums and annotations, and risks switch drift in MockitoStaticImportCheck.

1. ⚠️ Bug: RedundantNullability rule scope expands to enums/annotations
   Files: java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java:42-44, java-checks/src/main/java/org/sonar/java/checks/RedundantNullabilityAnnotationsCheck.java:81

   Previously nodesToVisit() returned only {INTERFACE, CLASS, RECORD} and checkMembers only recursed into {CLASS, INTERFACE, RECORD} members. This commit switches nodesToVisit() to Tree.CLASS_KINDS (adding ENUM, ANNOTATION_TYPE, IMPLICIT_CLASS) and line 81 manually adds ENUM/ANNOTATION_TYPE to the inner-class member check. This is not a behavior-preserving refactor: S6665 will now analyze top-level and nested enums and annotation types for redundant nullability annotations, which can raise new (possibly false-positive) issues on user code. Confirm this expansion is intended and covered by tests; if the rule should keep its original scope, restrict nodesToVisit() to the original kinds rather than CLASS_KINDS.

2. 💡 Quality: HiddenFieldCheck excludedNodes() now also excludes RECORD/ANN/IMPLICIT
   Files: java-checks/src/main/java/org/sonar/java/checks/HiddenFieldCheck.java:164-169

   VariableList.excludedNodes() previously stopped recursion at {METHOD, CLASS, ENUM, INTERFACE, NEW_CLASS}; it now uses Tree.CLASS_KINDS plus METHOD/NEW_CLASS, additionally excluding RECORD, ANNOTATION_TYPE and IMPLICIT_CLASS. This changes which variables inside static blocks/method bodies are collected as excluded when a local record (or annotation type) is present, altering hidden-field detection in that edge case. Verify this matches the intended behavior or that it is exercised by tests.

3. 💡 Quality: MockitoStaticImportCheck switch can drift from CLASS_KINDS
   Files: java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:56-57, java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:63-64, java-checks/src/main/java/org/sonar/java/checks/tests/MockitoStaticImportCheck.java:72-73

   nodesToVisit() now derives from Tree.CLASS_KINDS, but the visitNode()/leaveNode() switch statements still hand-list the class-like kinds (CLASS, ENUM, INTERFACE, RECORD, ANNOTATION_TYPE, IMPLICIT_CLASS). If Tree.CLASS_KINDS gains a new kind later, this check would subscribe to it but silently ignore it in the switch (falling into the default branch), leaving the classMethodsStack unbalanced. Consider centralizing the handling to avoid divergence from CLASS_KINDS.

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.
Unblock → Override a blocking verdict and allow merging.

Comment with these commands to change the behavior for this request:

Auto-apply Compact Unblock
gitar auto-apply:on         
gitar display:verbose         
gitar unblock         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

sonarqube-next Bot commented Aug 3, 2026

Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
1 New issue

See analysis details on SonarQube

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant