From a8e4fcc74a59b62cf3a422b82caa82bcef4d287b Mon Sep 17 00:00:00 2001 From: Asya Vorobeva Date: Mon, 1 Jun 2026 14:36:52 +0200 Subject: [PATCH 01/15] SONARJAVA-6410 Create SpringContextModel and related classes (#5646) Co-authored-by: Claude Sonnet 4.6 From dfe0a63e678b21473cec002528ea4dbf14fd356e Mon Sep 17 00:00:00 2001 From: Asya Vorobeva Date: Mon, 1 Jun 2026 14:36:52 +0200 Subject: [PATCH 02/15] SONARJAVA-6410 Create SpringContextModel and related classes (#5646) Co-authored-by: Claude Sonnet 4.6 From 4416621251e571120cc35741148c475ce168cdae Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Wed, 3 Jun 2026 13:03:06 +0200 Subject: [PATCH 03/15] Add ComponentScanPackageCollector - Add ComponentScanPackageCollector visitor in spring/context package: collects @ComponentScan and @SpringBootApplication scanned packages per module and writes them to ProjectPackageScan in the shared SpringContextModel. Supports incremental analysis via cache. This visitor's usage will replace existing SpringBeansShouldBeAccessible visitor in future PRs. Co-Authored-By: Claude Sonnet 4.6 --- .../ComponentScanPackageCollector.java | 214 ++++++++++++++++++ .../springcontext/ProjectPackageScan.java | 11 + .../springcontext/SpringContextModel.java | 1 - 3 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java b/java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java new file mode 100644 index 00000000000..295bafcdfd8 --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java @@ -0,0 +1,214 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks.spring.context; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.java.ast.visitors.SubscriptionVisitor; +import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.springcontext.SpringContextModel; +import org.sonar.plugins.java.api.InputFileScannerContext; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.ModuleScannerContext; +import org.sonar.plugins.java.api.internal.EndOfAnalysis; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.SymbolMetadata; +import org.sonar.plugins.java.api.tree.ClassTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonarsource.analyzer.commons.collections.SetUtils; + +/** + * Collects packages registered for Spring component scanning and stores them in + * {@link org.sonar.java.model.springcontext.ProjectPackageScan} within the shared + * {@link SpringContextModel}. + * + *

Scanned packages are derived from: + *

    + *
  • {@code @ComponentScan} attributes: {@code basePackages}, {@code basePackageClasses}, {@code value}
  • + *
  • {@code @SpringBootApplication} attributes: {@code scanBasePackages}, {@code scanBasePackageClasses}
  • + *
  • {@code @SpringBootApplication} without explicit attributes — the annotated class's own package
  • + *
+ * + *

Packages are grouped by module and written to {@link org.sonar.java.model.springcontext.ProjectPackageScan} + * at the end of each module's analysis. Per-file results are cached to speed up incremental analyses. + */ +public class ComponentScanPackageCollector extends SubscriptionVisitor implements EndOfAnalysis { + + private static final Logger LOG = LoggerFactory.getLogger(ComponentScanPackageCollector.class); + + private static final String CACHE_KEY_PREFIX = "java:spring:component-scan-packages:"; + + private static final String COMPONENT_SCAN_ANNOTATION = "org.springframework.context.annotation.ComponentScan"; + private static final Set COMPONENT_SCAN_BASE_ARGUMENTS = SetUtils.immutableSetOf("basePackages", "basePackageClasses", "value"); + private static final Set SCAN_BASE_ANNOTATIONS = SetUtils.immutableSetOf("scanBasePackages", "scanBasePackageClasses"); + + private final SpringContextModel springContextModel; + + /** Packages accumulated across all files in the current module. */ + private final Set collectedPackages = new HashSet<>(); + + /** Packages found in the file currently being scanned, used for per-file cache writes. */ + private final Set packagesCollectedAtFileLevel = new HashSet<>(); + + public ComponentScanPackageCollector(SpringContextModel springContextModel) { + this.springContextModel = springContextModel; + } + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.CLASS, Tree.Kind.INTERFACE); + } + + @Override + public boolean scanWithoutParsing(InputFileScannerContext inputFileScannerContext) { + return readFromCache(inputFileScannerContext).map(packages -> { + collectedPackages.addAll(packages); + return true; + }).orElse(false); + } + + @Override + public void setContext(JavaFileScannerContext context) { + packagesCollectedAtFileLevel.clear(); + super.setContext(context); + } + + @Override + public void visitNode(Tree tree) { + ClassTree classTree = (ClassTree) tree; + if (classTree.simpleName() == null) { + return; + } + + SymbolMetadata metadata = classTree.symbol().metadata(); + collectFromComponentScan(metadata); + collectFromSpringBootApplication(classTree.symbol(), metadata); + } + + @Override + public void leaveFile(JavaFileScannerContext context) { + if (context.getCacheContext().isCacheEnabled()) { + writeToCache(context, packagesCollectedAtFileLevel); + } + packagesCollectedAtFileLevel.clear(); + } + + @Override + public void endOfAnalysis(ModuleScannerContext context) { + springContextModel.getProjectPackageScan().addPackages(context.getModuleKey(), collectedPackages); + } + + private void collectFromComponentScan(SymbolMetadata metadata) { + List componentScanAttributes = metadata.valuesForAnnotation(COMPONENT_SCAN_ANNOTATION); + if (componentScanAttributes == null) { + return; + } + componentScanAttributes.stream() + .filter(v -> COMPONENT_SCAN_BASE_ARGUMENTS.contains(v.name())) + .forEach(this::addAnnotationValueToCollectedPackages); + } + + private void collectFromSpringBootApplication(Symbol classSymbol, SymbolMetadata metadata) { + if (!metadata.isAnnotatedWith(SpringUtils.SPRING_BOOT_APP_ANNOTATION)) { + return; + } + var packages = targetedPackages(packageNameOf(classSymbol), metadata, packagesCollectedAtFileLevel.isEmpty()); + collectedPackages.addAll(packages); + packagesCollectedAtFileLevel.addAll(packages); + } + + private static List targetedPackages(String classPackageName, SymbolMetadata metadata, boolean useOwnPackageAsFallback) { + var scanBaseValues = Objects.requireNonNull(metadata.valuesForAnnotation(SpringUtils.SPRING_BOOT_APP_ANNOTATION)).stream() + .filter(v -> SCAN_BASE_ANNOTATIONS.contains(v.name()) && v.value() instanceof Object[]) + .toList(); + + List packages = new ArrayList<>(); + for (SymbolMetadata.AnnotationValue value : scanBaseValues) { + boolean isClassBased = "scanBasePackageClasses".equals(value.name()); + for (Object element : (Object[]) value.value()) { + if (!isClassBased && element instanceof String s) { + packages.add(s); + } else if (isClassBased && element instanceof Symbol s) { + packages.add(packageNameOf(s)); + } + } + } + if (scanBaseValues.isEmpty()) { + // Without explicit scan attributes, @SpringBootApplication scans its own package — but only if + // no packages were already collected via @ComponentScan on the same class. + return useOwnPackageAsFallback ? Collections.singletonList(classPackageName) : List.of(); + } + return packages; + } + + private void addAnnotationValueToCollectedPackages(SymbolMetadata.AnnotationValue annotationValue) { + if (annotationValue.value() instanceof Object[] objects) { + for (Object o : objects) { + if (o instanceof String oString) { + collectedPackages.add(oString); + packagesCollectedAtFileLevel.add(oString); + } else if (o instanceof Symbol oSymbol) { + var pkg = packageNameOf(oSymbol); + collectedPackages.add(pkg); + packagesCollectedAtFileLevel.add(pkg); + } + } + } + } + + private static String packageNameOf(Symbol symbol) { + Symbol owner = symbol.owner(); + while (!owner.isPackageSymbol()) { + owner = owner.owner(); + } + return owner.name(); + } + + private static String cacheKey(InputFile inputFile) { + return CACHE_KEY_PREFIX + inputFile.key(); + } + + private static void writeToCache(InputFileScannerContext context, Set packages) { + var cacheKey = cacheKey(context.getInputFile()); + var data = String.join(";", packages).getBytes(StandardCharsets.UTF_8); + try { + context.getCacheContext().getWriteCache().write(cacheKey, data); + } catch (IllegalArgumentException e) { + LOG.trace("Tried to write multiple times to cache key '{}'. Ignoring writes after the first.", cacheKey); + } + } + + private static Optional> readFromCache(InputFileScannerContext context) { + var cacheKey = cacheKey(context.getInputFile()); + var bytes = context.getCacheContext().getReadCache().readBytes(cacheKey); + if (bytes != null) { + context.getCacheContext().getWriteCache().copyFromPrevious(cacheKey); + return Optional.of(Arrays.asList(new String(bytes, StandardCharsets.UTF_8).split(";"))); + } + return Optional.empty(); + } +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ProjectPackageScan.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ProjectPackageScan.java index 1bfd45958ec..2c2a25971be 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ProjectPackageScan.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ProjectPackageScan.java @@ -16,6 +16,7 @@ */ package org.sonar.java.model.springcontext; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -41,6 +42,16 @@ public void addPackage(String module, String packageName) { packagesScannedBySpringPerModule.computeIfAbsent(module, k -> new HashSet<>()).add(packageName); } + /** + * Registers all packages in the given collection as scanned by Spring for the given module. + * + * @param module the module in which the component scan is configured + * @param packageNames the package names to register + */ + public void addPackages(String module, Collection packageNames) { + packagesScannedBySpringPerModule.computeIfAbsent(module, k -> new HashSet<>()).addAll(packageNames); + } + /** * Returns the set of packages scanned by Spring for the given module. * diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModel.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModel.java index c25706c762c..675b8b6c670 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModel.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModel.java @@ -61,5 +61,4 @@ public TypeToBeanNamesIndex getTypeToBeanNamesIndex() { public EntityClassToPropertiesIndex getEntityClassToPropertiesIndex() { return entityClassToPropertiesIndex; } - } From e9aa7ba05dc7d3f0147e7b058789a108c92f0d70 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 09:47:54 +0200 Subject: [PATCH 04/15] Add tests for new visitor and move SpringUtils, UnitTestUtils to java-frontend - Move SpringUtils and UnitTestUtils from java-checks helpers to java-frontend model package - Rename ComponentScanPackageCollector to ComponentScanPackageGatherer - Add SpringContextModel provisioning support to CheckVerifier - Add unit tests for ComponentScanPackageGatherer Co-Authored-By: Claude Sonnet 4.6 --- .../ComponentScanWithBasePackages.java | 17 ++ .../spring/context/NoScanAnnotations.java | 4 + .../SpringBootAppNoScanAttributes.java | 7 + ...pringBootAppWithComponentScanPackages.java | 9 + ...ringBootAppWithScanBasePackageClasses.java | 8 + .../SpringBootAppWithScanBasePackages.java | 6 + .../java/checks/verifier/CheckVerifier.java | 3 + .../internal/InternalCheckVerifier.java | 6 + .../verifier/internal/JavaCheckVerifier.java | 11 + ...ssertThrowsInsteadOfTryCatchFailCheck.java | 2 +- .../AssertionsInProductionCodeCheck.java | 2 +- .../checks/ConfigurationBeanNamesCheck.java | 2 +- .../sonar/java/checks/EmptyMethodsCheck.java | 2 +- .../java/checks/LeastSpecificTypeCheck.java | 2 +- .../checks/TestsInSeparateFolderCheck.java | 2 +- .../java/checks/TooManyParametersCheck.java | 2 +- .../helpers/AbstractAssertionVisitor.java | 4 +- .../checks/naming/BadTestClassNameCheck.java | 2 +- .../checks/naming/BadTestMethodNameCheck.java | 2 +- .../AsyncMethodsCalledViaThisCheck.java | 2 +- ...AsyncMethodsOnConfigurationClassCheck.java | 2 +- .../spring/AsyncMethodsReturnTypeCheck.java | 2 +- ...structorWhenMultipleConstructorsCheck.java | 2 +- .../AutowiredOnMultipleConstructorsCheck.java | 2 +- .../AvoidQualifierOnBeanMethodsCheck.java | 2 +- ...eforeAndAfterTransactionContractCheck.java | 2 +- ...ldOnlyBeAppliedToConcreteClassesCheck.java | 2 +- ...lerWithRestControllerReplacementCheck.java | 2 +- .../ControllerWithSessionAttributesCheck.java | 2 +- ...BeanMethodInvocationWithoutProxyCheck.java | 2 +- .../spring/FieldDependencyInjectionCheck.java | 2 +- ...NonSingletonAutowiredInSingletonCheck.java | 2 +- ...leInjectedFieldsHaveDefaultValueCheck.java | 2 +- .../RequestMappingMethodPublicCheck.java | 2 +- .../SpringBeanNamingConventionCheck.java | 2 +- .../SpringBeansShouldBeAccessibleCheck.java | 2 +- ...ComponentWithNonAutowiredMembersCheck.java | 4 +- .../SpringComponentWithWrongScopeCheck.java | 6 +- ...ConfigurationWithAutowiredFieldsCheck.java | 2 +- .../SpringConstructorInjectionCheck.java | 2 +- .../SpringIncompatibleTransactionalCheck.java | 2 +- ...StaticFieldInjectionNotSupportedCheck.java | 2 +- .../spring/StatusCodesOnResponseCheck.java | 2 +- ...uperfluousResponseBodyAnnotationCheck.java | 2 +- .../TransactionalMethodVisibilityCheck.java | 2 +- ...sePageableParameterForPagedQueryCheck.java | 2 +- ...tationShouldInjectPropertyOrSpELCheck.java | 2 +- ...ractJUnit5NotCompliantModifierChecker.java | 2 +- .../AbstractOneExpectedExceptionRule.java | 2 +- .../AssertJChainSimplificationIndex.java | 2 +- .../AssertJConsecutiveAssertionCheck.java | 2 +- .../tests/AssertionCompareToSelfCheck.java | 2 +- .../tests/AssertionFailInCatchBlockCheck.java | 2 +- .../tests/AssertionInThreadRunCheck.java | 2 +- .../tests/AssertionInTryCatchCheck.java | 2 +- .../checks/tests/AssertionTypesCheck.java | 2 +- .../tests/AssertionsCompletenessCheck.java | 2 +- .../checks/tests/AssertionsInTestsCheck.java | 2 +- .../checks/tests/ExpectedExceptionCheck.java | 2 +- .../tests/JUnit45MethodAnnotationCheck.java | 2 +- .../tests/JunitNestedAnnotationCheck.java | 2 +- .../NoJUnit4AssertionsInJUnit5TestsCheck.java | 2 +- .../OneExpectedRuntimeExceptionCheck.java | 2 +- ...tAnnotationWithExpectedExceptionCheck.java | 2 +- .../checks/tests/TooManyAssertionsCheck.java | 6 +- .../org/sonar/java/filters/SpringFilter.java | 2 +- .../ComponentScanPackageGathererTest.java | 209 ++++++++++++++++++ .../org/sonar/java/model}/SpringUtils.java | 4 +- .../org/sonar/java/model}/UnitTestUtils.java | 3 +- .../ComponentScanPackageGatherer.java | 19 +- .../model/springcontext/package-info.java | 8 +- .../sonar/java/model}/SpringUtilsTest.java | 2 +- .../sonar/java/model}/UnitTestUtilsTest.java | 4 +- 73 files changed, 361 insertions(+), 85 deletions(-) create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java create mode 100644 java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java rename {java-checks/src/main/java/org/sonar/java/checks/helpers => java-frontend/src/main/java/org/sonar/java/model}/SpringUtils.java (97%) rename {java-checks/src/main/java/org/sonar/java/checks/helpers => java-frontend/src/main/java/org/sonar/java/model}/UnitTestUtils.java (99%) rename java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java => java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java (92%) rename {java-checks/src/test/java/org/sonar/java/checks/helpers => java-frontend/src/test/java/org/sonar/java/model}/SpringUtilsTest.java (97%) rename {java-checks/src/test/java/org/sonar/java/checks/helpers => java-frontend/src/test/java/org/sonar/java/model}/UnitTestUtilsTest.java (90%) diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java new file mode 100644 index 00000000000..2730eb1343b --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java @@ -0,0 +1,17 @@ +package checks.spring.context; + +import org.springframework.context.annotation.ComponentScan; + +@ComponentScan({"com.example.service", "com.example.repository"}) +class AppConfigValue {} + +@ComponentScan(basePackages = {"com.example.controller", "com.example.domain"}) +class AppConfigBasePackages {} + +@ComponentScan(basePackages = "com.example.single") +class AppConfigSingleBasePackage {} + +@ComponentScan(basePackageClasses = PackageMarker.class) +class AppConfigBasePackageClasses {} + +interface PackageMarker {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java new file mode 100644 index 00000000000..5e0f4864e61 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java @@ -0,0 +1,4 @@ +package checks.spring.context; + +// A class with no Spring scanning annotations — should not contribute any packages. +interface NoScanAnnotations {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java new file mode 100644 index 00000000000..75f0e5c7b28 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java @@ -0,0 +1,7 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +// No scanBasePackages / scanBasePackageClasses — the class's own package should be collected. +@SpringBootApplication +class SpringBootAppNoScanAttributes {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java new file mode 100644 index 00000000000..34ca7810855 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java @@ -0,0 +1,9 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +// @ComponentScan with explicit packages overrides @SpringBootApplication's default (own package). +@SpringBootApplication +@ComponentScan(basePackages = {"com.example.service", "com.example.web"}) +class SpringBootAppWithComponentScanPackages {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java new file mode 100644 index 00000000000..269d96c8b23 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java @@ -0,0 +1,8 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = ScanBaseMarker.class) +class SpringBootAppWithScanBasePackageClasses {} + +interface ScanBaseMarker {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java new file mode 100644 index 00000000000..149922186a3 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java @@ -0,0 +1,6 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = {"com.example.service", "com.example.web"}) +class SpringBootAppWithScanBasePackages {} diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java index 2fbf9b8781d..c0335aec005 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java @@ -25,6 +25,7 @@ import org.sonar.api.batch.sensor.cache.WriteCache; import org.sonar.java.checks.verifier.internal.InternalCheckVerifier; import org.sonar.java.checks.verifier.internal.JavaCheckVerifier; +import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.plugins.java.api.JavaFileScanner; import org.sonar.plugins.java.api.tree.CompilationUnitTree; @@ -304,4 +305,6 @@ default CheckVerifier withCompilationUnitModifier(Consumer * Verifies that no issues are raised by the rule(s) on the given file(s). */ void verifyNoIssues(); + + SpringContextModel getSpringContextModel(); } diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java index 67c7e0a12bd..756d3bccf07 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java @@ -48,6 +48,7 @@ import org.sonar.java.caching.JavaWriteCacheImpl; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.model.JavaVersionImpl; +import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.AnalyzerMessage.TextSpan; import org.sonar.java.reporting.JavaQuickFix; @@ -330,6 +331,11 @@ private void verifyAll() { checkIssues(issues, quickFixes); } + @Override + public SpringContextModel getSpringContextModel() { + throw new UnsupportedOperationException("Not implemented!"); + } + private void checkIssues(Set issues, Map> quickFixes) { if (expectations.expectNoIssues()) { assertNoIssues(issues); diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java index bd1ff4e5dd8..1f4f242d576 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java @@ -40,6 +40,7 @@ import org.sonar.java.caching.JavaWriteCacheImpl; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.model.JavaVersionImpl; +import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.telemetry.NoOpTelemetry; @@ -95,6 +96,8 @@ public static JavaCheckVerifier newInstance() { private ReadCache readCache; private WriteCache writeCache; private File rootDirectory; + @VisibleForTesting + private SpringContextModel springContextModel; private MultiFileVerifier createVerifier() { MultiFileVerifier verifier = MultiFileVerifier.create(Paths.get(files.get(0).uri()), UTF_8); @@ -104,7 +107,10 @@ private MultiFileVerifier createVerifier() { List visitors = new ArrayList<>(checks); CommentLinesVisitor commentLinesVisitor = new CommentLinesVisitor(); visitors.add(commentLinesVisitor); + + springContextModel = new SpringContextModel(); SonarComponents sonarComponents = CheckVerifierUtils.sonarComponents(isCacheEnabled, readCache, writeCache, rootDirectory); + sonarComponents.setSpringContextModel(springContextModel); VisitorsBridgeForTests.Builder visitorsBridgeBuilder = new VisitorsBridgeForTests.Builder(visitors) .withJavaVersion(actualVersion) .withSonarComponents(sonarComponents) @@ -387,4 +393,9 @@ public void verifyNoIssues() { createVerifier().assertNoIssuesRaised(); } + @Override + public SpringContextModel getSpringContextModel() { + return springContextModel; + } + } diff --git a/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java b/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java index 160948e5a71..3dccf49ee61 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java @@ -17,7 +17,7 @@ package org.sonar.java.checks; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.BaseTreeVisitor; import org.sonar.plugins.java.api.tree.BlockTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java index 44ba1545d4c..743e681b817 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java @@ -23,7 +23,7 @@ import org.sonar.check.Rule; import org.sonar.java.annotations.VisibleForTesting; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonar.java.checks.methods.AbstractMethodDetection; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.JavaFileScannerContext.Location; diff --git a/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java index 7ed8af1c484..7e38e4931ef 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java @@ -19,7 +19,7 @@ import java.util.HashSet; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java index fcdb8d4d4b9..6bca9849d57 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.LineUtils; import org.sonar.java.model.ModifiersUtils; import org.sonar.java.reporting.AnalyzerMessage; diff --git a/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java index 2c0dad7b9ec..fd2211ec370 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java index 159c56a3f4a..9a52b41a570 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java @@ -23,7 +23,7 @@ import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.isTestClass; +import static org.sonar.java.model.UnitTestUtils.isTestClass; @Rule(key = "S3414") public class TestsInSeparateFolderCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java index a9cc403479e..2e0ac09b87c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java b/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java index 3c5b13d9e94..079e9317e06 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java +++ b/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java @@ -24,8 +24,8 @@ import org.sonar.plugins.java.api.tree.MethodReferenceTree; import org.sonar.plugins.java.api.tree.NewClassTree; -import static org.sonar.java.checks.helpers.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; -import static org.sonar.java.checks.helpers.UnitTestUtils.methodNameMatchesAssertionMethodPattern; +import static org.sonar.java.model.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; +import static org.sonar.java.model.UnitTestUtils.methodNameMatchesAssertionMethodPattern; import static org.sonar.java.model.ExpressionUtils.methodName; public abstract class AbstractAssertionVisitor extends BaseTreeVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java index 121010c0a42..89e4b180d25 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java @@ -28,7 +28,7 @@ import org.sonar.plugins.java.api.tree.IdentifierTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.isTestClass; +import static org.sonar.java.model.UnitTestUtils.isTestClass; @Rule(key = "S3577") public class BadTestClassNameCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java index d7109dfc287..167bd60c7d7 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java @@ -26,7 +26,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; @Rule(key = "S3578") public class BadTestMethodNameCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java index 7f5f3a85b4c..e7ba82a660e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Map; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java index 6f4564c717a..29a0885c2d4 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java @@ -19,7 +19,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java index 341df68781d..8b8e1b53e96 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java index 4e05f768942..dcf5be0889e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java index 466b6cf87c0..b0326f1855e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java index 53d269b7ef8..15acda18813 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.expression.AssignmentExpressionTreeImpl; import org.sonar.java.model.expression.LiteralTreeImpl; import org.sonar.java.reporting.JavaQuickFix; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java index 9afcc4df60d..428f5f6fcd1 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.declaration.MethodTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java index 3ab2c2dead1..3e1c6a292c5 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java @@ -20,7 +20,7 @@ import java.util.Set; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java index 7378e2601ea..37c2a8963df 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java @@ -21,7 +21,7 @@ import java.util.Optional; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java index bb3dd866c78..e20ea872d70 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java index 0a2e504469e..42c0257c260 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java @@ -21,7 +21,7 @@ import java.util.Optional; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.AnnotationTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java index 7521c2c91c6..cb06b99e0bc 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java @@ -19,7 +19,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.InjectionHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java index 0cfc2e49865..c8175e46fc8 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java @@ -22,7 +22,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java index ce2ae0e0bb9..77c67b4d341 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java @@ -25,7 +25,7 @@ import org.sonar.java.checks.helpers.ExpressionsHelper; import org.sonar.java.checks.helpers.MethodTreeUtils; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java index 2576cf5bd57..c391e07b741 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java index 0ad6dd76b48..f1c62ba9f7d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java @@ -23,7 +23,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java index 7921c91aaf8..55fca00e871 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java @@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.DefaultJavaFileScannerContext; import org.sonar.java.model.DefaultModuleScannerContext; import org.sonar.java.reporting.AnalyzerMessage; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java index 8f0c1e9d97e..dd9a96d9663 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java @@ -24,7 +24,7 @@ import java.util.stream.Collectors; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; -import static org.sonar.java.checks.helpers.SpringUtils.isScopeSingleton; +import static org.sonar.java.model.SpringUtils.isScopeSingleton; @Rule(key = "S3749") public class SpringComponentWithNonAutowiredMembersCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java index f00760e7dfb..5448dc983d7 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java @@ -19,14 +19,14 @@ import java.util.Collections; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.SpringUtils.SCOPE_ANNOTATION; -import static org.sonar.java.checks.helpers.SpringUtils.isScopeSingleton; +import static org.sonar.java.model.SpringUtils.SCOPE_ANNOTATION; +import static org.sonar.java.model.SpringUtils.isScopeSingleton; @Rule(key = "S3750") public class SpringComponentWithWrongScopeCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java index 8734589db61..9e9c0cfffd9 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java index 50b1017e121..d62fc9caa6f 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java index 7d3427c465f..c6e805c88b8 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java @@ -27,7 +27,7 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java index cf624783341..6d84bdea234 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java @@ -21,7 +21,7 @@ import java.util.stream.Stream; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.tree.AnnotationTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java index 789b3d2ae44..53e76ab9c8c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java index 4118f32c7f3..1242c420dab 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java index b8f619e7491..d70f5586a79 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java @@ -22,7 +22,7 @@ import java.util.Optional; import java.util.function.Function; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.DependencyVersionAware; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.Version; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java index 71c06b02103..3970198e28c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.model.declaration.MethodTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java index a613a0fd9b7..264a1d912e8 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java @@ -20,7 +20,7 @@ import java.util.stream.Stream; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java index 324be9fe19f..b8296d74c46 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonar.java.model.ModifiersUtils; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.JavaQuickFix; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java index d9899a27da4..c622e56fe13 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TryStatementTree; -import static org.sonar.java.checks.helpers.UnitTestUtils.findFail; +import static org.sonar.java.model.UnitTestUtils.findFail; public abstract class AbstractOneExpectedExceptionRule extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java index 012410c853c..fc2a0fe29d7 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java @@ -23,7 +23,7 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.MapBuilder; import org.sonar.java.model.ExpressionUtils; import org.sonar.java.model.LiteralUtils; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java index c611e05c82a..cfd51a1b75c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java @@ -40,7 +40,7 @@ import org.sonar.plugins.java.api.tree.StatementTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; @Rule(key = "S5853") public class AssertJConsecutiveAssertionCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java index d69f3146601..6550cfdbcc8 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java @@ -23,7 +23,7 @@ import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.SetUtils; import org.sonar.java.model.SyntacticEquivalence; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java index 349253a6845..0a92a4afa00 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java @@ -22,7 +22,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.FAIL_METHOD_MATCHER; +import static org.sonar.java.model.UnitTestUtils.FAIL_METHOD_MATCHER; @Rule(key = "S3658") public class AssertionFailInCatchBlockCheck extends AbstractMethodDetection { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java index be20d574e64..31eded338ff 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java @@ -27,7 +27,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.COMMON_ASSERTION_MATCHER; +import static org.sonar.java.model.UnitTestUtils.COMMON_ASSERTION_MATCHER; @Rule(key = "S2186") public class AssertionInThreadRunCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java index d4bb5ac8efc..fdc509da0c9 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.TryStatementTree; import org.sonar.plugins.java.api.tree.VariableTree; -import static org.sonar.java.checks.helpers.UnitTestUtils.COMMON_ASSERTION_MATCHER; +import static org.sonar.java.model.UnitTestUtils.COMMON_ASSERTION_MATCHER; @Rule(key = "S5779") public class AssertionInTryCatchCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java index 2be08a4a633..6c3bf0b9c80 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java @@ -21,7 +21,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java index 4ca75fe0eb9..d43b8fd7171 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java @@ -44,7 +44,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static java.util.Collections.emptyList; -import static org.sonar.java.checks.helpers.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; import static org.sonar.plugins.java.api.semantic.MethodMatchers.ANY; @Rule(key = "S2970") diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java index 77553fc1ec5..fe70ef638a2 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java @@ -43,7 +43,7 @@ import org.sonar.plugins.java.api.tree.Tree; import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.sonar.java.checks.helpers.UnitTestUtils.isUnitTest; +import static org.sonar.java.model.UnitTestUtils.isUnitTest; @Rule(key = "S2699") public class AssertionsInTestsCheck extends BaseTreeVisitor implements JavaFileScanner { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java index e3ef0a7cac2..576b39e6fd4 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java @@ -32,7 +32,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.MethodTree; -import static org.sonar.java.checks.helpers.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; +import static org.sonar.java.model.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; @Rule(key = "S5776") public class ExpectedExceptionCheck extends AbstractMethodDetection { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java index 10d145f5138..75c9f8c7659 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.MapBuilder; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java index bfd259761ce..b935ba4ea8b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.UnitTestUtils; +import org.sonar.java.model.UnitTestUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java index 9693e262800..1a6ef748999 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java @@ -28,7 +28,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.JUNIT5_TEST_ANNOTATIONS; +import static org.sonar.java.model.UnitTestUtils.JUNIT5_TEST_ANNOTATIONS; /** * Check that JUnit Jupiter (JUnit 5) tests do not use JUnit 4 assertions. 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 785b151226c..9cef58efabb 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 @@ -23,7 +23,7 @@ import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.FAIL_METHOD_MATCHER; +import static org.sonar.java.model.UnitTestUtils.FAIL_METHOD_MATCHER; @Rule(key = "S5778") public class OneExpectedRuntimeExceptionCheck extends AbstractOneExpectedExceptionRule { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java index a25f495a273..26917f3c379 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java @@ -33,7 +33,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; +import static org.sonar.java.model.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; @Rule(key = "S5777") public class TestAnnotationWithExpectedExceptionCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java index 64293a55fc3..93b7f3e460c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java @@ -39,9 +39,9 @@ import org.sonar.plugins.java.api.tree.Modifier; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.checks.helpers.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; -import static org.sonar.java.checks.helpers.UnitTestUtils.isUnitTest; -import static org.sonar.java.checks.helpers.UnitTestUtils.methodNameMatchesAssertionMethodPattern; +import static org.sonar.java.model.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; +import static org.sonar.java.model.UnitTestUtils.isUnitTest; +import static org.sonar.java.model.UnitTestUtils.methodNameMatchesAssertionMethodPattern; import static org.sonar.java.model.ExpressionUtils.methodName; @Rule(key = "S5961") diff --git a/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java b/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java index fb2e92c9a1b..55fddc6d3bf 100644 --- a/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java +++ b/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java @@ -24,7 +24,7 @@ import org.sonar.java.checks.ServletInstanceFieldCheck; import org.sonar.java.checks.TooManyParametersCheck; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.checks.helpers.SpringUtils; +import org.sonar.java.model.SpringUtils; import org.sonar.java.checks.naming.BadMethodNameCheck; import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java new file mode 100644 index 00000000000..402e91064b5 --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java @@ -0,0 +1,209 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks.spring.context; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.java.checks.helpers.HashCacheTestHelper; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.sonar.java.checks.verifier.internal.InternalReadCache; +import org.sonar.java.checks.verifier.internal.InternalWriteCache; +import org.sonar.java.model.springcontext.ComponentScanPackageGatherer; +import org.sonar.java.model.springcontext.SpringContextModel; +import org.sonar.api.batch.sensor.cache.ReadCache; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; + +class ComponentScanPackageGathererTest { + + private static final String BASE_PATH = "checks/spring/context/"; + // Module key used by CheckVerifier's SonarComponents (no projectDefinition → empty string) + private static final String MODULE_KEY = ""; + + private SpringContextModel model; + private ComponentScanPackageGatherer gatherer; + private InternalReadCache readCache; + private InternalWriteCache writeCache; + + @BeforeEach + void setUp() { + gatherer = new ComponentScanPackageGatherer(); + readCache = new InternalReadCache(); + writeCache = new InternalWriteCache().bind(readCache); + } + + // ---- @ComponentScan ------------------------------------------------------- + + @Test + void componentScan_value_and_basePackages_attributes_are_collected() { + scan(mainCodeSourcesPath(BASE_PATH + "ComponentScanWithBasePackages.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder( + "com.example.service", + "com.example.repository", + "com.example.controller", + "com.example.domain", + "com.example.single", + "checks.spring.context" // basePackageClasses = PackageMarker.class → its package + ); + } + + // ---- @SpringBootApplication ----------------------------------------------- + + @Test + void springBootApplication_without_scan_attributes_collects_own_package() { + scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("checks.spring.context"); + } + + @Test + void springBootApplication_scanBasePackages_are_collected() { + scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + @Test + void springBootApplication_scanBasePackageClasses_resolves_to_class_package() { + scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackageClasses.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("checks.spring.context"); + } + + @Test + void componentScan_with_packages_overrides_springBootApplication_default_own_package() { + scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithComponentScanPackages.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + // ---- No annotations ------------------------------------------------------- + + @Test + void no_scan_annotations_collects_nothing() { + scan(mainCodeSourcesPath(BASE_PATH + "NoScanAnnotations.java")); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); + } + + // ---- Multiple files ------------------------------------------------------- + + @Test + void packages_from_multiple_files_are_merged() { + scan( + mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java"), + mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java") + ); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("checks.spring.context", "com.example.service", "com.example.web"); + } + + // ---- Caching -------------------------------------------------------------- + + @Test + void packages_are_written_to_cache_after_scanning() { + scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); + + assertThat(writeCache.getData().keySet()) + .isNotEmpty() + .anySatisfy(key -> assertThat(key).endsWith(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); + } + + @Test + void scanWithoutParsing_populates_model_from_cache() throws NoSuchAlgorithmException, IOException { + String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); + + // First pass: scan with a read cache that has the content hash + ReadCache firstReadCache = HashCacheTestHelper.internalReadCacheFromFile(filePath); + InternalWriteCache firstWriteCache = new InternalWriteCache().bind(firstReadCache); + CheckVerifier checkVerifier = CheckVerifier.newVerifier() + .withCache(firstReadCache, firstWriteCache) + .onFiles(filePath) + .withCheck(gatherer); + checkVerifier.verifyNoIssues(); + + assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + + // Second pass: the file is SAME — scanWithoutParsing restores packages from cache + var populatedReadCache = new InternalReadCache().putAll(firstWriteCache); + var secondGatherer = new ComponentScanPackageGatherer(); + CheckVerifier secondCheckVerifier = CheckVerifier.newVerifier() + .withCache(populatedReadCache, new InternalWriteCache().bind(populatedReadCache)) + .addFiles(InputFile.Status.SAME, filePath) + .withCheck(secondGatherer); + secondCheckVerifier.verifyNoIssues(); + + assertThat(secondCheckVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + @Test + void scanWithoutParsing_returns_false_on_cache_miss() throws NoSuchAlgorithmException, IOException { + String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); + + // Read cache has content hash but no package data → cache miss for package key + var readCacheWithHashOnly = HashCacheTestHelper.internalReadCacheFromFile(filePath); + CheckVerifier checkVerifier = CheckVerifier.newVerifier() + .withCache(readCacheWithHashOnly, new InternalWriteCache().bind(readCacheWithHashOnly)) + .addFiles(InputFile.Status.SAME, filePath) + .withCheck(gatherer); + checkVerifier.verifyNoIssues(); + + // Full parse was forced, packages still collected correctly + assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + @Test + void duplicate_cache_write_IllegalArgumentException_is_caught() { + String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); + + var verifier = CheckVerifier.newVerifier() + .withCache(readCache, writeCache) + .onFiles(filePath) + .withCheck(gatherer); + verifier.verifyNoIssues(); + + // Second run attempts to write the same cache key; the IllegalArgumentException must be caught internally + assertThatCode(verifier::verifyNoIssues).doesNotThrowAnyException(); + } + + // ---- Helpers -------------------------------------------------------------- + + private void scan(String... filePaths) { + CheckVerifier checkVerifier = CheckVerifier.newVerifier() + .withCache(readCache, writeCache) + .onFiles(List.of(filePaths)) + .withCheck(gatherer); + checkVerifier.verifyNoIssues(); + model = checkVerifier.getSpringContextModel(); + } +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/SpringUtils.java b/java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java similarity index 97% rename from java-checks/src/main/java/org/sonar/java/checks/helpers/SpringUtils.java rename to java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java index 4250acd3ede..d639f0d6719 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/SpringUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java @@ -14,10 +14,10 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.model; import java.util.List; -import org.sonar.java.model.ExpressionUtils; + import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/UnitTestUtils.java b/java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java similarity index 99% rename from java-checks/src/main/java/org/sonar/java/checks/helpers/UnitTestUtils.java rename to java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java index c6402f358e2..fa77a03c2e2 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/UnitTestUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.model; import java.util.HashSet; import java.util.List; @@ -26,7 +26,6 @@ import javax.annotation.Nullable; import org.sonar.java.annotations.VisibleForTesting; -import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java similarity index 92% rename from java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java rename to java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java index 295bafcdfd8..091ac36bd5d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/context/ComponentScanPackageCollector.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.spring.context; +package org.sonar.java.model.springcontext; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -28,13 +28,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; -import org.sonar.java.ast.visitors.SubscriptionVisitor; -import org.sonar.java.checks.helpers.SpringUtils; -import org.sonar.java.model.springcontext.SpringContextModel; +import org.sonar.java.model.SpringUtils; import org.sonar.plugins.java.api.InputFileScannerContext; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.ModuleScannerContext; -import org.sonar.plugins.java.api.internal.EndOfAnalysis; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; @@ -56,9 +53,9 @@ *

Packages are grouped by module and written to {@link org.sonar.java.model.springcontext.ProjectPackageScan} * at the end of each module's analysis. Per-file results are cached to speed up incremental analyses. */ -public class ComponentScanPackageCollector extends SubscriptionVisitor implements EndOfAnalysis { +public class ComponentScanPackageGatherer extends SpringContextModelGatherer { - private static final Logger LOG = LoggerFactory.getLogger(ComponentScanPackageCollector.class); + private static final Logger LOG = LoggerFactory.getLogger(ComponentScanPackageGatherer.class); private static final String CACHE_KEY_PREFIX = "java:spring:component-scan-packages:"; @@ -66,18 +63,12 @@ public class ComponentScanPackageCollector extends SubscriptionVisitor implement private static final Set COMPONENT_SCAN_BASE_ARGUMENTS = SetUtils.immutableSetOf("basePackages", "basePackageClasses", "value"); private static final Set SCAN_BASE_ANNOTATIONS = SetUtils.immutableSetOf("scanBasePackages", "scanBasePackageClasses"); - private final SpringContextModel springContextModel; - /** Packages accumulated across all files in the current module. */ private final Set collectedPackages = new HashSet<>(); /** Packages found in the file currently being scanned, used for per-file cache writes. */ private final Set packagesCollectedAtFileLevel = new HashSet<>(); - public ComponentScanPackageCollector(SpringContextModel springContextModel) { - this.springContextModel = springContextModel; - } - @Override public List nodesToVisit() { return List.of(Tree.Kind.CLASS, Tree.Kind.INTERFACE); @@ -118,7 +109,7 @@ public void leaveFile(JavaFileScannerContext context) { } @Override - public void endOfAnalysis(ModuleScannerContext context) { + public void gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) { springContextModel.getProjectPackageScan().addPackages(context.getModuleKey(), collectedPackages); } diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/package-info.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/package-info.java index d7f5beee53b..df5d595e0df 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/package-info.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/package-info.java @@ -32,4 +32,10 @@ *

  • {@link org.sonar.java.model.springcontext.EntityClassToPropertiesIndex} — JPA {@code @Entity} properties
  • * */ -package org.sonar.java.model.springcontext; \ No newline at end of file +@ParametersAreNonnullByDefault +@MethodsAreNonnullByDefault +package org.sonar.java.model.springcontext; + +import org.sonar.plugins.java.api.tree.MethodsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/java-checks/src/test/java/org/sonar/java/checks/helpers/SpringUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java similarity index 97% rename from java-checks/src/test/java/org/sonar/java/checks/helpers/SpringUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java index a936b795dc7..9b84a1ba3d2 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/helpers/SpringUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.model; import org.junit.jupiter.api.Test; import org.sonar.java.model.declaration.ClassTreeImpl; diff --git a/java-checks/src/test/java/org/sonar/java/checks/helpers/UnitTestUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java similarity index 90% rename from java-checks/src/test/java/org/sonar/java/checks/helpers/UnitTestUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java index 6834ae89004..fc417a9245e 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/helpers/UnitTestUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java @@ -14,13 +14,13 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.model; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.sonar.java.checks.helpers.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; +import static org.sonar.java.model.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; class UnitTestUtilsTest { From f7b702b1548713bf0681a78f057bab42ad7937d0 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 13:26:48 +0200 Subject: [PATCH 05/15] - Register ComponentScanPackageGatherer as a SpringContextModel visitor - Add test verifying SpringContextModel is filled with ProjectPackageScan after JavaFrontend.scan Co-Authored-By: Claude Sonnet 4.6 --- .../SpringContextModelGatherers.java | 2 +- .../files/springcontext/SpringBootApp.java | 7 ++++ .../springcontext/SpringContextModelTest.java | 32 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 java-frontend/src/test/files/springcontext/SpringBootApp.java diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java index 8d012038d34..d8860c4216f 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java @@ -27,7 +27,7 @@ private SpringContextModelGatherers() { public static List getAllGatherers() { return List.of( - // example: new SampleSpringContextModelGatherer() + new ComponentScanPackageGatherer() ); } diff --git a/java-frontend/src/test/files/springcontext/SpringBootApp.java b/java-frontend/src/test/files/springcontext/SpringBootApp.java new file mode 100644 index 00000000000..702f0bc7582 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/SpringBootApp.java @@ -0,0 +1,7 @@ +package springcontext; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +class SpringBootApp { +} diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelTest.java index b207ce9f29e..bbcfbbea446 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelTest.java @@ -16,9 +16,21 @@ */ package org.sonar.java.model.springcontext; +import java.util.Collections; import org.junit.jupiter.api.Test; +import org.sonar.java.JavaFrontend; +import org.sonar.java.Measurer; +import org.sonar.java.SonarComponents; +import org.sonar.java.TestUtils; +import org.sonar.java.model.JavaVersionImpl; +import org.sonar.java.telemetry.NoOpTelemetry; +import org.sonar.java.test.classpath.TestClasspathUtils; +import org.sonar.plugins.java.api.JavaResourceLocator; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class SpringContextModelTest { @@ -32,4 +44,24 @@ void testInitialization() { assertNotNull(model.getEntityClassToPropertiesIndex(), "EntityClassToPropertiesIndex should be initialized"); } + @Test + void scan_fills_project_package_scan_in_spring_context_model() { + SpringContextModel springContextModel = new SpringContextModel(); + SonarComponents sonarComponents = TestUtils.mockSonarComponents(); + when(sonarComponents.getSpringContextModel()).thenReturn(springContextModel); + when(sonarComponents.getJavaClasspath()).thenReturn(TestClasspathUtils.DEFAULT_MODULE.getClassPath()); + when(sonarComponents.getModuleKey()).thenReturn("a"); + + JavaFrontend frontend = new JavaFrontend(new JavaVersionImpl(), sonarComponents, mock(Measurer.class), new NoOpTelemetry(), mock(JavaResourceLocator.class), null); + frontend.scan( + Collections.singletonList(TestUtils.inputFile("src/test/files/springcontext/SpringBootApp.java")), + Collections.emptyList(), + Collections.emptyList() + ); + + assertThat(springContextModel.getProjectPackageScan().getModules()).isNotEmpty(); + assertThat(springContextModel.getProjectPackageScan().getPackagesForModule("a")) + .containsExactly("springcontext"); + } + } From 62710af1601839c7759c9a635c3102e6fadacc98 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 13:34:51 +0200 Subject: [PATCH 06/15] Add Javadoc to SpringContextModelGatherers, SpringContextModelSensor and CheckVerifier#getSpringContextModel Co-Authored-By: Claude Sonnet 4.6 --- .../sonar/java/checks/verifier/CheckVerifier.java | 9 +++++++++ .../springcontext/SpringContextModelGatherers.java | 12 ++++++++++++ .../sonar/plugins/java/SpringContextModelSensor.java | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java index c0335aec005..85c5658f176 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java @@ -306,5 +306,14 @@ default CheckVerifier withCompilationUnitModifier(Consumer */ void verifyNoIssues(); + /** + * Returns the {@link SpringContextModel} that was populated during the last verification run. + * + *

    Useful in tests that need to assert on Spring context data (e.g. component-scan packages, + * bean definitions) collected by {@link org.sonar.java.model.springcontext.SpringContextModelGatherer} + * visitors alongside the check under test. + * + * @return the {@link SpringContextModel} instance used during verification + */ SpringContextModel getSpringContextModel(); } diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java index d8860c4216f..e9d2db5c1d3 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherers.java @@ -19,12 +19,24 @@ import java.util.List; import org.sonar.plugins.java.api.JavaCheck; +/** + * Registry of all {@link SpringContextModelGatherer} visitors that populate the {@link SpringContextModel} + * during a module analysis. + * + *

    Use {@link #getAllGatherers()} to obtain the full list of gatherers to be registered with the scanner. + * New gatherers should be added here as the set of Spring context data we collect grows. + */ public class SpringContextModelGatherers { private SpringContextModelGatherers() { // utility class, should not be instantiated } + /** + * Returns all gatherers that contribute data to the {@link SpringContextModel}. + * + * @return a list of {@link JavaCheck} instances, each implementing {@link SpringContextModelGatherer} + */ public static List getAllGatherers() { return List.of( new ComponentScanPackageGatherer() diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java index 0a925a651da..52a2f5dcc06 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java @@ -23,6 +23,16 @@ import org.sonar.java.jsp.Jasper; import org.sonar.java.model.springcontext.SpringContextModel; +/** + * A post-phase {@link ProjectSensor} that holds the shared {@link SpringContextModel} built during analysis. + * + *

    This sensor runs after the main Java analysis phase, ensuring that all + * {@link org.sonar.java.model.springcontext.SpringContextModelGatherer} visitors have finished populating + * the model before it can be consumed by downstream components. + * + *

    The {@link SpringContextModel} instance is injected via the IoC container and shared across all + * components that need access to Spring context information (bean definitions, component-scan packages, etc.). + */ @Phase(name = Phase.Name.POST) public class SpringContextModelSensor implements ProjectSensor { From c128e6e713c7a908827c3fecb5d405526e2f1301 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 13:54:30 +0200 Subject: [PATCH 07/15] Fix empty cache entry bug and add test for scanWithoutParsing with no scan annotations When a file has no scan annotations, an empty string was written to cache. Reading it back via String#split(";") incorrectly produced [""] instead of an empty list, causing a blank package name to be registered in ProjectPackageScan. Co-Authored-By: Claude Sonnet 4.6 --- .../ComponentScanPackageGathererTest.java | 25 +++++++++++++++++++ .../ComponentScanPackageGatherer.java | 6 ++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java index 402e91064b5..1c570429d3b 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java @@ -182,6 +182,31 @@ void scanWithoutParsing_returns_false_on_cache_miss() throws NoSuchAlgorithmExce .containsExactlyInAnyOrder("com.example.service", "com.example.web"); } + @Test + void scanWithoutParsing_with_no_scan_annotations_restores_empty_packages_from_cache() throws NoSuchAlgorithmException, IOException { + String filePath = mainCodeSourcesPath(BASE_PATH + "NoScanAnnotations.java"); + + // First pass: scan and write empty package data to cache + ReadCache firstReadCache = HashCacheTestHelper.internalReadCacheFromFile(filePath); + InternalWriteCache firstWriteCache = new InternalWriteCache().bind(firstReadCache); + CheckVerifier.newVerifier() + .withCache(firstReadCache, firstWriteCache) + .onFiles(filePath) + .withCheck(gatherer) + .verifyNoIssues(); + + // Second pass: file is SAME — scanWithoutParsing must restore an empty package set, not [""] + var populatedReadCache = new InternalReadCache().putAll(firstWriteCache); + var secondGatherer = new ComponentScanPackageGatherer(); + CheckVerifier secondCheckVerifier = CheckVerifier.newVerifier() + .withCache(populatedReadCache, new InternalWriteCache().bind(populatedReadCache)) + .addFiles(InputFile.Status.SAME, filePath) + .withCheck(secondGatherer); + secondCheckVerifier.verifyNoIssues(); + + assertThat(secondCheckVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); + } + @Test void duplicate_cache_write_IllegalArgumentException_is_caught() { String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java index 091ac36bd5d..b5f981e4bd3 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -198,7 +198,11 @@ private static Optional> readFromCache(InputFileScannerContext cont var bytes = context.getCacheContext().getReadCache().readBytes(cacheKey); if (bytes != null) { context.getCacheContext().getWriteCache().copyFromPrevious(cacheKey); - return Optional.of(Arrays.asList(new String(bytes, StandardCharsets.UTF_8).split(";"))); + String content = new String(bytes, StandardCharsets.UTF_8); + if (content.isEmpty()) { + return Optional.of(List.of()); + } + return Optional.of(Arrays.asList(content.split(";"))); } return Optional.empty(); } From aefc020bd9b8c9c8ac14383af44d9320848a0305 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 14:22:13 +0200 Subject: [PATCH 08/15] Filter out blank package names in ComponentScanPackageGatherer Co-Authored-By: Claude Sonnet 4.6 --- .../verifier/internal/JavaCheckVerifier.java | 1 - .../ComponentScanPackageGatherer.java | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java index 1f4f242d576..1168d94fae5 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java @@ -96,7 +96,6 @@ public static JavaCheckVerifier newInstance() { private ReadCache readCache; private WriteCache writeCache; private File rootDirectory; - @VisibleForTesting private SpringContextModel springContextModel; private MultiFileVerifier createVerifier() { diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java index b5f981e4bd3..2d0422e343a 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -141,10 +141,13 @@ private static List targetedPackages(String classPackageName, SymbolMeta for (SymbolMetadata.AnnotationValue value : scanBaseValues) { boolean isClassBased = "scanBasePackageClasses".equals(value.name()); for (Object element : (Object[]) value.value()) { - if (!isClassBased && element instanceof String s) { + if (!isClassBased && element instanceof String s && !s.isBlank()) { packages.add(s); } else if (isClassBased && element instanceof Symbol s) { - packages.add(packageNameOf(s)); + var pkg = packageNameOf(s); + if (!pkg.isBlank()) { + packages.add(pkg); + } } } } @@ -159,13 +162,15 @@ private static List targetedPackages(String classPackageName, SymbolMeta private void addAnnotationValueToCollectedPackages(SymbolMetadata.AnnotationValue annotationValue) { if (annotationValue.value() instanceof Object[] objects) { for (Object o : objects) { - if (o instanceof String oString) { + if (o instanceof String oString && !oString.isBlank()) { collectedPackages.add(oString); packagesCollectedAtFileLevel.add(oString); } else if (o instanceof Symbol oSymbol) { var pkg = packageNameOf(oSymbol); - collectedPackages.add(pkg); - packagesCollectedAtFileLevel.add(pkg); + if (!pkg.isBlank()) { + collectedPackages.add(pkg); + packagesCollectedAtFileLevel.add(pkg); + } } } } From e883d3415395ff9dbaf53afb18a2185ffc95207c Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 15:15:27 +0200 Subject: [PATCH 09/15] Skip SpringContextModel gatherers when spring-context is not in classpath Implement DependencyVersionAware in SpringContextModelGatherer so all gatherers are automatically skipped on modules without spring-context on the classpath. Add integration test verifying the gatherer produces no output when spring-context is removed from the classpath. Co-Authored-By: Claude Sonnet 4.6 --- .../context/ComponentScanPackageGathererTest.java | 15 ++++++++++++++- .../springcontext/SpringContextModelGatherer.java | 13 ++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java index 1c570429d3b..99ebdfe348e 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java @@ -22,13 +22,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.cache.ReadCache; import org.sonar.java.checks.helpers.HashCacheTestHelper; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.checks.verifier.internal.InternalReadCache; import org.sonar.java.checks.verifier.internal.InternalWriteCache; import org.sonar.java.model.springcontext.ComponentScanPackageGatherer; import org.sonar.java.model.springcontext.SpringContextModel; -import org.sonar.api.batch.sensor.cache.ReadCache; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -103,6 +103,19 @@ void componentScan_with_packages_overrides_springBootApplication_default_own_pac .containsExactlyInAnyOrder("com.example.service", "com.example.web"); } + // ---- DependencyVersionAware ----------------------------------------------- + + @Test + void gatherer_is_skipped_when_spring_context_is_not_in_classpath() { + CheckVerifier checkVerifier = CheckVerifier.newVerifier() + .removeJarsFromClasspath("spring-context") + .onFiles(mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java")) + .withCheck(gatherer); + checkVerifier.verifyNoIssues(); + + assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getModules()).isEmpty(); + } + // ---- No annotations ------------------------------------------------------- @Test diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java index 71204364628..e40a31cfe85 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java @@ -16,17 +16,28 @@ */ package org.sonar.java.model.springcontext; +import java.util.Optional; +import java.util.function.Function; import org.sonar.java.ast.visitors.SubscriptionVisitor; import org.sonar.java.model.DefaultModuleScannerContext; +import org.sonar.plugins.java.api.DependencyVersionAware; import org.sonar.plugins.java.api.ModuleScannerContext; +import org.sonar.plugins.java.api.Version; import org.sonar.plugins.java.api.internal.EndOfAnalysis; /** * Base class for visitors that need to gather data in the SpringContextModel at the end of the analysis. * Extending classes will have to implement the AST visitor pattern, gather relevant spring-related data, and store it * in the SpringContextModel at the of a module analysis. + * + *

    All gatherers are skipped when {@code spring-context} is not present in the module classpath. */ -public abstract class SpringContextModelGatherer extends SubscriptionVisitor implements EndOfAnalysis { +public abstract class SpringContextModelGatherer extends SubscriptionVisitor implements EndOfAnalysis, DependencyVersionAware { + + @Override + public boolean isCompatibleWithDependencies(Function> dependencyFinder) { + return dependencyFinder.apply("spring-context").isPresent(); + } @Override public final void endOfAnalysis(ModuleScannerContext context) { From c53ed28004f97596b885d4bc2afa8981d763bb5b Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 15:51:08 +0200 Subject: [PATCH 10/15] Move non-caching ComponentScanPackageGatherer tests to java-frontend Reimplements all non-caching tests using VisitorsBridge + JParserTestUtils directly in java-frontend, closing the coverage gap caused by the class living in java-frontend while its tests previously lived in java-checks. Test resource files that were only needed for non-caching tests are moved from java-checks-test-sources to java-frontend/src/test/files/springcontext/. The java-checks ComponentScanPackageGathererTest retains only caching tests. Co-Authored-By: Claude Sonnet 4.6 --- .../ComponentScanPackageGathererTest.java | 86 ---------- .../ComponentScanWithBasePackages.java | 0 .../springcontext/NoScanAnnotations.java | 4 + .../SpringBootAppNoScanAttributes.java | 0 ...pringBootAppWithComponentScanPackages.java | 0 ...ringBootAppWithScanBasePackageClasses.java | 0 .../SpringBootAppWithScanBasePackages.java | 6 + .../ComponentScanPackageGathererTest.java | 149 ++++++++++++++++++ .../SpringContextModelGathererTest.java | 24 ++- 9 files changed, 177 insertions(+), 92 deletions(-) rename {java-checks-test-sources/default/src/main/java/checks/spring/context => java-frontend/src/test/files/springcontext}/ComponentScanWithBasePackages.java (100%) create mode 100644 java-frontend/src/test/files/springcontext/NoScanAnnotations.java rename {java-checks-test-sources/default/src/main/java/checks/spring/context => java-frontend/src/test/files/springcontext}/SpringBootAppNoScanAttributes.java (100%) rename {java-checks-test-sources/default/src/main/java/checks/spring/context => java-frontend/src/test/files/springcontext}/SpringBootAppWithComponentScanPackages.java (100%) rename {java-checks-test-sources/default/src/main/java/checks/spring/context => java-frontend/src/test/files/springcontext}/SpringBootAppWithScanBasePackageClasses.java (100%) create mode 100644 java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackages.java create mode 100644 java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java index 99ebdfe348e..42d13f6b07d 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java @@ -52,92 +52,6 @@ void setUp() { writeCache = new InternalWriteCache().bind(readCache); } - // ---- @ComponentScan ------------------------------------------------------- - - @Test - void componentScan_value_and_basePackages_attributes_are_collected() { - scan(mainCodeSourcesPath(BASE_PATH + "ComponentScanWithBasePackages.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder( - "com.example.service", - "com.example.repository", - "com.example.controller", - "com.example.domain", - "com.example.single", - "checks.spring.context" // basePackageClasses = PackageMarker.class → its package - ); - } - - // ---- @SpringBootApplication ----------------------------------------------- - - @Test - void springBootApplication_without_scan_attributes_collects_own_package() { - scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactly("checks.spring.context"); - } - - @Test - void springBootApplication_scanBasePackages_are_collected() { - scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("com.example.service", "com.example.web"); - } - - @Test - void springBootApplication_scanBasePackageClasses_resolves_to_class_package() { - scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackageClasses.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactly("checks.spring.context"); - } - - @Test - void componentScan_with_packages_overrides_springBootApplication_default_own_package() { - scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithComponentScanPackages.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("com.example.service", "com.example.web"); - } - - // ---- DependencyVersionAware ----------------------------------------------- - - @Test - void gatherer_is_skipped_when_spring_context_is_not_in_classpath() { - CheckVerifier checkVerifier = CheckVerifier.newVerifier() - .removeJarsFromClasspath("spring-context") - .onFiles(mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java")) - .withCheck(gatherer); - checkVerifier.verifyNoIssues(); - - assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getModules()).isEmpty(); - } - - // ---- No annotations ------------------------------------------------------- - - @Test - void no_scan_annotations_collects_nothing() { - scan(mainCodeSourcesPath(BASE_PATH + "NoScanAnnotations.java")); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); - } - - // ---- Multiple files ------------------------------------------------------- - - @Test - void packages_from_multiple_files_are_merged() { - scan( - mainCodeSourcesPath(BASE_PATH + "SpringBootAppNoScanAttributes.java"), - mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java") - ); - - assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("checks.spring.context", "com.example.service", "com.example.web"); - } - // ---- Caching -------------------------------------------------------------- @Test diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java b/java-frontend/src/test/files/springcontext/ComponentScanWithBasePackages.java similarity index 100% rename from java-checks-test-sources/default/src/main/java/checks/spring/context/ComponentScanWithBasePackages.java rename to java-frontend/src/test/files/springcontext/ComponentScanWithBasePackages.java diff --git a/java-frontend/src/test/files/springcontext/NoScanAnnotations.java b/java-frontend/src/test/files/springcontext/NoScanAnnotations.java new file mode 100644 index 00000000000..5e0f4864e61 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/NoScanAnnotations.java @@ -0,0 +1,4 @@ +package checks.spring.context; + +// A class with no Spring scanning annotations — should not contribute any packages. +interface NoScanAnnotations {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java b/java-frontend/src/test/files/springcontext/SpringBootAppNoScanAttributes.java similarity index 100% rename from java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppNoScanAttributes.java rename to java-frontend/src/test/files/springcontext/SpringBootAppNoScanAttributes.java diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithComponentScanPackages.java similarity index 100% rename from java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithComponentScanPackages.java rename to java-frontend/src/test/files/springcontext/SpringBootAppWithComponentScanPackages.java diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackageClasses.java similarity index 100% rename from java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackageClasses.java rename to java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackageClasses.java diff --git a/java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackages.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackages.java new file mode 100644 index 00000000000..149922186a3 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackages.java @@ -0,0 +1,6 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = {"com.example.service", "com.example.web"}) +class SpringBootAppWithScanBasePackages {} diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java new file mode 100644 index 00000000000..3d87ccbd984 --- /dev/null +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java @@ -0,0 +1,149 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model.springcontext; + +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.java.SonarComponents; +import org.sonar.java.TestUtils; +import org.sonar.java.model.JParserTestUtils; +import org.sonar.java.model.VisitorsBridge; +import org.sonar.java.test.classpath.TestClasspathUtils; +import org.sonar.plugins.java.api.JavaCheck; + +import static org.assertj.core.api.Assertions.assertThat; + +class ComponentScanPackageGathererTest { + + private static final String MODULE_KEY = ""; + + private SpringContextModel model; + private ComponentScanPackageGatherer gatherer; + + @BeforeEach + void setUp() { + gatherer = new ComponentScanPackageGatherer(); + model = new SpringContextModel(); + } + + // ---- @ComponentScan ------------------------------------------------------- + + @Test + void componentScan_value_and_basePackages_attributes_are_collected() { + scan("src/test/files/springcontext/ComponentScanWithBasePackages.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder( + "com.example.service", + "com.example.repository", + "com.example.controller", + "com.example.domain", + "com.example.single", + "checks.spring.context" // basePackageClasses = PackageMarker.class → its package + ); + } + + // ---- @SpringBootApplication ----------------------------------------------- + + @Test + void springBootApplication_without_scan_attributes_collects_own_package() { + scan("src/test/files/springcontext/SpringBootAppNoScanAttributes.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("checks.spring.context"); + } + + @Test + void springBootApplication_scanBasePackages_are_collected() { + scan("src/test/files/springcontext/SpringBootAppWithScanBasePackages.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + @Test + void springBootApplication_scanBasePackageClasses_resolves_to_class_package() { + scan("src/test/files/springcontext/SpringBootAppWithScanBasePackageClasses.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("checks.spring.context"); + } + + @Test + void componentScan_with_packages_overrides_springBootApplication_default_own_package() { + scan("src/test/files/springcontext/SpringBootAppWithComponentScanPackages.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + // ---- DependencyVersionAware ----------------------------------------------- + + @Test + void gatherer_is_skipped_when_spring_context_is_not_in_classpath() { + scan(List.of(), "src/test/files/springcontext/SpringBootAppNoScanAttributes.java"); + + assertThat(model.getProjectPackageScan().getModules()).isEmpty(); + } + + // ---- No annotations ------------------------------------------------------- + + @Test + void no_scan_annotations_collects_nothing() { + scan("src/test/files/springcontext/NoScanAnnotations.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); + } + + // ---- Multiple files ------------------------------------------------------- + + @Test + void packages_from_multiple_files_are_merged() { + scan( + "src/test/files/springcontext/SpringBootAppNoScanAttributes.java", + "src/test/files/springcontext/SpringBootAppWithScanBasePackages.java" + ); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("checks.spring.context", "com.example.service", "com.example.web"); + } + + // ---- Helpers -------------------------------------------------------------- + + private void scan(String... filePaths) { + scan(TestClasspathUtils.DEFAULT_MODULE.getClassPath(), filePaths); + } + + private void scan(List classpath, String... filePaths) { + SensorContextTester sensorContextTester = SensorContextTester.create(new File("")); + var sonarComponents = new SonarComponents(null, null, null, null, null, null); + sonarComponents.setSensorContext(sensorContextTester); + sonarComponents.setSpringContextModel(model); + + VisitorsBridge visitorsBridge = new VisitorsBridge(List.of((JavaCheck) gatherer), classpath, sonarComponents); + for (String filePath : filePaths) { + File file = new File(filePath); + var compilationUnit = JParserTestUtils.parse(file, classpath); + visitorsBridge.setCurrentFile(TestUtils.inputFile(file)); + visitorsBridge.visitFile(compilationUnit, false); + } + visitorsBridge.endOfAnalysis(); + } +} \ No newline at end of file diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java index bc2c879fd15..2498aad2234 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java @@ -17,7 +17,6 @@ package org.sonar.java.model.springcontext; import java.io.File; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import org.sonar.api.batch.fs.InputFile; @@ -26,6 +25,7 @@ import org.sonar.java.TestUtils; import org.sonar.java.model.JParserTestUtils; import org.sonar.java.model.VisitorsBridge; +import org.sonar.java.test.classpath.TestClasspathUtils; import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.ModuleScannerContext; import org.sonar.plugins.java.api.tree.Tree; @@ -38,26 +38,38 @@ class SpringContextModelGathererTest { @Test void testGatherSpringContextData() { - getSpringModelAfterVisitingFile("src/test/files/model/SimpleClass.java", new SampleGatherer()); + scanFile("src/test/files/model/SimpleClass.java", new SampleGatherer(), TestClasspathUtils.DEFAULT_MODULE.getClassPath()); assertThat(model.getTypeToBeanNamesIndex().getNamesForType("com.example.MyService")).containsExactly("myServiceBean"); } - private void getSpringModelAfterVisitingFile(String filePath, JavaCheck check) { + // ---- ComponentScanPackageGatherer ----------------------------------------- + + @Test + void componentScanPackageGatherer_collects_package_from_springBootApplication() { + var gatherer = new ComponentScanPackageGatherer(); + scanFile("src/test/files/springcontext/SpringBootApp.java", gatherer, TestClasspathUtils.DEFAULT_MODULE.getClassPath()); + + assertThat(model.getProjectPackageScan().getPackagesForModule("")).containsExactly("springcontext"); + } + + // ---- Helpers -------------------------------------------------------------- + + private void scanFile(String filePath, JavaCheck check, List classpath) { File file = new File(filePath); InputFile inputFile = TestUtils.inputFile(file); - var compilationUnit = JParserTestUtils.parse(file); + var compilationUnit = JParserTestUtils.parse(file, classpath); SensorContextTester sensorContextTester = SensorContextTester.create(new File("")); var sonarComponents = new SonarComponents(null, null, null, null, null, null); sonarComponents.setSensorContext(sensorContextTester); sonarComponents.setSpringContextModel(model); - VisitorsBridge visitorsBridge = new VisitorsBridge(List.of(check), new ArrayList<>(), sonarComponents); + VisitorsBridge visitorsBridge = new VisitorsBridge(List.of(check), classpath, sonarComponents); visitorsBridge.setCurrentFile(inputFile); visitorsBridge.visitFile(compilationUnit, false); visitorsBridge.endOfAnalysis(); } - class SampleGatherer extends SpringContextModelGatherer { + static class SampleGatherer extends SpringContextModelGatherer { @Override public void gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) { From 20dc0fbb828bb9cfc8d571a87caa99202e67d67a Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 17:21:58 +0200 Subject: [PATCH 11/15] Fix --- .../internal/InternalCheckVerifierTest.java | 12 ++++++++++++ .../internal/JavaCheckVerifierTest.java | 12 ++++++++++++ .../ComponentScanPackageGatherer.java | 19 +++++++++++-------- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java index c2359561cc8..3e663914a0d 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java @@ -1095,4 +1095,16 @@ void removeJarsFromClasspath_not_supported() { .hasMessage("Not implemented!"); } + @Test + void getSpringContextModel_not_supported() { + InternalCheckVerifier checkVerifier = InternalCheckVerifier.newInstance() + .onFile(TEST_FILE); + + Throwable e = catchThrowable(checkVerifier::getSpringContextModel); + + assertThat(e) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("Not implemented!"); + } + } diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java index 2f21d6c69dd..e3a0f1c2252 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java @@ -36,6 +36,7 @@ import org.sonar.java.checks.verifier.internal.CheckVerifierTestUtils.IssueWithQuickFix; import org.sonar.java.checks.verifier.internal.CheckVerifierTestUtils.NoEffectEndOfAnalysisCheck; import org.sonar.java.model.InternalSyntaxToken; +import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.model.JavaVersionImpl; import org.sonar.java.model.declaration.ClassTreeImpl; import org.sonar.java.model.declaration.ModifiersTreeImpl; @@ -455,4 +456,15 @@ void removeJarsFromClasspath_modifies_classpath() { assertThat(dummyVerifier.actualClasspath).hasSize(initialSize - 1); assertThat(dummyVerifier.actualClasspath.stream().map(File::getName)).noneMatch(n -> n.equals("testng-7.12.0.jar")); } + + @Test + void getSpringContextModel_returns_model_populated_after_analysis() { + JavaCheckVerifier dummyVerifier = JavaCheckVerifier.newInstance(); + dummyVerifier.withCheck(NO_EFFECT_CHECK) + .onFile(TEST_FILE) + .verifyNoIssues(); + + SpringContextModel model = dummyVerifier.getSpringContextModel(); + assertThat(model).isNotNull().isInstanceOf(SpringContextModel.class); + } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java index 2d0422e343a..dce1d55693a 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -141,14 +141,7 @@ private static List targetedPackages(String classPackageName, SymbolMeta for (SymbolMetadata.AnnotationValue value : scanBaseValues) { boolean isClassBased = "scanBasePackageClasses".equals(value.name()); for (Object element : (Object[]) value.value()) { - if (!isClassBased && element instanceof String s && !s.isBlank()) { - packages.add(s); - } else if (isClassBased && element instanceof Symbol s) { - var pkg = packageNameOf(s); - if (!pkg.isBlank()) { - packages.add(pkg); - } - } + resolvePackage(element, isClassBased).ifPresent(packages::add); } } if (scanBaseValues.isEmpty()) { @@ -176,6 +169,16 @@ private void addAnnotationValueToCollectedPackages(SymbolMetadata.AnnotationValu } } + private static Optional resolvePackage(Object element, boolean isClassBased) { + if (!isClassBased && element instanceof String s && !s.isBlank()) { + return Optional.of(s); + } else if (isClassBased && element instanceof Symbol s) { + var pkg = packageNameOf(s); + return pkg.isBlank() ? Optional.empty() : Optional.of(pkg); + } + return Optional.empty(); + } + private static String packageNameOf(Symbol symbol) { Symbol owner = symbol.owner(); while (!owner.isPackageSymbol()) { From ec796dae4bfd98685e563130c62b8b9df07f559b Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Sun, 7 Jun 2026 23:34:21 +0200 Subject: [PATCH 12/15] Consolidate ComponentScanPackageGatherer tests into java-frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extend SpringContextModelGatherer from IssuableSubscriptionVisitor instead of SubscriptionVisitor so that leaveFile is called by IssuableSubscriptionVisitorsRunner, enabling per-file cache writes and fixing incremental analysis performance (eclipse_jetty_incremental) - Move all ComponentScanPackageGatherer tests to java-frontend using VisitorsBridge + JParserTestUtils; caching tests use mocked ReadCache/WriteCache injected via SensorContextTester - Remove getSpringContextModel() from CheckVerifier interface and both implementations — no longer needed now tests live in java-frontend Co-Authored-By: Claude Sonnet 4.6 --- .../spring/context/NoScanAnnotations.java | 4 - .../SpringBootAppWithScanBasePackages.java | 6 - .../java/checks/verifier/CheckVerifier.java | 12 -- .../internal/InternalCheckVerifier.java | 6 - .../verifier/internal/JavaCheckVerifier.java | 10 -- .../internal/InternalCheckVerifierTest.java | 11 -- .../internal/JavaCheckVerifierTest.java | 11 -- .../ComponentScanPackageGathererTest.java | 161 ------------------ .../SpringContextModelGatherer.java | 9 +- .../ComponentScanPackageGathererTest.java | 129 +++++++++++++- 10 files changed, 133 insertions(+), 226 deletions(-) delete mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java delete mode 100644 java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java delete mode 100644 java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java deleted file mode 100644 index 5e0f4864e61..00000000000 --- a/java-checks-test-sources/default/src/main/java/checks/spring/context/NoScanAnnotations.java +++ /dev/null @@ -1,4 +0,0 @@ -package checks.spring.context; - -// A class with no Spring scanning annotations — should not contribute any packages. -interface NoScanAnnotations {} diff --git a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java b/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java deleted file mode 100644 index 149922186a3..00000000000 --- a/java-checks-test-sources/default/src/main/java/checks/spring/context/SpringBootAppWithScanBasePackages.java +++ /dev/null @@ -1,6 +0,0 @@ -package checks.spring.context; - -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication(scanBasePackages = {"com.example.service", "com.example.web"}) -class SpringBootAppWithScanBasePackages {} diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java index 85c5658f176..2fbf9b8781d 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/CheckVerifier.java @@ -25,7 +25,6 @@ import org.sonar.api.batch.sensor.cache.WriteCache; import org.sonar.java.checks.verifier.internal.InternalCheckVerifier; import org.sonar.java.checks.verifier.internal.JavaCheckVerifier; -import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.plugins.java.api.JavaFileScanner; import org.sonar.plugins.java.api.tree.CompilationUnitTree; @@ -305,15 +304,4 @@ default CheckVerifier withCompilationUnitModifier(Consumer * Verifies that no issues are raised by the rule(s) on the given file(s). */ void verifyNoIssues(); - - /** - * Returns the {@link SpringContextModel} that was populated during the last verification run. - * - *

    Useful in tests that need to assert on Spring context data (e.g. component-scan packages, - * bean definitions) collected by {@link org.sonar.java.model.springcontext.SpringContextModelGatherer} - * visitors alongside the check under test. - * - * @return the {@link SpringContextModel} instance used during verification - */ - SpringContextModel getSpringContextModel(); } diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java index 756d3bccf07..67c7e0a12bd 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java @@ -48,7 +48,6 @@ import org.sonar.java.caching.JavaWriteCacheImpl; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.model.JavaVersionImpl; -import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.AnalyzerMessage.TextSpan; import org.sonar.java.reporting.JavaQuickFix; @@ -331,11 +330,6 @@ private void verifyAll() { checkIssues(issues, quickFixes); } - @Override - public SpringContextModel getSpringContextModel() { - throw new UnsupportedOperationException("Not implemented!"); - } - private void checkIssues(Set issues, Map> quickFixes) { if (expectations.expectNoIssues()) { assertNoIssues(issues); diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java index 1168d94fae5..bd1ff4e5dd8 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java @@ -40,7 +40,6 @@ import org.sonar.java.caching.JavaWriteCacheImpl; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.model.JavaVersionImpl; -import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.telemetry.NoOpTelemetry; @@ -96,7 +95,6 @@ public static JavaCheckVerifier newInstance() { private ReadCache readCache; private WriteCache writeCache; private File rootDirectory; - private SpringContextModel springContextModel; private MultiFileVerifier createVerifier() { MultiFileVerifier verifier = MultiFileVerifier.create(Paths.get(files.get(0).uri()), UTF_8); @@ -106,10 +104,7 @@ private MultiFileVerifier createVerifier() { List visitors = new ArrayList<>(checks); CommentLinesVisitor commentLinesVisitor = new CommentLinesVisitor(); visitors.add(commentLinesVisitor); - - springContextModel = new SpringContextModel(); SonarComponents sonarComponents = CheckVerifierUtils.sonarComponents(isCacheEnabled, readCache, writeCache, rootDirectory); - sonarComponents.setSpringContextModel(springContextModel); VisitorsBridgeForTests.Builder visitorsBridgeBuilder = new VisitorsBridgeForTests.Builder(visitors) .withJavaVersion(actualVersion) .withSonarComponents(sonarComponents) @@ -392,9 +387,4 @@ public void verifyNoIssues() { createVerifier().assertNoIssuesRaised(); } - @Override - public SpringContextModel getSpringContextModel() { - return springContextModel; - } - } diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java index 3e663914a0d..eecc74e90a1 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java @@ -1095,16 +1095,5 @@ void removeJarsFromClasspath_not_supported() { .hasMessage("Not implemented!"); } - @Test - void getSpringContextModel_not_supported() { - InternalCheckVerifier checkVerifier = InternalCheckVerifier.newInstance() - .onFile(TEST_FILE); - - Throwable e = catchThrowable(checkVerifier::getSpringContextModel); - - assertThat(e) - .isInstanceOf(UnsupportedOperationException.class) - .hasMessage("Not implemented!"); - } } diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java index e3a0f1c2252..cf498762795 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java @@ -36,7 +36,6 @@ import org.sonar.java.checks.verifier.internal.CheckVerifierTestUtils.IssueWithQuickFix; import org.sonar.java.checks.verifier.internal.CheckVerifierTestUtils.NoEffectEndOfAnalysisCheck; import org.sonar.java.model.InternalSyntaxToken; -import org.sonar.java.model.springcontext.SpringContextModel; import org.sonar.java.model.JavaVersionImpl; import org.sonar.java.model.declaration.ClassTreeImpl; import org.sonar.java.model.declaration.ModifiersTreeImpl; @@ -457,14 +456,4 @@ void removeJarsFromClasspath_modifies_classpath() { assertThat(dummyVerifier.actualClasspath.stream().map(File::getName)).noneMatch(n -> n.equals("testng-7.12.0.jar")); } - @Test - void getSpringContextModel_returns_model_populated_after_analysis() { - JavaCheckVerifier dummyVerifier = JavaCheckVerifier.newInstance(); - dummyVerifier.withCheck(NO_EFFECT_CHECK) - .onFile(TEST_FILE) - .verifyNoIssues(); - - SpringContextModel model = dummyVerifier.getSpringContextModel(); - assertThat(model).isNotNull().isInstanceOf(SpringContextModel.class); - } } diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java deleted file mode 100644 index 42d13f6b07d..00000000000 --- a/java-checks/src/test/java/org/sonar/java/checks/spring/context/ComponentScanPackageGathererTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * SonarQube Java - * Copyright (C) SonarSource Sàrl - * mailto:info AT sonarsource DOT com - * - * You can redistribute and/or modify this program under the terms of - * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the Sonar Source-Available License for more details. - * - * You should have received a copy of the Sonar Source-Available License - * along with this program; if not, see https://sonarsource.com/license/ssal/ - */ -package org.sonar.java.checks.spring.context; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.sensor.cache.ReadCache; -import org.sonar.java.checks.helpers.HashCacheTestHelper; -import org.sonar.java.checks.verifier.CheckVerifier; -import org.sonar.java.checks.verifier.internal.InternalReadCache; -import org.sonar.java.checks.verifier.internal.InternalWriteCache; -import org.sonar.java.model.springcontext.ComponentScanPackageGatherer; -import org.sonar.java.model.springcontext.SpringContextModel; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; - -class ComponentScanPackageGathererTest { - - private static final String BASE_PATH = "checks/spring/context/"; - // Module key used by CheckVerifier's SonarComponents (no projectDefinition → empty string) - private static final String MODULE_KEY = ""; - - private SpringContextModel model; - private ComponentScanPackageGatherer gatherer; - private InternalReadCache readCache; - private InternalWriteCache writeCache; - - @BeforeEach - void setUp() { - gatherer = new ComponentScanPackageGatherer(); - readCache = new InternalReadCache(); - writeCache = new InternalWriteCache().bind(readCache); - } - - // ---- Caching -------------------------------------------------------------- - - @Test - void packages_are_written_to_cache_after_scanning() { - scan(mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); - - assertThat(writeCache.getData().keySet()) - .isNotEmpty() - .anySatisfy(key -> assertThat(key).endsWith(BASE_PATH + "SpringBootAppWithScanBasePackages.java")); - } - - @Test - void scanWithoutParsing_populates_model_from_cache() throws NoSuchAlgorithmException, IOException { - String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); - - // First pass: scan with a read cache that has the content hash - ReadCache firstReadCache = HashCacheTestHelper.internalReadCacheFromFile(filePath); - InternalWriteCache firstWriteCache = new InternalWriteCache().bind(firstReadCache); - CheckVerifier checkVerifier = CheckVerifier.newVerifier() - .withCache(firstReadCache, firstWriteCache) - .onFiles(filePath) - .withCheck(gatherer); - checkVerifier.verifyNoIssues(); - - assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("com.example.service", "com.example.web"); - - // Second pass: the file is SAME — scanWithoutParsing restores packages from cache - var populatedReadCache = new InternalReadCache().putAll(firstWriteCache); - var secondGatherer = new ComponentScanPackageGatherer(); - CheckVerifier secondCheckVerifier = CheckVerifier.newVerifier() - .withCache(populatedReadCache, new InternalWriteCache().bind(populatedReadCache)) - .addFiles(InputFile.Status.SAME, filePath) - .withCheck(secondGatherer); - secondCheckVerifier.verifyNoIssues(); - - assertThat(secondCheckVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("com.example.service", "com.example.web"); - } - - @Test - void scanWithoutParsing_returns_false_on_cache_miss() throws NoSuchAlgorithmException, IOException { - String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); - - // Read cache has content hash but no package data → cache miss for package key - var readCacheWithHashOnly = HashCacheTestHelper.internalReadCacheFromFile(filePath); - CheckVerifier checkVerifier = CheckVerifier.newVerifier() - .withCache(readCacheWithHashOnly, new InternalWriteCache().bind(readCacheWithHashOnly)) - .addFiles(InputFile.Status.SAME, filePath) - .withCheck(gatherer); - checkVerifier.verifyNoIssues(); - - // Full parse was forced, packages still collected correctly - assertThat(checkVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)) - .containsExactlyInAnyOrder("com.example.service", "com.example.web"); - } - - @Test - void scanWithoutParsing_with_no_scan_annotations_restores_empty_packages_from_cache() throws NoSuchAlgorithmException, IOException { - String filePath = mainCodeSourcesPath(BASE_PATH + "NoScanAnnotations.java"); - - // First pass: scan and write empty package data to cache - ReadCache firstReadCache = HashCacheTestHelper.internalReadCacheFromFile(filePath); - InternalWriteCache firstWriteCache = new InternalWriteCache().bind(firstReadCache); - CheckVerifier.newVerifier() - .withCache(firstReadCache, firstWriteCache) - .onFiles(filePath) - .withCheck(gatherer) - .verifyNoIssues(); - - // Second pass: file is SAME — scanWithoutParsing must restore an empty package set, not [""] - var populatedReadCache = new InternalReadCache().putAll(firstWriteCache); - var secondGatherer = new ComponentScanPackageGatherer(); - CheckVerifier secondCheckVerifier = CheckVerifier.newVerifier() - .withCache(populatedReadCache, new InternalWriteCache().bind(populatedReadCache)) - .addFiles(InputFile.Status.SAME, filePath) - .withCheck(secondGatherer); - secondCheckVerifier.verifyNoIssues(); - - assertThat(secondCheckVerifier.getSpringContextModel().getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); - } - - @Test - void duplicate_cache_write_IllegalArgumentException_is_caught() { - String filePath = mainCodeSourcesPath(BASE_PATH + "SpringBootAppWithScanBasePackages.java"); - - var verifier = CheckVerifier.newVerifier() - .withCache(readCache, writeCache) - .onFiles(filePath) - .withCheck(gatherer); - verifier.verifyNoIssues(); - - // Second run attempts to write the same cache key; the IllegalArgumentException must be caught internally - assertThatCode(verifier::verifyNoIssues).doesNotThrowAnyException(); - } - - // ---- Helpers -------------------------------------------------------------- - - private void scan(String... filePaths) { - CheckVerifier checkVerifier = CheckVerifier.newVerifier() - .withCache(readCache, writeCache) - .onFiles(List.of(filePaths)) - .withCheck(gatherer); - checkVerifier.verifyNoIssues(); - model = checkVerifier.getSpringContextModel(); - } -} diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java index e40a31cfe85..de35d6280ee 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java @@ -18,9 +18,9 @@ import java.util.Optional; import java.util.function.Function; -import org.sonar.java.ast.visitors.SubscriptionVisitor; import org.sonar.java.model.DefaultModuleScannerContext; import org.sonar.plugins.java.api.DependencyVersionAware; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.ModuleScannerContext; import org.sonar.plugins.java.api.Version; import org.sonar.plugins.java.api.internal.EndOfAnalysis; @@ -31,8 +31,13 @@ * in the SpringContextModel at the of a module analysis. * *

    All gatherers are skipped when {@code spring-context} is not present in the module classpath. + * + *

    Extends {@link IssuableSubscriptionVisitor} so that {@link #leaveFile} is invoked by + * {@code IssuableSubscriptionVisitorsRunner} after each file. This is required for per-file cache + * writes: without it, cache entries are never stored and {@link #scanWithoutParsing} always misses, + * preventing unchanged files from being skipped in incremental analyses. */ -public abstract class SpringContextModelGatherer extends SubscriptionVisitor implements EndOfAnalysis, DependencyVersionAware { +public abstract class SpringContextModelGatherer extends IssuableSubscriptionVisitor implements EndOfAnalysis, DependencyVersionAware { @Override public boolean isCompatibleWithDependencies(Function> dependencyFinder) { diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java index 3d87ccbd984..6290e22e6d4 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java @@ -17,18 +17,34 @@ package org.sonar.java.model.springcontext; import java.io.File; +import java.nio.charset.StandardCharsets; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.cache.WriteCache; import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.java.SonarComponents; import org.sonar.java.TestUtils; import org.sonar.java.model.JParserTestUtils; import org.sonar.java.model.VisitorsBridge; import org.sonar.java.test.classpath.TestClasspathUtils; +import org.sonar.plugins.java.api.InputFileScannerContext; import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.ModuleScannerContext; +import org.sonar.plugins.java.api.caching.CacheContext; +import org.sonar.plugins.java.api.caching.JavaReadCache; +import org.sonar.plugins.java.api.caching.JavaWriteCache; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class ComponentScanPackageGathererTest { @@ -125,16 +141,115 @@ void packages_from_multiple_files_are_merged() { .containsExactlyInAnyOrder("checks.spring.context", "com.example.service", "com.example.web"); } + // ---- Caching -------------------------------------------------------------- + + @Test + void leaveFile_writes_packages_to_cache() { + WriteCache writeCache = mock(WriteCache.class); + SensorContextTester ctx = SensorContextTester.create(new File("")); + ctx.setCacheEnabled(true); + ctx.setNextCache(writeCache); + + scan(ctx, "src/test/files/springcontext/SpringBootAppWithScanBasePackages.java"); + + var dataCaptor = ArgumentCaptor.forClass(byte[].class); + verify(writeCache).write( + org.mockito.ArgumentMatchers.endsWith("SpringBootAppWithScanBasePackages.java"), + dataCaptor.capture()); + assertThat(new String(dataCaptor.getValue(), StandardCharsets.UTF_8)) + .contains("com.example.service") + .contains("com.example.web"); + } + + @Test + void scanWithoutParsing_returns_true_and_restores_packages_on_cache_hit() { + InputFile inputFile = TestUtils.inputFile(new File("src/test/files/springcontext/SpringBootAppWithScanBasePackages.java")); + String cacheKey = "java:spring:component-scan-packages:" + inputFile.key(); + + JavaReadCache readCache = mock(JavaReadCache.class); + when(readCache.readBytes(cacheKey)).thenReturn("com.example.service;com.example.web".getBytes(StandardCharsets.UTF_8)); + CacheContext cacheContext = mockCacheContext(readCache, mock(JavaWriteCache.class)); + + InputFileScannerContext context = mock(InputFileScannerContext.class); + when(context.getInputFile()).thenReturn(inputFile); + when(context.getCacheContext()).thenReturn(cacheContext); + + assertThat(gatherer.scanWithoutParsing(context)).isTrue(); + + ModuleScannerContext moduleScannerContext = mock(ModuleScannerContext.class); + when(moduleScannerContext.getModuleKey()).thenReturn(MODULE_KEY); + gatherer.gatherSpringContextData(moduleScannerContext, model); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactlyInAnyOrder("com.example.service", "com.example.web"); + } + + @Test + void scanWithoutParsing_returns_false_on_cache_miss() { + InputFile inputFile = TestUtils.inputFile(new File("src/test/files/springcontext/SpringBootAppWithScanBasePackages.java")); + + JavaReadCache readCache = mock(JavaReadCache.class); + when(readCache.readBytes(anyString())).thenReturn(null); + CacheContext cacheContext = mockCacheContext(readCache, mock(JavaWriteCache.class)); + + InputFileScannerContext context = mock(InputFileScannerContext.class); + when(context.getInputFile()).thenReturn(inputFile); + when(context.getCacheContext()).thenReturn(cacheContext); + + assertThat(gatherer.scanWithoutParsing(context)).isFalse(); + } + + @Test + void scanWithoutParsing_restores_empty_package_set_from_cache() { + InputFile inputFile = TestUtils.inputFile(new File("src/test/files/springcontext/NoScanAnnotations.java")); + String cacheKey = "java:spring:component-scan-packages:" + inputFile.key(); + + JavaReadCache readCache = mock(JavaReadCache.class); + when(readCache.readBytes(cacheKey)).thenReturn("".getBytes(StandardCharsets.UTF_8)); + CacheContext cacheContext = mockCacheContext(readCache, mock(JavaWriteCache.class)); + + InputFileScannerContext context = mock(InputFileScannerContext.class); + when(context.getInputFile()).thenReturn(inputFile); + when(context.getCacheContext()).thenReturn(cacheContext); + + assertThat(gatherer.scanWithoutParsing(context)).isTrue(); + + ModuleScannerContext moduleScannerContext = mock(ModuleScannerContext.class); + when(moduleScannerContext.getModuleKey()).thenReturn(MODULE_KEY); + gatherer.gatherSpringContextData(moduleScannerContext, model); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)).isEmpty(); + } + + @Test + void duplicate_cache_write_IllegalArgumentException_is_caught() { + WriteCache writeCache = mock(WriteCache.class); + doThrow(new IllegalArgumentException("duplicate key")).when(writeCache).write(anyString(), any(byte[].class)); + SensorContextTester ctx = SensorContextTester.create(new File("")); + ctx.setCacheEnabled(true); + ctx.setNextCache(writeCache); + + assertThatCode(() -> scan(ctx, "src/test/files/springcontext/SpringBootAppWithScanBasePackages.java")) + .doesNotThrowAnyException(); + } + // ---- Helpers -------------------------------------------------------------- private void scan(String... filePaths) { - scan(TestClasspathUtils.DEFAULT_MODULE.getClassPath(), filePaths); + scan(SensorContextTester.create(new File("")), filePaths); } private void scan(List classpath, String... filePaths) { - SensorContextTester sensorContextTester = SensorContextTester.create(new File("")); + scan(classpath, SensorContextTester.create(new File("")), filePaths); + } + + private void scan(SensorContextTester ctx, String... filePaths) { + scan(TestClasspathUtils.DEFAULT_MODULE.getClassPath(), ctx, filePaths); + } + + private void scan(List classpath, SensorContextTester ctx, String... filePaths) { var sonarComponents = new SonarComponents(null, null, null, null, null, null); - sonarComponents.setSensorContext(sensorContextTester); + sonarComponents.setSensorContext(ctx); sonarComponents.setSpringContextModel(model); VisitorsBridge visitorsBridge = new VisitorsBridge(List.of((JavaCheck) gatherer), classpath, sonarComponents); @@ -146,4 +261,12 @@ private void scan(List classpath, String... filePaths) { } visitorsBridge.endOfAnalysis(); } + + private static CacheContext mockCacheContext(JavaReadCache readCache, JavaWriteCache writeCache) { + CacheContext cacheContext = mock(CacheContext.class); + when(cacheContext.isCacheEnabled()).thenReturn(true); + when(cacheContext.getReadCache()).thenReturn(readCache); + when(cacheContext.getWriteCache()).thenReturn(writeCache); + return cacheContext; + } } \ No newline at end of file From 71df0e0ad40f7533016f39b959e8a4151e8d72e6 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Mon, 8 Jun 2026 09:25:51 +0200 Subject: [PATCH 13/15] Add test coverage for uncovered branches in ComponentScanPackageGatherer - Anonymous class: visitNode returns early when simpleName() is null - Blank scanBasePackages entry: resolvePackage falls through to Optional.empty() for blank strings, which are silently ignored Co-Authored-By: Claude Sonnet 4.6 --- .../internal/InternalCheckVerifierTest.java | 1 - .../internal/JavaCheckVerifierTest.java | 1 - .../SpringBootAppWithAnonymousClass.java | 12 ++++++++++++ .../SpringBootAppWithBlankScanBasePackage.java | 7 +++++++ .../ComponentScanPackageGathererTest.java | 18 +++++++++++++++++- 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 java-frontend/src/test/files/springcontext/SpringBootAppWithAnonymousClass.java create mode 100644 java-frontend/src/test/files/springcontext/SpringBootAppWithBlankScanBasePackage.java diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java index eecc74e90a1..c2359561cc8 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifierTest.java @@ -1095,5 +1095,4 @@ void removeJarsFromClasspath_not_supported() { .hasMessage("Not implemented!"); } - } diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java index cf498762795..2f21d6c69dd 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifierTest.java @@ -455,5 +455,4 @@ void removeJarsFromClasspath_modifies_classpath() { assertThat(dummyVerifier.actualClasspath).hasSize(initialSize - 1); assertThat(dummyVerifier.actualClasspath.stream().map(File::getName)).noneMatch(n -> n.equals("testng-7.12.0.jar")); } - } diff --git a/java-frontend/src/test/files/springcontext/SpringBootAppWithAnonymousClass.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithAnonymousClass.java new file mode 100644 index 00000000000..8defd9eb751 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/SpringBootAppWithAnonymousClass.java @@ -0,0 +1,12 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +// The anonymous Runnable triggers visitNode with a null simpleName — should be skipped gracefully. +@SpringBootApplication +class SpringBootAppWithAnonymousClass { + Runnable r = new Runnable() { + @Override + public void run() {} + }; +} diff --git a/java-frontend/src/test/files/springcontext/SpringBootAppWithBlankScanBasePackage.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithBlankScanBasePackage.java new file mode 100644 index 00000000000..38073133bfb --- /dev/null +++ b/java-frontend/src/test/files/springcontext/SpringBootAppWithBlankScanBasePackage.java @@ -0,0 +1,7 @@ +package checks.spring.context; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +// Empty string in scanBasePackages should be silently ignored. +@SpringBootApplication(scanBasePackages = {"com.example.service", ""}) +class SpringBootAppWithBlankScanBasePackage {} diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java index 6290e22e6d4..a3ff19d2651 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java @@ -78,6 +78,22 @@ void componentScan_value_and_basePackages_attributes_are_collected() { // ---- @SpringBootApplication ----------------------------------------------- + @Test + void anonymous_class_inside_springBootApplication_class_is_skipped() { + scan("src/test/files/springcontext/SpringBootAppWithAnonymousClass.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("checks.spring.context"); + } + + @Test + void springBootApplication_blank_scanBasePackage_is_ignored() { + scan("src/test/files/springcontext/SpringBootAppWithBlankScanBasePackage.java"); + + assertThat(model.getProjectPackageScan().getPackagesForModule(MODULE_KEY)) + .containsExactly("com.example.service"); + } + @Test void springBootApplication_without_scan_attributes_collects_own_package() { scan("src/test/files/springcontext/SpringBootAppNoScanAttributes.java"); @@ -269,4 +285,4 @@ private static CacheContext mockCacheContext(JavaReadCache readCache, JavaWriteC when(cacheContext.getWriteCache()).thenReturn(writeCache); return cacheContext; } -} \ No newline at end of file +} From b0a66b4e7407a135f69717720fed30e9311c6e1e Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Mon, 8 Jun 2026 12:27:46 +0200 Subject: [PATCH 14/15] Add isCompatibleWithDependencies tests and move Spring/UnitTest utils - Test isCompatibleWithDependencies for all four trigger dependencies (spring-context, spring-beans, spring-boot-starter, spring-boot-starter-web) and for the none-present case using a parameterized test - Move SpringUtils and UnitTestUtils from java-frontend model package to java-frontend utils package; update all import sites in java-checks Co-Authored-By: Claude Sonnet 4.6 --- ...ssertThrowsInsteadOfTryCatchFailCheck.java | 2 +- .../AssertionsInProductionCodeCheck.java | 2 +- .../checks/ConfigurationBeanNamesCheck.java | 2 +- .../sonar/java/checks/EmptyMethodsCheck.java | 2 +- .../java/checks/LeastSpecificTypeCheck.java | 2 +- .../checks/TestsInSeparateFolderCheck.java | 2 +- .../java/checks/TooManyParametersCheck.java | 2 +- .../helpers/AbstractAssertionVisitor.java | 4 +-- .../checks/naming/BadTestClassNameCheck.java | 2 +- .../checks/naming/BadTestMethodNameCheck.java | 2 +- .../AsyncMethodsCalledViaThisCheck.java | 2 +- ...AsyncMethodsOnConfigurationClassCheck.java | 2 +- .../spring/AsyncMethodsReturnTypeCheck.java | 2 +- ...structorWhenMultipleConstructorsCheck.java | 2 +- .../AutowiredOnMultipleConstructorsCheck.java | 2 +- .../AvoidQualifierOnBeanMethodsCheck.java | 2 +- ...eforeAndAfterTransactionContractCheck.java | 2 +- ...ldOnlyBeAppliedToConcreteClassesCheck.java | 2 +- ...lerWithRestControllerReplacementCheck.java | 2 +- .../ControllerWithSessionAttributesCheck.java | 2 +- ...BeanMethodInvocationWithoutProxyCheck.java | 2 +- .../spring/FieldDependencyInjectionCheck.java | 2 +- ...NonSingletonAutowiredInSingletonCheck.java | 2 +- ...leInjectedFieldsHaveDefaultValueCheck.java | 2 +- .../RequestMappingMethodPublicCheck.java | 2 +- .../SpringBeanNamingConventionCheck.java | 2 +- .../SpringBeansShouldBeAccessibleCheck.java | 2 +- ...ComponentWithNonAutowiredMembersCheck.java | 4 +-- .../SpringComponentWithWrongScopeCheck.java | 6 ++--- ...ConfigurationWithAutowiredFieldsCheck.java | 2 +- .../SpringConstructorInjectionCheck.java | 2 +- .../SpringIncompatibleTransactionalCheck.java | 2 +- ...StaticFieldInjectionNotSupportedCheck.java | 2 +- .../spring/StatusCodesOnResponseCheck.java | 2 +- ...uperfluousResponseBodyAnnotationCheck.java | 2 +- .../TransactionalMethodVisibilityCheck.java | 2 +- ...sePageableParameterForPagedQueryCheck.java | 2 +- ...tationShouldInjectPropertyOrSpELCheck.java | 2 +- ...ractJUnit5NotCompliantModifierChecker.java | 2 +- .../AbstractOneExpectedExceptionRule.java | 2 +- .../AssertJChainSimplificationIndex.java | 2 +- .../AssertJConsecutiveAssertionCheck.java | 2 +- .../tests/AssertionCompareToSelfCheck.java | 2 +- .../tests/AssertionFailInCatchBlockCheck.java | 2 +- .../tests/AssertionInThreadRunCheck.java | 2 +- .../tests/AssertionInTryCatchCheck.java | 2 +- .../checks/tests/AssertionTypesCheck.java | 2 +- .../tests/AssertionsCompletenessCheck.java | 2 +- .../checks/tests/AssertionsInTestsCheck.java | 2 +- .../checks/tests/ExpectedExceptionCheck.java | 2 +- .../tests/JUnit45MethodAnnotationCheck.java | 2 +- .../tests/JunitNestedAnnotationCheck.java | 2 +- .../NoJUnit4AssertionsInJUnit5TestsCheck.java | 2 +- .../OneExpectedRuntimeExceptionCheck.java | 2 +- ...tAnnotationWithExpectedExceptionCheck.java | 2 +- .../checks/tests/TooManyAssertionsCheck.java | 6 ++--- .../org/sonar/java/filters/SpringFilter.java | 2 +- .../ComponentScanPackageGatherer.java | 4 +-- .../SpringContextModelGatherer.java | 9 +++++-- .../java/{model => utils}/SpringUtils.java | 3 ++- .../java/{model => utils}/UnitTestUtils.java | 3 ++- .../SpringContextModelGathererTest.java | 25 +++++++++++++++++++ .../{model => utils}/SpringUtilsTest.java | 3 ++- .../{model => utils}/UnitTestUtilsTest.java | 4 +-- 64 files changed, 105 insertions(+), 72 deletions(-) rename java-frontend/src/main/java/org/sonar/java/{model => utils}/SpringUtils.java (98%) rename java-frontend/src/main/java/org/sonar/java/{model => utils}/UnitTestUtils.java (99%) rename java-frontend/src/test/java/org/sonar/java/{model => utils}/SpringUtilsTest.java (95%) rename java-frontend/src/test/java/org/sonar/java/{model => utils}/UnitTestUtilsTest.java (93%) diff --git a/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java b/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java index 3dccf49ee61..366690bb645 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java @@ -17,7 +17,7 @@ package org.sonar.java.checks; import org.sonar.check.Rule; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.BaseTreeVisitor; import org.sonar.plugins.java.api.tree.BlockTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java index 743e681b817..33cae11d5f6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AssertionsInProductionCodeCheck.java @@ -23,7 +23,7 @@ import org.sonar.check.Rule; import org.sonar.java.annotations.VisibleForTesting; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonar.java.checks.methods.AbstractMethodDetection; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.JavaFileScannerContext.Location; diff --git a/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java index 7e38e4931ef..4680a599a58 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/ConfigurationBeanNamesCheck.java @@ -19,7 +19,7 @@ import java.util.HashSet; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java index 6bca9849d57..93656c446ce 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/EmptyMethodsCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.LineUtils; import org.sonar.java.model.ModifiersUtils; import org.sonar.java.reporting.AnalyzerMessage; diff --git a/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java index fd2211ec370..b3c55bedf31 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java index 9a52b41a570..62c115be87e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java @@ -23,7 +23,7 @@ import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.isTestClass; +import static org.sonar.java.utils.UnitTestUtils.isTestClass; @Rule(key = "S3414") public class TestsInSeparateFolderCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java index 2e0ac09b87c..732ccc89483 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/TooManyParametersCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java b/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java index 079e9317e06..818ad961669 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java +++ b/java-checks/src/main/java/org/sonar/java/checks/helpers/AbstractAssertionVisitor.java @@ -24,8 +24,8 @@ import org.sonar.plugins.java.api.tree.MethodReferenceTree; import org.sonar.plugins.java.api.tree.NewClassTree; -import static org.sonar.java.model.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; -import static org.sonar.java.model.UnitTestUtils.methodNameMatchesAssertionMethodPattern; +import static org.sonar.java.utils.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; +import static org.sonar.java.utils.UnitTestUtils.methodNameMatchesAssertionMethodPattern; import static org.sonar.java.model.ExpressionUtils.methodName; public abstract class AbstractAssertionVisitor extends BaseTreeVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java index 89e4b180d25..a297b4d7f3f 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestClassNameCheck.java @@ -28,7 +28,7 @@ import org.sonar.plugins.java.api.tree.IdentifierTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.isTestClass; +import static org.sonar.java.utils.UnitTestUtils.isTestClass; @Rule(key = "S3577") public class BadTestClassNameCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java index 167bd60c7d7..08da9694feb 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/naming/BadTestMethodNameCheck.java @@ -26,7 +26,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.utils.UnitTestUtils.hasTestAnnotation; @Rule(key = "S3578") public class BadTestMethodNameCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java index e7ba82a660e..efcf54a8428 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Map; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java index 29a0885c2d4..5f0f630e970 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsOnConfigurationClassCheck.java @@ -19,7 +19,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java index 8b8e1b53e96..0093889c593 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java index dcf5be0889e..d35b66ba857 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnConstructorWhenMultipleConstructorsCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java index b0326f1855e..0eabf9cabeb 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AutowiredOnMultipleConstructorsCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java index 15acda18813..4de17d91668 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AvoidQualifierOnBeanMethodsCheck.java @@ -20,7 +20,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.expression.AssignmentExpressionTreeImpl; import org.sonar.java.model.expression.LiteralTreeImpl; import org.sonar.java.reporting.JavaQuickFix; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java index 428f5f6fcd1..fc66200c469 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/BeforeAndAfterTransactionContractCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.declaration.MethodTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java index 3e1c6a292c5..03e2634737d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/CacheAnnotationsShouldOnlyBeAppliedToConcreteClassesCheck.java @@ -20,7 +20,7 @@ import java.util.Set; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java index 37c2a8963df..ae5692854af 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithRestControllerReplacementCheck.java @@ -21,7 +21,7 @@ import java.util.Optional; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java index e20ea872d70..c279b58fb5c 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ControllerWithSessionAttributesCheck.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java index 42c0257c260..6d794d33969 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/DirectBeanMethodInvocationWithoutProxyCheck.java @@ -21,7 +21,7 @@ import java.util.Optional; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.AnnotationTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java index cb06b99e0bc..7228aa658d7 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/FieldDependencyInjectionCheck.java @@ -19,7 +19,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.InjectionHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java index c8175e46fc8..deb5e8bf6f3 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java @@ -22,7 +22,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java index 77c67b4d341..20a20c2618b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/NullableInjectedFieldsHaveDefaultValueCheck.java @@ -25,7 +25,7 @@ import org.sonar.java.checks.helpers.ExpressionsHelper; import org.sonar.java.checks.helpers.MethodTreeUtils; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.reporting.JavaQuickFix; import org.sonar.java.reporting.JavaTextEdit; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java index c391e07b741..0166fdb59a4 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/RequestMappingMethodPublicCheck.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java index f1c62ba9f7d..a8434ae52b9 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeanNamingConventionCheck.java @@ -23,7 +23,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java index 55fca00e871..c900f61ab1e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringBeansShouldBeAccessibleCheck.java @@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.DefaultJavaFileScannerContext; import org.sonar.java.model.DefaultModuleScannerContext; import org.sonar.java.reporting.AnalyzerMessage; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java index dd9a96d9663..9b297157889 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithNonAutowiredMembersCheck.java @@ -24,7 +24,7 @@ import java.util.stream.Collectors; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; -import static org.sonar.java.model.SpringUtils.isScopeSingleton; +import static org.sonar.java.utils.SpringUtils.isScopeSingleton; @Rule(key = "S3749") public class SpringComponentWithNonAutowiredMembersCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java index 5448dc983d7..300cc494e15 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringComponentWithWrongScopeCheck.java @@ -19,14 +19,14 @@ import java.util.Collections; import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.SpringUtils.SCOPE_ANNOTATION; -import static org.sonar.java.model.SpringUtils.isScopeSingleton; +import static org.sonar.java.utils.SpringUtils.SCOPE_ANNOTATION; +import static org.sonar.java.utils.SpringUtils.isScopeSingleton; @Rule(key = "S3750") public class SpringComponentWithWrongScopeCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java index 9e9c0cfffd9..a1bde0df33e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConfigurationWithAutowiredFieldsCheck.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java index d62fc9caa6f..3f33fed6bb1 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringConstructorInjectionCheck.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java index c6e805c88b8..2810806c5dc 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SpringIncompatibleTransactionalCheck.java @@ -27,7 +27,7 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java index 6d84bdea234..2aae433dcca 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/StaticFieldInjectionNotSupportedCheck.java @@ -21,7 +21,7 @@ import java.util.stream.Stream; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.tree.AnnotationTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java index 53e76ab9c8c..b570ef26190 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/StatusCodesOnResponseCheck.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.stream.Stream; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java index 1242c420dab..2d15ba28bce 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/SuperfluousResponseBodyAnnotationCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java index d70f5586a79..d5094969f48 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/TransactionalMethodVisibilityCheck.java @@ -22,7 +22,7 @@ import java.util.Optional; import java.util.function.Function; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.DependencyVersionAware; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.Version; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java index 3970198e28c..8527f1c4d7f 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/UsePageableParameterForPagedQueryCheck.java @@ -18,7 +18,7 @@ import java.util.List; import org.sonar.check.Rule; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.model.declaration.MethodTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java index 264a1d912e8..ff80f1df7ef 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/ValueAnnotationShouldInjectPropertyOrSpELCheck.java @@ -20,7 +20,7 @@ import java.util.stream.Stream; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java index b8296d74c46..73b2a8e03ab 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractJUnit5NotCompliantModifierChecker.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import org.sonar.java.checks.helpers.QuickFixHelper; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonar.java.model.ModifiersUtils; import org.sonar.java.reporting.AnalyzerMessage; import org.sonar.java.reporting.JavaQuickFix; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java index c622e56fe13..1989b575d47 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AbstractOneExpectedExceptionRule.java @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TryStatementTree; -import static org.sonar.java.model.UnitTestUtils.findFail; +import static org.sonar.java.utils.UnitTestUtils.findFail; public abstract class AbstractOneExpectedExceptionRule extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java index fc2a0fe29d7..186c354c4f2 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJChainSimplificationIndex.java @@ -23,7 +23,7 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.MapBuilder; import org.sonar.java.model.ExpressionUtils; import org.sonar.java.model.LiteralUtils; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java index cfd51a1b75c..7a819352dff 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertJConsecutiveAssertionCheck.java @@ -40,7 +40,7 @@ import org.sonar.plugins.java.api.tree.StatementTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.utils.UnitTestUtils.hasTestAnnotation; @Rule(key = "S5853") public class AssertJConsecutiveAssertionCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java index 6550cfdbcc8..184cd8966cf 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionCompareToSelfCheck.java @@ -23,7 +23,7 @@ import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ExpressionsHelper; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.SetUtils; import org.sonar.java.model.SyntacticEquivalence; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java index 0a92a4afa00..5e591aa9dcd 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionFailInCatchBlockCheck.java @@ -22,7 +22,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.FAIL_METHOD_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.FAIL_METHOD_MATCHER; @Rule(key = "S3658") public class AssertionFailInCatchBlockCheck extends AbstractMethodDetection { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java index 31eded338ff..f559afdb68d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInThreadRunCheck.java @@ -27,7 +27,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.COMMON_ASSERTION_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.COMMON_ASSERTION_MATCHER; @Rule(key = "S2186") public class AssertionInThreadRunCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java index fdc509da0c9..ebbbc44cee3 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionInTryCatchCheck.java @@ -34,7 +34,7 @@ import org.sonar.plugins.java.api.tree.TryStatementTree; import org.sonar.plugins.java.api.tree.VariableTree; -import static org.sonar.java.model.UnitTestUtils.COMMON_ASSERTION_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.COMMON_ASSERTION_MATCHER; @Rule(key = "S5779") public class AssertionInTryCatchCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java index 6c3bf0b9c80..b6246ae0a90 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionTypesCheck.java @@ -21,7 +21,7 @@ import java.util.List; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.MethodTreeUtils; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java index d43b8fd7171..7bb1758ef91 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsCompletenessCheck.java @@ -44,7 +44,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static java.util.Collections.emptyList; -import static org.sonar.java.model.UnitTestUtils.hasTestAnnotation; +import static org.sonar.java.utils.UnitTestUtils.hasTestAnnotation; import static org.sonar.plugins.java.api.semantic.MethodMatchers.ANY; @Rule(key = "S2970") diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java index fe70ef638a2..9dc02af16da 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/AssertionsInTestsCheck.java @@ -43,7 +43,7 @@ import org.sonar.plugins.java.api.tree.Tree; import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.sonar.java.model.UnitTestUtils.isUnitTest; +import static org.sonar.java.utils.UnitTestUtils.isUnitTest; @Rule(key = "S2699") public class AssertionsInTestsCheck extends BaseTreeVisitor implements JavaFileScanner { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java index 576b39e6fd4..99a9cfe0829 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/ExpectedExceptionCheck.java @@ -32,7 +32,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.MethodTree; -import static org.sonar.java.model.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; @Rule(key = "S5776") public class ExpectedExceptionCheck extends AbstractMethodDetection { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java index 75c9f8c7659..465ef6b4875 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/JUnit45MethodAnnotationCheck.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonarsource.analyzer.commons.collections.MapBuilder; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java index b935ba4ea8b..c1e80583953 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/JunitNestedAnnotationCheck.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Optional; import org.sonar.check.Rule; -import org.sonar.java.model.UnitTestUtils; +import org.sonar.java.utils.UnitTestUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java index 1a6ef748999..444d7995beb 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/NoJUnit4AssertionsInJUnit5TestsCheck.java @@ -28,7 +28,7 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.JUNIT5_TEST_ANNOTATIONS; +import static org.sonar.java.utils.UnitTestUtils.JUNIT5_TEST_ANNOTATIONS; /** * Check that JUnit Jupiter (JUnit 5) tests do not use JUnit 4 assertions. 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 9cef58efabb..a9c976f09a6 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 @@ -23,7 +23,7 @@ import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.FAIL_METHOD_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.FAIL_METHOD_MATCHER; @Rule(key = "S5778") public class OneExpectedRuntimeExceptionCheck extends AbstractOneExpectedExceptionRule { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java index 26917f3c379..75a067611b4 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/TestAnnotationWithExpectedExceptionCheck.java @@ -33,7 +33,7 @@ import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; +import static org.sonar.java.utils.UnitTestUtils.ASSERTIONS_METHOD_MATCHER; @Rule(key = "S5777") public class TestAnnotationWithExpectedExceptionCheck extends IssuableSubscriptionVisitor { diff --git a/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java index 93b7f3e460c..8a8a5f00b75 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/tests/TooManyAssertionsCheck.java @@ -39,9 +39,9 @@ import org.sonar.plugins.java.api.tree.Modifier; import org.sonar.plugins.java.api.tree.Tree; -import static org.sonar.java.model.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; -import static org.sonar.java.model.UnitTestUtils.isUnitTest; -import static org.sonar.java.model.UnitTestUtils.methodNameMatchesAssertionMethodPattern; +import static org.sonar.java.utils.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; +import static org.sonar.java.utils.UnitTestUtils.isUnitTest; +import static org.sonar.java.utils.UnitTestUtils.methodNameMatchesAssertionMethodPattern; import static org.sonar.java.model.ExpressionUtils.methodName; @Rule(key = "S5961") diff --git a/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java b/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java index 55fddc6d3bf..57edb998fb0 100644 --- a/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java +++ b/java-checks/src/main/java/org/sonar/java/filters/SpringFilter.java @@ -24,7 +24,7 @@ import org.sonar.java.checks.ServletInstanceFieldCheck; import org.sonar.java.checks.TooManyParametersCheck; import org.sonar.java.checks.helpers.ExpressionsHelper; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.java.checks.naming.BadMethodNameCheck; import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.semantic.Symbol; diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java index dce1d55693a..13d114e942c 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -28,7 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; -import org.sonar.java.model.SpringUtils; +import org.sonar.java.utils.SpringUtils; import org.sonar.plugins.java.api.InputFileScannerContext; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.ModuleScannerContext; @@ -147,7 +147,7 @@ private static List targetedPackages(String classPackageName, SymbolMeta if (scanBaseValues.isEmpty()) { // Without explicit scan attributes, @SpringBootApplication scans its own package — but only if // no packages were already collected via @ComponentScan on the same class. - return useOwnPackageAsFallback ? Collections.singletonList(classPackageName) : List.of(); + return (useOwnPackageAsFallback && !classPackageName.isBlank()) ? Collections.singletonList(classPackageName) : List.of(); } return packages; } diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java index de35d6280ee..91f871ab3a5 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextModelGatherer.java @@ -30,7 +30,8 @@ * Extending classes will have to implement the AST visitor pattern, gather relevant spring-related data, and store it * in the SpringContextModel at the of a module analysis. * - *

    All gatherers are skipped when {@code spring-context} is not present in the module classpath. + *

    All gatherers are skipped when none of {@code spring-context}, {@code spring-beans}, + * {@code spring-boot-starter}, or {@code spring-boot-starter-web} is present in the module classpath. * *

    Extends {@link IssuableSubscriptionVisitor} so that {@link #leaveFile} is invoked by * {@code IssuableSubscriptionVisitorsRunner} after each file. This is required for per-file cache @@ -41,7 +42,11 @@ public abstract class SpringContextModelGatherer extends IssuableSubscriptionVis @Override public boolean isCompatibleWithDependencies(Function> dependencyFinder) { - return dependencyFinder.apply("spring-context").isPresent(); + return dependencyFinder.apply("spring-context") + .or(() -> dependencyFinder.apply("spring-beans")) + .or(() -> dependencyFinder.apply("spring-boot-starter")) + .or(() -> dependencyFinder.apply("spring-boot-starter-web")) + .isPresent(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java similarity index 98% rename from java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java rename to java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java index d639f0d6719..1adb19c993b 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/SpringUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java @@ -14,10 +14,11 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.model; +package org.sonar.java.utils; import java.util.List; +import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.ClassTree; diff --git a/java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java similarity index 99% rename from java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java rename to java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java index fa77a03c2e2..5981fb867f6 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/UnitTestUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.model; +package org.sonar.java.utils; import java.util.HashSet; import java.util.List; @@ -26,6 +26,7 @@ import javax.annotation.Nullable; import org.sonar.java.annotations.VisibleForTesting; +import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java index 2498aad2234..7364ea2a97f 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/SpringContextModelGathererTest.java @@ -18,7 +18,12 @@ import java.io.File; import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.java.SonarComponents; @@ -28,9 +33,11 @@ import org.sonar.java.test.classpath.TestClasspathUtils; import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.ModuleScannerContext; +import org.sonar.plugins.java.api.Version; import org.sonar.plugins.java.api.tree.Tree; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; class SpringContextModelGathererTest { @@ -42,6 +49,19 @@ void testGatherSpringContextData() { assertThat(model.getTypeToBeanNamesIndex().getNamesForType("com.example.MyService")).containsExactly("myServiceBean"); } + // ---- isCompatibleWithDependencies ----------------------------------------- + + @ParameterizedTest + @ValueSource(strings = {"spring-context", "spring-beans", "spring-boot-starter", "spring-boot-starter-web"}) + void isCompatibleWithDependencies_true_when_spring_dependency_is_present(String dependency) { + assertThat(new SampleGatherer().isCompatibleWithDependencies(finderFor(dependency))).isTrue(); + } + + @Test + void isCompatibleWithDependencies_false_when_no_spring_dependency_is_present() { + assertThat(new SampleGatherer().isCompatibleWithDependencies(finderFor())).isFalse(); + } + // ---- ComponentScanPackageGatherer ----------------------------------------- @Test @@ -69,6 +89,11 @@ private void scanFile(String filePath, JavaCheck check, List classpath) { visitorsBridge.endOfAnalysis(); } + private static Function> finderFor(String... presentDeps) { + var available = Set.of(presentDeps); + return name -> available.contains(name) ? Optional.of(mock(Version.class)) : Optional.empty(); + } + static class SampleGatherer extends SpringContextModelGatherer { @Override diff --git a/java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/utils/SpringUtilsTest.java similarity index 95% rename from java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/utils/SpringUtilsTest.java index 9b84a1ba3d2..e326a72c7c7 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/SpringUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/utils/SpringUtilsTest.java @@ -14,9 +14,10 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.model; +package org.sonar.java.utils; import org.junit.jupiter.api.Test; +import org.sonar.java.model.JParserTestUtils; import org.sonar.java.model.declaration.ClassTreeImpl; import org.sonar.java.model.declaration.VariableTreeImpl; diff --git a/java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/utils/UnitTestUtilsTest.java similarity index 93% rename from java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/utils/UnitTestUtilsTest.java index fc417a9245e..56371ad0abc 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/UnitTestUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/utils/UnitTestUtilsTest.java @@ -14,13 +14,13 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.model; +package org.sonar.java.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.sonar.java.model.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; +import static org.sonar.java.utils.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; class UnitTestUtilsTest { From 2671ad7e423966172b6576fe9ffd089c5a07d478 Mon Sep 17 00:00:00 2001 From: asya-vorobeva Date: Tue, 9 Jun 2026 09:38:30 +0200 Subject: [PATCH 15/15] Fix after rebase --- .gitmodules | 1 - .../java/org/sonar/java/checks/AbstractPrintfChecker.java | 2 +- .../src/main/java/org/sonar/java/checks/HardcodedIpCheck.java | 2 +- .../sonar/java/checks/PreparedStatementAndResultSetCheck.java | 2 +- .../org/sonar/java/checks/regex/RegexComplexityCheck.java | 2 +- .../src/main/java/org/sonar/java/utils}/StringUtils.java | 2 +- .../src/main/java/org/sonar/java/utils/UnitTestUtils.java | 2 +- .../src/test/java/org/sonar/java/utils}/StringUtilsTest.java | 4 ++-- 8 files changed, 8 insertions(+), 9 deletions(-) rename {java-checks/src/main/java/org/sonar/java/checks/helpers => java-frontend/src/main/java/org/sonar/java/utils}/StringUtils.java (98%) rename {java-checks/src/test/java/org/sonar/java/checks/helpers => java-frontend/src/test/java/org/sonar/java/utils}/StringUtilsTest.java (96%) diff --git a/.gitmodules b/.gitmodules index 38540b20fef..53e42abb632 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "its/sources"] path = its/sources url = https://github.com/SonarSource/ruling_java.git - branch = lp/add-spring-ruling diff --git a/java-checks/src/main/java/org/sonar/java/checks/AbstractPrintfChecker.java b/java-checks/src/main/java/org/sonar/java/checks/AbstractPrintfChecker.java index cd783f0c69c..7830f1429e3 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AbstractPrintfChecker.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AbstractPrintfChecker.java @@ -30,7 +30,7 @@ import java.util.stream.IntStream; import javax.annotation.Nullable; -import org.sonar.java.checks.helpers.StringUtils; +import org.sonar.java.utils.StringUtils; import org.sonar.java.checks.methods.AbstractMethodDetection; import org.sonar.java.model.LiteralUtils; import org.sonar.plugins.java.api.semantic.MethodMatchers; diff --git a/java-checks/src/main/java/org/sonar/java/checks/HardcodedIpCheck.java b/java-checks/src/main/java/org/sonar/java/checks/HardcodedIpCheck.java index 5c256d8423f..00ddc1f0e28 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/HardcodedIpCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/HardcodedIpCheck.java @@ -22,7 +22,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sonar.check.Rule; -import org.sonar.java.checks.helpers.StringUtils; +import org.sonar.java.utils.StringUtils; import org.sonar.java.model.LiteralUtils; import org.sonar.plugins.java.api.JavaFileScanner; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/PreparedStatementAndResultSetCheck.java b/java-checks/src/main/java/org/sonar/java/checks/PreparedStatementAndResultSetCheck.java index 21f6f821707..22c1b6d0a8d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/PreparedStatementAndResultSetCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/PreparedStatementAndResultSetCheck.java @@ -21,7 +21,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.java.checks.helpers.ReassignmentFinder; -import org.sonar.java.checks.helpers.StringUtils; +import org.sonar.java.utils.StringUtils; import org.sonar.java.checks.methods.AbstractMethodDetection; import org.sonar.java.model.ExpressionUtils; import org.sonar.plugins.java.api.semantic.MethodMatchers; diff --git a/java-checks/src/main/java/org/sonar/java/checks/regex/RegexComplexityCheck.java b/java-checks/src/main/java/org/sonar/java/checks/regex/RegexComplexityCheck.java index 4afb6f73f71..d58e8e64205 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/regex/RegexComplexityCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/regex/RegexComplexityCheck.java @@ -22,7 +22,7 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.java.checks.helpers.StringUtils; +import org.sonar.java.utils.StringUtils; import org.sonar.java.model.ExpressionUtils; import org.sonar.java.model.LineUtils; import org.sonar.plugins.java.api.JavaFileScannerContext; diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/StringUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/StringUtils.java similarity index 98% rename from java-checks/src/main/java/org/sonar/java/checks/helpers/StringUtils.java rename to java-frontend/src/main/java/org/sonar/java/utils/StringUtils.java index 2c2e755afcb..434bcf161d5 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/StringUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/utils/StringUtils.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.utils; import javax.annotation.Nullable; import java.util.ArrayList; diff --git a/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java index 5981fb867f6..6b9c29d1e6b 100644 --- a/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/utils/UnitTestUtils.java @@ -41,7 +41,7 @@ import org.sonar.plugins.java.api.tree.Tree; import static java.util.Arrays.asList; -import static org.sonar.java.checks.helpers.StringUtils.flatten; +import static org.sonar.java.utils.StringUtils.flatten; public final class UnitTestUtils { private static final List ORG_ASSERTJ_CORE_API_ASSERTIONS = List.of( diff --git a/java-checks/src/test/java/org/sonar/java/checks/helpers/StringUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/utils/StringUtilsTest.java similarity index 96% rename from java-checks/src/test/java/org/sonar/java/checks/helpers/StringUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/utils/StringUtilsTest.java index ac2ca8fee8e..39df401f190 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/helpers/StringUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/utils/StringUtilsTest.java @@ -14,7 +14,7 @@ * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ -package org.sonar.java.checks.helpers; +package org.sonar.java.utils; import org.junit.jupiter.api.Test; @@ -66,7 +66,7 @@ void testFlatten_exceptions() { .withMessageContaining("Unsupported argument type:"); assertThatIllegalArgumentException() - .isThrownBy(() -> StringUtils.flatten(new int[]{4, 5})) + .isThrownBy(() -> StringUtils.flatten((Object) new int[]{4, 5})) .withMessageContaining("Unsupported argument type:"); var list = List.of("b", List.of("c", "d"));