Skip to content

SONARJAVA-6412 Implement Visitor for grouping beans per types - #5987

Merged
NoemieBenard merged 13 commits into
epic-SONARJAVA-6237from
nb/sonarjava-6412-type-to-bean-names-index
Aug 25, 2026
Merged

SONARJAVA-6412 Implement Visitor for grouping beans per types#5987
NoemieBenard merged 13 commits into
epic-SONARJAVA-6237from
nb/sonarjava-6412-type-to-bean-names-index

Conversation

@NoemieBenard

@NoemieBenard NoemieBenard commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary by Gitar

  • New features:
    • Added TypeToBeanNamesIndexGatherer to map type hierarchies of Spring beans to bean names
  • Refactorings:
    • Extracted Spring bean and method name resolution helpers into SpringUtils
  • Tests:
    • Added comprehensive unit tests in TypeToBeanNamesIndexGathererTest covering stereotype and @Bean methods

This will update automatically on new commits.

@hashicorp-vault-sonar-prod

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

Copy link
Copy Markdown
Contributor

SONARJAVA-6412

Comment thread java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java Outdated
Comment thread java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java Outdated
@NoemieBenard
NoemieBenard force-pushed the nb/sonarjava-6412-type-to-bean-names-index branch 2 times, most recently from a1da0bf to c3b31d8 Compare August 21, 2026 14:30
@datadog-sonarsource

This comment has been minimized.

@NoemieBenard
NoemieBenard marked this pull request as ready for review August 24, 2026 08:11
@NoemieBenard
NoemieBenard force-pushed the nb/sonarjava-6412-type-to-bean-names-index branch from 21b680b to 856e9a3 Compare August 24, 2026 09:49
@NoemieBenard
NoemieBenard force-pushed the nb/sonarjava-6412-type-to-bean-names-index branch from 927abb3 to ddb8a45 Compare August 24, 2026 13:35

@asya-vorobeva asya-vorobeva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The logic for filling index looks correct. Please move it to existing gatherer and rename it.

* Populates {@link TypeToBeanNamesIndex} by mapping every type in a bean's hierarchy
* (concrete class, superclasses, interfaces) to the bean's name.
*/
public class TypeToBeanNamesIndexGatherer extends SpringContextModelGatherer {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the point of implementing this visitor as separated one? We can fill needed data in existing BeanDefinitionGatherer as we already have all the needed machinery there.

@asya-vorobeva asya-vorobeva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💯

@NoemieBenard
NoemieBenard force-pushed the nb/sonarjava-6412-type-to-bean-names-index branch from fac106b to 2f87eb6 Compare August 25, 2026 11:58
@gitar-bot

gitar-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 8 resolved / 8 findings

Implements TypeToBeanNamesIndexGatherer to map Spring bean types and extracts resolution helpers into SpringUtils, addressing multiple null-guard, caching, and constructor injection findings.

✅ 8 resolved
Edge Case: resolveStereotypeBeanName lacks null-guard on annotation value

📄 java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java:105-109 📄 java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java:134
In SpringUtils.resolveStereotypeBeanName the stream does .map(v -> (String) v.value()) followed by .filter(s -> !s.isBlank()) with no null check, so a null annotation value would throw NPE. The sibling method resolveBeanMethodName guards this with s != null && !s.isBlank(). Although a null value is unlikely from the semantic model, make the two methods consistent by filtering nulls (e.g. .filter(s -> s != null && !s.isBlank())).

Quality: @Bean/stereotype name aliases beyond the first are dropped

📄 java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java:122-136 📄 java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java:101-115 📄 java-frontend/src/main/java/org/sonar/java/model/springcontext/TypeToBeanNamesIndexGatherer.java:55-64
resolveBeanMethodName returns only arr[0] for @Bean(name={"a","b"}), and resolveStereotypeBeanName returns a single name. Spring treats additional entries as bean aliases, so the type-to-bean-names index will not contain those aliases and lookups by an alias name will miss the bean. If alias-based resolution is in scope for this index, register all names; otherwise this is acceptable and could be documented.

Edge Case: extractBeanName drops null-guard on annotation value

📄 java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java:257-261
extractBeanName maps (String) v.value() then filters with .filter(s -> !s.isBlank()), which throws NPE if the annotation value is null. This re-introduces the exact defect previously fixed in SpringUtils.resolveStereotypeBeanName, which guards with s != null && !s.isBlank() (and the sibling @bean logic at lines 287 correctly keeps s != null). Add the null check for consistency and safety.

Quality: Bean-name resolution duplicated instead of reusing SpringUtils

📄 java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java:253-267
extractBeanName/defaultBeanName and the inline @bean name logic reimplement what SpringUtils.resolveStereotypeBeanName and resolveBeanMethodName already do. TypeToBeanNamesIndexGatherer still resolves bean names via those SpringUtils helpers, so the two gatherers now have separate code paths that must stay in sync — if they diverge, bean names in the registry won't match names indexed by type (as already happened with the dropped null-guard above). Consider delegating to the shared SpringUtils methods to keep both gatherers consistent.

Edge Case: Single-constructor injection skipped when any other dep exists

📄 java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java:329-343
The implicit single-constructor injection is only applied when deps.isEmpty(), so a @Component that has both an @Autowired field (or @Autowired setter) and a single non-default constructor gets its constructor parameters dropped from dependingBeans — Spring injects both in that case. Trigger: @Component class A { @Autowired Foo foo; A(Bar bar) {...} } yields only the Foo edge, silently losing the Bar dependency from the model other rules will consume. Collect the single unannotated constructor's parameters unconditionally (only skipping when a constructor/method is already @Autowired), instead of gating on deps.isEmpty().

...and 3 more resolved from earlier reviews

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

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

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

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown
Contributor

@NoemieBenard
NoemieBenard merged commit 94c700e into epic-SONARJAVA-6237 Aug 25, 2026
15 checks passed
@NoemieBenard
NoemieBenard deleted the nb/sonarjava-6412-type-to-bean-names-index branch August 25, 2026 12:17
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.

2 participants