diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2022-09-06 16:28:00 -0500 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-09-16 20:03:13 +0000 |
commit | 00431799f91fa3b5e7b28d4379991e2456a41ea4 (patch) | |
tree | a1e89e9094e4cbacc77a2799daf913c9c4cd36d6 /sonar-plugin-api-impl | |
parent | dcc6a1c62033cec81c70f740fe3e7a7f745a3e79 (diff) | |
download | sonarqube-00431799f91fa3b5e7b28d4379991e2456a41ea4.tar.gz sonarqube-00431799f91fa3b5e7b28d4379991e2456a41ea4.zip |
SONAR-17287 Scanner supports flow types and description
Diffstat (limited to 'sonar-plugin-api-impl')
3 files changed, 101 insertions, 29 deletions
diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/AbstractDefaultIssue.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/AbstractDefaultIssue.java index dc07a5f6393..1c0f1464f82 100644 --- a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/AbstractDefaultIssue.java +++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/AbstractDefaultIssue.java @@ -27,30 +27,24 @@ import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.batch.sensor.issue.Issue.Flow; -import org.sonar.api.batch.sensor.issue.IssueLocation; -import org.sonar.api.batch.sensor.issue.NewIssueLocation; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.sensor.internal.DefaultStorable; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.batch.sensor.issue.Issue.Flow; +import org.sonar.api.batch.sensor.issue.IssueLocation; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; import org.sonar.api.utils.PathUtils; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; import static org.sonar.api.utils.Preconditions.checkArgument; import static org.sonar.api.utils.Preconditions.checkState; public abstract class AbstractDefaultIssue<T extends AbstractDefaultIssue> extends DefaultStorable { protected IssueLocation primaryLocation; - protected List<List<IssueLocation>> flows = new ArrayList<>(); + protected List<DefaultIssueFlow> flows = new ArrayList<>(); protected DefaultInputProject project; - protected AbstractDefaultIssue(DefaultInputProject project) { - this(project, null); - } - public AbstractDefaultIssue(DefaultInputProject project, @Nullable SensorStorage storage) { super(storage); this.project = project; @@ -61,9 +55,7 @@ public abstract class AbstractDefaultIssue<T extends AbstractDefaultIssue> exten } public List<Flow> flows() { - return this.flows.stream() - .<Flow>map(l -> () -> unmodifiableList(new ArrayList<>(l))) - .collect(toList()); + return Collections.unmodifiableList(flows); } public NewIssueLocation newLocation() { @@ -79,16 +71,21 @@ public abstract class AbstractDefaultIssue<T extends AbstractDefaultIssue> exten } public T addLocation(NewIssueLocation secondaryLocation) { - flows.add(Collections.singletonList(rewriteLocation((DefaultIssueLocation) secondaryLocation))); + flows.add(new DefaultIssueFlow(List.of(rewriteLocation((DefaultIssueLocation) secondaryLocation)), DefaultIssueFlow.Type.UNDEFINED, null)); return (T) this; } public T addFlow(Iterable<NewIssueLocation> locations) { + return addFlow(locations, DefaultIssueFlow.Type.UNDEFINED, null); + } + + public T addFlow(Iterable<NewIssueLocation> locations, DefaultIssueFlow.Type type, @Nullable String description) { + checkArgument(type != null, "Type can't be null"); List<IssueLocation> flowAsList = new ArrayList<>(); for (NewIssueLocation issueLocation : locations) { flowAsList.add(rewriteLocation((DefaultIssueLocation) issueLocation)); } - flows.add(flowAsList); + flows.add(new DefaultIssueFlow(flowAsList, type, description)); return (T) this; } diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueFlow.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueFlow.java new file mode 100644 index 00000000000..e4aabcc85da --- /dev/null +++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueFlow.java @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.sensor.issue.internal; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.issue.IssueLocation; + +public class DefaultIssueFlow implements Issue.Flow { + private final List<IssueLocation> locations; + private final Type type; + @Nullable + private final String description; + + public DefaultIssueFlow(List<IssueLocation> locations, Type type, @Nullable String description) { + this.locations = locations; + this.type = type; + this.description = description; + } + + @Override + public List<IssueLocation> locations() { + return locations; + } + + public Type getType() { + return type; + } + + @CheckForNull + public String getDescription() { + return description; + } + + public enum Type { + UNDEFINED, DATA, EXECUTION; + } +} diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java index 686f804812e..085ba25f645 100644 --- a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java +++ b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java @@ -21,34 +21,39 @@ package org.sonar.api.batch.sensor.issue.internal; import java.io.File; import java.io.IOException; +import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.DefaultInputProject; +import org.sonar.api.batch.fs.internal.DefaultTextPointer; +import org.sonar.api.batch.fs.internal.DefaultTextRange; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.rule.RuleKey; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; public class DefaultIssueTest { - + private static final RuleKey RULE_KEY = RuleKey.of("repo", "rule"); @Rule public TemporaryFolder temp = new TemporaryFolder(); - private DefaultInputProject project; - - private DefaultInputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.php") + private final SensorStorage storage = mock(SensorStorage.class); + private final DefaultInputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.php") .initMetadata("Foo\nBar\n") .build(); + private DefaultInputProject project; @Before public void prepare() throws IOException { @@ -60,13 +65,12 @@ public class DefaultIssueTest { @Test public void build_file_issue() { - SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage) .at(new DefaultIssueLocation() .on(inputFile) .at(inputFile.selectLine(1)) .message("Wrong way!")) - .forRule(RuleKey.of("repo", "rule")) + .forRule(RULE_KEY) .gap(10.0) .setRuleDescriptionContextKey("spring"); @@ -83,8 +87,26 @@ public class DefaultIssueTest { } @Test + public void build_issue_with_flows() { + TextRange range1 = new DefaultTextRange(new DefaultTextPointer(1, 1), new DefaultTextPointer(1, 2)); + TextRange range2 = new DefaultTextRange(new DefaultTextPointer(2, 1), new DefaultTextPointer(2, 2)); + + DefaultIssue issue = new DefaultIssue(project, storage) + .at(new DefaultIssueLocation().on(inputFile)) + .addFlow(List.of(new DefaultIssueLocation().message("loc1").on(inputFile)), DefaultIssueFlow.Type.DATA, "desc") + .addFlow(List.of(new DefaultIssueLocation().message("loc1").on(inputFile).at(range1), new DefaultIssueLocation().message("loc1").on(inputFile).at(range2))) + .forRule(RULE_KEY); + + assertThat(issue.flows) + .extracting(DefaultIssueFlow::getType, DefaultIssueFlow::getDescription) + .containsExactly(tuple(DefaultIssueFlow.Type.DATA, "desc"), tuple(DefaultIssueFlow.Type.UNDEFINED, null)); + + assertThat(issue.flows.get(0).locations()).hasSize(1); + assertThat(issue.flows.get(1).locations()).hasSize(2); + } + + @Test public void move_directory_issue_to_project_root() { - SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage) .at(new DefaultIssueLocation() .on(new DefaultInputDir("foo", "src/main").setModuleBaseDir(project.getBaseDir())) @@ -115,12 +137,11 @@ public class DefaultIssueTest { project.definition().addSubProject(subModuleDefinition); DefaultInputModule subModule = new DefaultInputModule(subModuleDefinition); - SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage) .at(new DefaultIssueLocation() .on(subModule) .message("Wrong way!")) - .forRule(RuleKey.of("repo", "rule")) + .forRule(RULE_KEY) .overrideSeverity(Severity.BLOCKER); assertThat(issue.primaryLocation().inputComponent()).isEqualTo(project); @@ -136,13 +157,12 @@ public class DefaultIssueTest { @Test public void build_project_issue() throws IOException { - SensorStorage storage = mock(SensorStorage.class); DefaultInputModule inputModule = new DefaultInputModule(ProjectDefinition.create().setKey("foo").setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder())); DefaultIssue issue = new DefaultIssue(project, storage) .at(new DefaultIssueLocation() .on(inputModule) .message("Wrong way!")) - .forRule(RuleKey.of("repo", "rule")) + .forRule(RULE_KEY) .gap(10.0); assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputModule); @@ -158,7 +178,6 @@ public class DefaultIssueTest { @Test public void default_issue_has_no_quickfix() { - SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage); assertThat(issue.isQuickFixAvailable()).isFalse(); @@ -166,7 +185,6 @@ public class DefaultIssueTest { @Test public void issue_can_have_quickfix() { - SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(project, storage).setQuickFixAvailable(true); assertThat(issue.isQuickFixAvailable()).isTrue(); |