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/AssertThrowsInsteadOfTryCatchFailCheck.java b/java-checks/src/main/java/org/sonar/java/checks/AssertThrowsInsteadOfTryCatchFailCheck.java index 160948e5a71..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.checks.helpers.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 44ba1545d4c..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.checks.helpers.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 7ed8af1c484..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.checks.helpers.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 fcdb8d4d4b9..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.checks.helpers.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/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/LeastSpecificTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/LeastSpecificTypeCheck.java index 2c0dad7b9ec..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.checks.helpers.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/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/TestsInSeparateFolderCheck.java b/java-checks/src/main/java/org/sonar/java/checks/TestsInSeparateFolderCheck.java index 159c56a3f4a..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.checks.helpers.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 a9cc403479e..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.checks.helpers.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 3c5b13d9e94..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.checks.helpers.UnitTestUtils.ASSERTION_INVOCATION_MATCHERS; -import static org.sonar.java.checks.helpers.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 121010c0a42..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.checks.helpers.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 d7109dfc287..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.checks.helpers.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/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/spring/AsyncMethodsCalledViaThisCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsCalledViaThisCheck.java index 7f5f3a85b4c..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.checks.helpers.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 6f4564c717a..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.checks.helpers.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 341df68781d..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.checks.helpers.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 4e05f768942..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.checks.helpers.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 466b6cf87c0..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.checks.helpers.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 53d269b7ef8..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.checks.helpers.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 9afcc4df60d..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.checks.helpers.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 3ab2c2dead1..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.checks.helpers.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 7378e2601ea..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.checks.helpers.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 bb3dd866c78..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.checks.helpers.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 0a2e504469e..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.checks.helpers.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 7521c2c91c6..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.checks.helpers.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 0cfc2e49865..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.checks.helpers.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 ce2ae0e0bb9..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.checks.helpers.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 2576cf5bd57..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.checks.helpers.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 0ad6dd76b48..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.checks.helpers.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 7921c91aaf8..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.checks.helpers.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 8f0c1e9d97e..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.checks.helpers.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.checks.helpers.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 f00760e7dfb..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.checks.helpers.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.checks.helpers.SpringUtils.SCOPE_ANNOTATION; -import static org.sonar.java.checks.helpers.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 8734589db61..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.checks.helpers.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 50b1017e121..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.checks.helpers.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 7d3427c465f..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.checks.helpers.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 cf624783341..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.checks.helpers.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 789b3d2ae44..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.checks.helpers.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 4118f32c7f3..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.checks.helpers.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 b8f619e7491..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.checks.helpers.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 71c06b02103..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.checks.helpers.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 a613a0fd9b7..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.checks.helpers.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 324be9fe19f..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.checks.helpers.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 d9899a27da4..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.checks.helpers.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 012410c853c..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.checks.helpers.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 c611e05c82a..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.checks.helpers.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 d69f3146601..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.checks.helpers.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 349253a6845..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.checks.helpers.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 be20d574e64..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.checks.helpers.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 d4bb5ac8efc..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.checks.helpers.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 2be08a4a633..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.checks.helpers.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 4ca75fe0eb9..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.checks.helpers.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 77553fc1ec5..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.checks.helpers.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 e3ef0a7cac2..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.checks.helpers.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 10d145f5138..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.checks.helpers.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 bfd259761ce..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.checks.helpers.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 9693e262800..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.checks.helpers.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 785b151226c..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.checks.helpers.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 a25f495a273..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.checks.helpers.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 64293a55fc3..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.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.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 fb2e92c9a1b..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.checks.helpers.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 new file mode 100644 index 00000000000..13d114e942c --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/ComponentScanPackageGatherer.java @@ -0,0 +1,217 @@ +/* + * 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.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.utils.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.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: + *

+ * + *

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 ComponentScanPackageGatherer extends SpringContextModelGatherer { + + private static final Logger LOG = LoggerFactory.getLogger(ComponentScanPackageGatherer.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"); + + /** 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<>(); + + @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 gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) { + 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()) { + resolvePackage(element, isClassBased).ifPresent(packages::add); + } + } + 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 && !classPackageName.isBlank()) ? 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 && !oString.isBlank()) { + collectedPackages.add(oString); + packagesCollectedAtFileLevel.add(oString); + } else if (o instanceof Symbol oSymbol) { + var pkg = packageNameOf(oSymbol); + if (!pkg.isBlank()) { + collectedPackages.add(pkg); + packagesCollectedAtFileLevel.add(pkg); + } + } + } + } + } + + 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()) { + 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); + 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(); + } +} 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; } - } 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..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 @@ -16,17 +16,38 @@ */ package org.sonar.java.model.springcontext; -import org.sonar.java.ast.visitors.SubscriptionVisitor; +import java.util.Optional; +import java.util.function.Function; 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; /** * 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 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 + * 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 { +public abstract class SpringContextModelGatherer extends IssuableSubscriptionVisitor implements EndOfAnalysis, DependencyVersionAware { + + @Override + public boolean isCompatibleWithDependencies(Function> dependencyFinder) { + 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 public final void endOfAnalysis(ModuleScannerContext context) { 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..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,15 +19,27 @@ 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( - // example: new SampleSpringContextModelGatherer() + new ComponentScanPackageGatherer() ); } 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/main/java/org/sonar/java/checks/helpers/SpringUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java similarity index 99% rename from java-checks/src/main/java/org/sonar/java/checks/helpers/SpringUtils.java rename to java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.java index 4250acd3ede..1adb19c993b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/SpringUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/utils/SpringUtils.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.checks.helpers; +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; 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-checks/src/main/java/org/sonar/java/checks/helpers/UnitTestUtils.java b/java-frontend/src/main/java/org/sonar/java/utils/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/utils/UnitTestUtils.java index c6402f358e2..6b9c29d1e6b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/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.checks.helpers; +package org.sonar.java.utils; import java.util.HashSet; import java.util.List; @@ -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-frontend/src/test/files/springcontext/ComponentScanWithBasePackages.java b/java-frontend/src/test/files/springcontext/ComponentScanWithBasePackages.java new file mode 100644 index 00000000000..2730eb1343b --- /dev/null +++ b/java-frontend/src/test/files/springcontext/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-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-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/files/springcontext/SpringBootAppNoScanAttributes.java b/java-frontend/src/test/files/springcontext/SpringBootAppNoScanAttributes.java new file mode 100644 index 00000000000..75f0e5c7b28 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/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-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/files/springcontext/SpringBootAppWithComponentScanPackages.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithComponentScanPackages.java new file mode 100644 index 00000000000..34ca7810855 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/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-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackageClasses.java b/java-frontend/src/test/files/springcontext/SpringBootAppWithScanBasePackageClasses.java new file mode 100644 index 00000000000..269d96c8b23 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/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-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..a3ff19d2651 --- /dev/null +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/ComponentScanPackageGathererTest.java @@ -0,0 +1,288 @@ +/* + * 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.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 { + + 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 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"); + + 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"); + } + + // ---- 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(SensorContextTester.create(new File("")), filePaths); + } + + private void scan(List classpath, String... filePaths) { + 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(ctx); + 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(); + } + + 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; + } +} 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..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 @@ -17,20 +17,27 @@ package org.sonar.java.model.springcontext; import java.io.File; -import java.util.ArrayList; 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; 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.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 { @@ -38,26 +45,56 @@ 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) { + // ---- 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 + 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 { + 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 public void gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) { 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"); + } + } diff --git a/java-checks/src/test/java/org/sonar/java/checks/helpers/SpringUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/utils/SpringUtilsTest.java similarity index 95% rename from java-checks/src/test/java/org/sonar/java/checks/helpers/SpringUtilsTest.java rename to java-frontend/src/test/java/org/sonar/java/utils/SpringUtilsTest.java index a936b795dc7..e326a72c7c7 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/helpers/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.checks.helpers; +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-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")); diff --git a/java-checks/src/test/java/org/sonar/java/checks/helpers/UnitTestUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/utils/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/utils/UnitTestUtilsTest.java index 6834ae89004..56371ad0abc 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/helpers/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.checks.helpers; +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.checks.helpers.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; +import static org.sonar.java.utils.UnitTestUtils.ASSERTJ_ASSERTION_METHODS_PREDICATE; class UnitTestUtilsTest { 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 {