aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api-impl
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2022-09-06 16:28:00 -0500
committersonartech <sonartech@sonarsource.com>2022-09-16 20:03:13 +0000
commit00431799f91fa3b5e7b28d4379991e2456a41ea4 (patch)
treea1e89e9094e4cbacc77a2799daf913c9c4cd36d6 /sonar-plugin-api-impl
parentdcc6a1c62033cec81c70f740fe3e7a7f745a3e79 (diff)
downloadsonarqube-00431799f91fa3b5e7b28d4379991e2456a41ea4.tar.gz
sonarqube-00431799f91fa3b5e7b28d4379991e2456a41ea4.zip
SONAR-17287 Scanner supports flow types and description
Diffstat (limited to 'sonar-plugin-api-impl')
-rw-r--r--sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/AbstractDefaultIssue.java29
-rw-r--r--sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueFlow.java57
-rw-r--r--sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java44
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();