From 8c1ed5a6eaa5e48e6809a54c718ba0f83b61e7e4 Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Fri, 11 Apr 2025 17:31:40 +0200 Subject: [PATCH 1/6] SONARCH-708 SonarJava: extend CheckRegistrar API for registering custom file scanner hooks --- .../verifier/TestCheckRegistrarContext.java | 24 ++++++++++- .../java/org/sonar/java/SonarComponents.java | 12 ++++++ .../plugins/java/api/CheckRegistrar.java | 8 ++++ .../org/sonar/java/SonarComponentsTest.java | 43 +++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java index 11e97269fed..474df0af3af 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java @@ -24,9 +24,11 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; import org.sonar.api.rules.RuleAnnotationUtils; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.JavaFileScanner; public class TestCheckRegistrarContext extends CheckRegistrar.RegistrarContext { @@ -52,16 +54,34 @@ public void registerTestChecks(String repositoryKey, Collection javaCheckClas @Override public void registerMainSharedCheck(JavaCheck check, Collection ruleKeys) { + registerMainHook(check); + mainRuleKeys.addAll(ruleKeys); + } + + private void registerMainHook(JavaCheck check) { mainCheckClasses.add(check.getClass()); mainCheckInstances.add(check); - mainRuleKeys.addAll(ruleKeys); } @Override public void registerTestSharedCheck(JavaCheck check, Collection ruleKeys) { + registerTestHook(check); + testRuleKeys.addAll(ruleKeys); + } + + private void registerTestHook(JavaCheck check) { testCheckClasses.add(check.getClass()); testCheckInstances.add(check); - testRuleKeys.addAll(ruleKeys); + } + + @Override + public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { + if (ruleScope != RuleScope.TEST) { + registerMainHook(scanner); + } + if (ruleScope != RuleScope.MAIN) { + registerTestHook(scanner); + } } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java index 5e48d7bfdcb..41a70587d46 100644 --- a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java +++ b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java @@ -56,6 +56,7 @@ import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; import org.sonar.api.utils.Version; import org.sonar.java.annotations.VisibleForTesting; import org.sonar.java.caching.ContentHashCache; @@ -69,6 +70,7 @@ import org.sonar.java.reporting.JavaIssue; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.JavaFileScanner; import org.sonar.plugins.java.api.JspCodeVisitor; import org.sonar.plugins.java.api.caching.SonarLintCache; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -305,6 +307,16 @@ public void registerTestSharedCheck(JavaCheck check, Collection ruleKey } } + @Override + public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { + if (ruleScope != RuleScope.TEST) { + mainChecks.add(scanner); + } + if (ruleScope != RuleScope.MAIN) { + testChecks.add(scanner); + } + } + @Override public void registerAutoScanCompatibleRules(Collection ruleKeys) { additionalAutoScanCompatibleRuleKeys.addAll(ruleKeys); diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java index 99895c93fb3..5ee0f491e69 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java @@ -25,6 +25,7 @@ import org.sonar.api.batch.rule.CheckFactory; import org.sonar.api.batch.rule.Checks; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.java.Preconditions; import org.sonar.java.annotations.Beta; @@ -171,6 +172,13 @@ public void registerMainSharedCheck(JavaCheck check, Collection ruleKey // to be overridden } + /** + * Registers a custom file scanner not related to any rule or repository. + */ + public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { + // to be overridden + } + /** * Registers one test code check related to not one but a list of rules. The check will be active if at least one * of the given rule key is active. In this context injection of @RuleProperty and auto instantiation of rules diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index e5eab251e20..616753cb220 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -68,6 +68,7 @@ import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; import org.sonar.api.testfixtures.log.LogTesterJUnit5; import org.sonar.api.utils.Version; import org.sonar.check.Rule; @@ -81,6 +82,8 @@ import org.sonar.java.testing.ThreadLocalLogTester; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.JspCodeVisitor; import org.sonar.plugins.java.api.caching.SonarLintCache; import org.sonarsource.sonarlint.core.plugin.commons.sonarapi.SonarLintRuntimeImpl; @@ -407,6 +410,46 @@ class RuleF implements JavaCheck { .containsExactly("RuleE"); } + @Test + void register_custom_file_scanners_with_no_active_rules() { + var noActiveRules = (new ActiveRulesBuilder()).build(); + CheckFactory specificCheckFactory = new CheckFactory(noActiveRules); + SensorContextTester specificContext = SensorContextTester.create(new File(".")).setActiveRules(noActiveRules); + + class MainScanner implements JavaFileScanner { + @Override + public void scanFile(JavaFileScannerContext context) { + } + } + + class TestScanner implements JavaFileScanner { + @Override + public void scanFile(JavaFileScannerContext context) { + } + } + + class AllScanner implements JavaFileScanner { + @Override + public void scanFile(JavaFileScannerContext context) { + } + } + + SonarComponents sonarComponents = new SonarComponents(fileLinesContextFactory, null, null, + null, specificCheckFactory, noActiveRules, new CheckRegistrar[]{ + ctx -> ctx.registerCustomFileScanner(RuleScope.MAIN, new MainScanner()), + ctx -> ctx.registerCustomFileScanner(RuleScope.TEST, new TestScanner()), + ctx -> ctx.registerCustomFileScanner(RuleScope.ALL, new AllScanner()) + }); + + sonarComponents.setSensorContext(specificContext); + assertThat(sonarComponents.mainChecks()) + .extracting(c -> c.getClass().getSimpleName()) + .containsExactly("MainScanner", "AllScanner"); + assertThat(sonarComponents.testChecks()) + .extracting(c -> c.getClass().getSimpleName()) + .containsExactly("TestScanner", "AllScanner"); + } + @Test void register_custom_rule_by_instances_instead_of_classes() { ActiveRules activeRules = activeRules("java:S101", "java:S102"); From 1a3cd0eba1324581197069e9ea649051e78d0865 Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Fri, 11 Apr 2025 18:03:52 +0200 Subject: [PATCH 2/6] QG --- .../TestCheckRegistrarContextTest.java | 33 +++++++++++++++++++ .../org/sonar/java/SonarComponentsTest.java | 16 ++++----- .../plugins/java/api/CheckRegistrarTest.java | 7 ++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/TestCheckRegistrarContextTest.java b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/TestCheckRegistrarContextTest.java index bbc18aa893b..4a5b50a01f4 100644 --- a/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/TestCheckRegistrarContextTest.java +++ b/java-checks-testkit/src/test/java/org/sonar/java/checks/verifier/TestCheckRegistrarContextTest.java @@ -19,8 +19,11 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; import org.sonar.check.Rule; import org.sonar.plugins.java.api.JavaCheck; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -125,4 +128,34 @@ void should_fail_if_not_a_JavaCheck() { .hasMessage("Fail to instantiate class java.lang.Object"); } + @Test + void register_custom_file_scanners_with_no_active_rules() { + class DummyScanner implements JavaFileScanner { + @Override + public void scanFile(JavaFileScannerContext context) { + // Dummy implementation. We just need the class instance + } + } + + class MainScanner extends DummyScanner { + } + + class TestScanner extends DummyScanner { + } + + class AllScanner extends DummyScanner { + } + + var ctx = new TestCheckRegistrarContext(); + ctx.registerCustomFileScanner(RuleScope.MAIN, new MainScanner()); + ctx.registerCustomFileScanner(RuleScope.TEST, new TestScanner()); + ctx.registerCustomFileScanner(RuleScope.ALL, new AllScanner()); + + assertThat(ctx.mainCheckInstances) + .extracting(c -> c.getClass().getSimpleName()) + .containsExactly("MainScanner", "AllScanner"); + assertThat(ctx.testCheckInstances) + .extracting(c -> c.getClass().getSimpleName()) + .containsExactly("TestScanner", "AllScanner"); + } } diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index 616753cb220..4e61dfc0c1c 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -416,22 +416,20 @@ void register_custom_file_scanners_with_no_active_rules() { CheckFactory specificCheckFactory = new CheckFactory(noActiveRules); SensorContextTester specificContext = SensorContextTester.create(new File(".")).setActiveRules(noActiveRules); - class MainScanner implements JavaFileScanner { + class DummyScanner implements JavaFileScanner { @Override public void scanFile(JavaFileScannerContext context) { + // Dummy implementation. We just need the class instance } } - class TestScanner implements JavaFileScanner { - @Override - public void scanFile(JavaFileScannerContext context) { - } + class MainScanner extends DummyScanner { } - class AllScanner implements JavaFileScanner { - @Override - public void scanFile(JavaFileScannerContext context) { - } + class TestScanner extends DummyScanner { + } + + class AllScanner extends DummyScanner { } SonarComponents sonarComponents = new SonarComponents(fileLinesContextFactory, null, null, diff --git a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java index 84136f5d106..471095e4678 100644 --- a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java +++ b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java @@ -27,6 +27,7 @@ import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -96,6 +97,12 @@ class Scanner implements JavaCheck { "register 3 instantiated test checks."); } + @Test + void empty_default_method_coverage() { + var context = new CheckRegistrar.RegistrarContext(); + assertDoesNotThrow(() -> context.registerCustomFileScanner(null, null)); + } + private static class TestInternalRegistration extends CheckRegistrar.RegistrarContext { public final List events = new ArrayList<>(); From c1c3e4b514fa7abab357959dac90785aa9a03fae Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Thu, 17 Apr 2025 16:03:57 +0200 Subject: [PATCH 3/6] Added API comments and comments to explain the purpose of the test --- .../java/checks/verifier/TestCheckRegistrarContext.java | 4 ++-- .../src/main/java/org/sonar/java/SonarComponents.java | 4 ++-- .../main/java/org/sonar/plugins/java/api/CheckRegistrar.java | 2 ++ .../java/org/sonar/plugins/java/api/CheckRegistrarTest.java | 5 ++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java index 474df0af3af..8afd2bb0af1 100644 --- a/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java +++ b/java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/TestCheckRegistrarContext.java @@ -76,10 +76,10 @@ private void registerTestHook(JavaCheck check) { @Override public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { - if (ruleScope != RuleScope.TEST) { + if (ruleScope == RuleScope.MAIN || ruleScope == RuleScope.ALL) { registerMainHook(scanner); } - if (ruleScope != RuleScope.MAIN) { + if (ruleScope == RuleScope.TEST || ruleScope == RuleScope.ALL) { registerTestHook(scanner); } } diff --git a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java index 41a70587d46..8845d59b0a5 100644 --- a/java-frontend/src/main/java/org/sonar/java/SonarComponents.java +++ b/java-frontend/src/main/java/org/sonar/java/SonarComponents.java @@ -309,10 +309,10 @@ public void registerTestSharedCheck(JavaCheck check, Collection ruleKey @Override public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { - if (ruleScope != RuleScope.TEST) { + if (ruleScope == RuleScope.MAIN || ruleScope == RuleScope.ALL) { mainChecks.add(scanner); } - if (ruleScope != RuleScope.MAIN) { + if (ruleScope == RuleScope.TEST || ruleScope == RuleScope.ALL) { testChecks.add(scanner); } } diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java index 5ee0f491e69..adb7630f39e 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java @@ -174,6 +174,8 @@ public void registerMainSharedCheck(JavaCheck check, Collection ruleKey /** * Registers a custom file scanner not related to any rule or repository. + * CheckRegistrars call this function to register a custom file scanner for execution during the analysis + * on all source files that match the given rule scope (MAIN, TEST or ALL). */ public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { // to be overridden diff --git a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java index 471095e4678..4bac8aec821 100644 --- a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java +++ b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java @@ -98,8 +98,11 @@ class Scanner implements JavaCheck { } @Test - void empty_default_method_coverage() { + void cover_empty_default_implementations() { var context = new CheckRegistrar.RegistrarContext(); + // Required for test coverage: we call the empty default implementation of the "registerCustomFileScanner" method + // and assert that it returns "void" normally. This is equivalent to asserting that it does not return abnormally, + // i.e., does not throw an exception. assertDoesNotThrow(() -> context.registerCustomFileScanner(null, null)); } From e3731be378c0f8a25d6de02cc15c6e9204f2deee Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Fri, 25 Apr 2025 17:19:01 +0200 Subject: [PATCH 4/6] Improve API doc, remove empty method test, add report test --- .../sonar/plugins/java/api/CheckRegistrar.java | 1 + .../java/org/sonar/java/SonarComponentsTest.java | 15 +++++++++++++++ .../plugins/java/api/CheckRegistrarTest.java | 10 ---------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java index adb7630f39e..e83cfc2b713 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/CheckRegistrar.java @@ -176,6 +176,7 @@ public void registerMainSharedCheck(JavaCheck check, Collection ruleKey * Registers a custom file scanner not related to any rule or repository. * CheckRegistrars call this function to register a custom file scanner for execution during the analysis * on all source files that match the given rule scope (MAIN, TEST or ALL). + * Custom file scanners reporting an issue will have no effect, since no rule is associated. */ public void registerCustomFileScanner(RuleScope ruleScope, JavaFileScanner scanner) { // to be overridden diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index 4e61dfc0c1c..b3bbf3317e6 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -512,6 +512,21 @@ void no_issue_when_check_not_found() { verify(context, never()).newIssue(); } + @Test + void no_issue_when_reporting_from_custom_file_scanner() { + JavaFileScanner customScanner = scannerContext -> { + // empty + }; + CheckRegistrar registrar =registrarContext -> + registrarContext.registerCustomFileScanner(RuleScope.ALL, customScanner); + SonarComponents sonarComponents = new SonarComponents(fileLinesContextFactory, null, null, + null, checkFactory, context.activeRules(), new CheckRegistrar[]{registrar}); + sonarComponents.setSensorContext(context); + + sonarComponents.addIssue(TestUtils.emptyInputFile("file.java"), customScanner, 0, "message", null); + verify(context, never()).newIssue(); + } + @Test void add_issue_or_parse_error() { JavaCheck expectedCheck = new CustomCheck(); diff --git a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java index 4bac8aec821..84136f5d106 100644 --- a/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java +++ b/java-frontend/src/test/java/org/sonar/plugins/java/api/CheckRegistrarTest.java @@ -27,7 +27,6 @@ import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -97,15 +96,6 @@ class Scanner implements JavaCheck { "register 3 instantiated test checks."); } - @Test - void cover_empty_default_implementations() { - var context = new CheckRegistrar.RegistrarContext(); - // Required for test coverage: we call the empty default implementation of the "registerCustomFileScanner" method - // and assert that it returns "void" normally. This is equivalent to asserting that it does not return abnormally, - // i.e., does not throw an exception. - assertDoesNotThrow(() -> context.registerCustomFileScanner(null, null)); - } - private static class TestInternalRegistration extends CheckRegistrar.RegistrarContext { public final List events = new ArrayList<>(); From 566c32bbffc6bbe5259bfcf27a45f81093b7136e Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Mon, 5 May 2025 11:06:13 +0200 Subject: [PATCH 5/6] Added E2E test --- .../java/org/sonar/java/JavaFrontendTest.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/java-frontend/src/test/java/org/sonar/java/JavaFrontendTest.java b/java-frontend/src/test/java/org/sonar/java/JavaFrontendTest.java index 29dc3e4e2c8..3288d79aedf 100644 --- a/java-frontend/src/test/java/org/sonar/java/JavaFrontendTest.java +++ b/java-frontend/src/test/java/org/sonar/java/JavaFrontendTest.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nullable; import org.eclipse.core.runtime.OperationCanceledException; import org.jetbrains.annotations.NotNull; import org.junit.Rule; @@ -51,6 +52,7 @@ import org.sonar.api.issue.NoSonarFilter; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.rule.RuleScope; import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilterChain; import org.sonar.api.testfixtures.log.LogTesterJUnit5; @@ -60,6 +62,8 @@ import org.sonar.java.exceptions.ApiMismatchException; import org.sonar.java.filters.SonarJavaIssueFilter; import org.sonar.java.model.JavaVersionImpl; +import org.sonar.plugins.java.api.CheckRegistrar; +import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.JavaFileScanner; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.JavaResourceLocator; @@ -191,6 +195,38 @@ void scanning_empty_project_should_be_logged_in_batch() { ); } + @Test + void scan_method_is_called_for_hook() throws IOException { + class Hook implements CheckRegistrar, JavaFileScanner { + int callCount; + + @Override + public void register(RegistrarContext registrarContext) { + registrarContext.registerCustomFileScanner(RuleScope.ALL, this); + } + + @Override + public void scanFile(JavaFileScannerContext context) { + callCount++; + } + } + + var settings = new MapSettings(); + if (sensorContext == null) { + File baseDir = temp.getRoot().getAbsoluteFile(); + sensorContext = SensorContextTester.create(baseDir); + sensorContext.setSettings(settings); + } + + var hook = new Hook(); + var inputFiles = List.of( + addFile("class A {}", sensorContext), + addFile("class B {}", sensorContext) + ); + scan(settings, SONARLINT_RUNTIME, inputFiles, new CheckRegistrar[]{hook}); + assertThat(hook.callCount).isEqualTo(2); + } + @Test void scanning_empty_project_should_be_logged_in_autoscan() { MapSettings settings = new MapSettings(); @@ -808,6 +844,10 @@ private List scan(MapSettings settings, SonarRuntime sonarRuntime, St } private List scan(MapSettings settings, SonarRuntime sonarRuntime, List inputFiles) { + return scan(settings, sonarRuntime, inputFiles, null); + } + + private List scan(MapSettings settings, SonarRuntime sonarRuntime, List inputFiles, @Nullable CheckRegistrar[] checkRegistrars) { if (sensorContext == null) { File baseDir = temp.getRoot().getAbsoluteFile(); sensorContext = SensorContextTester.create(baseDir); @@ -823,7 +863,7 @@ private List scan(MapSettings settings, SonarRuntime sonarRuntime, Li javaClasspath = mock(ClasspathForMain.class); javaTestClasspath = mock(ClasspathForTest.class); sonarComponents = new SonarComponents(fileLinesContextFactory, sensorContext.fileSystem(), javaClasspath, javaTestClasspath, - mock(CheckFactory.class), mock(ActiveRules.class)); + mock(CheckFactory.class), mock(ActiveRules.class), checkRegistrars); sonarComponents.setSensorContext(sensorContext); sonarComponents.mainChecks().add(mainCodeIssueScannerAndFilter); sonarComponents.testChecks().add(testCodeIssueScannerAndFilter); @@ -831,7 +871,7 @@ private List scan(MapSettings settings, SonarRuntime sonarRuntime, Li .map(JavaVersionImpl::fromString) .orElse(new JavaVersionImpl()); JavaFrontend frontend = new JavaFrontend(javaVersion, sonarComponents, new Measurer(sensorContext, mock(NoSonarFilter.class)), mock(JavaResourceLocator.class), - null, mainCodeIssueScannerAndFilter); + null, sonarComponents.mainChecks().toArray(new JavaCheck[0])); frontend.scan(inputFiles, Collections.emptyList(), Collections.emptyList()); return inputFiles; From b5a687357b3ef894f9554d5405efcf99680ef9b2 Mon Sep 17 00:00:00 2001 From: Marco Kaufmann Date: Mon, 5 May 2025 16:00:33 +0200 Subject: [PATCH 6/6] Fix typo --- .../src/test/java/org/sonar/java/SonarComponentsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index b3bbf3317e6..5c726325f62 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -517,7 +517,7 @@ void no_issue_when_reporting_from_custom_file_scanner() { JavaFileScanner customScanner = scannerContext -> { // empty }; - CheckRegistrar registrar =registrarContext -> + CheckRegistrar registrar = registrarContext -> registrarContext.registerCustomFileScanner(RuleScope.ALL, customScanner); SonarComponents sonarComponents = new SonarComponents(fileLinesContextFactory, null, null, null, checkFactory, context.activeRules(), new CheckRegistrar[]{registrar});