aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java23
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java6
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java109
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto8
7 files changed, 192 insertions, 84 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();
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
index 48ac551dd1c..f9b38d860cb 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
@@ -25,13 +25,14 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputComponent;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.Issue.Flow;
-import org.sonar.api.batch.fs.internal.DefaultInputComponent;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssueFlow;
import org.sonar.scanner.protocol.Constants.Severity;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation;
@@ -160,7 +161,8 @@ public class IssuePublisher {
private static void applyFlows(Consumer<ScannerReport.Flow> consumer, ScannerReport.IssueLocation.Builder locationBuilder,
ScannerReport.TextRange.Builder textRangeBuilder, Collection<Flow> flows) {
ScannerReport.Flow.Builder flowBuilder = ScannerReport.Flow.newBuilder();
- for (Flow flow : flows) {
+ for (Flow f : flows) {
+ DefaultIssueFlow flow = (DefaultIssueFlow) f;
if (flow.locations().isEmpty()) {
return;
}
@@ -179,10 +181,25 @@ public class IssuePublisher {
}
flowBuilder.addLocation(locationBuilder.build());
}
+ if (flow.getDescription() != null) {
+ flowBuilder.setDescription(flow.getDescription());
+ }
+ flowBuilder.setType(toProtobufFlowType(flow.getType()));
consumer.accept(flowBuilder.build());
}
}
+ private static ScannerReport.FlowType toProtobufFlowType(DefaultIssueFlow.Type flowType) {
+ switch (flowType) {
+ case EXECUTION:
+ return ScannerReport.FlowType.EXECUTION;
+ case DATA:
+ return ScannerReport.FlowType.DATA;
+ default:
+ return ScannerReport.FlowType.UNDEFINED;
+ }
+ }
+
private static ScannerReport.TextRange toProtobufTextRange(ScannerReport.TextRange.Builder textRangeBuilder, TextRange primaryTextRange) {
textRangeBuilder.clear();
textRangeBuilder.setStartLine(primaryTextRange.start().line());
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
index 574fdd1e2a1..0fc66aa1c5f 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
@@ -79,9 +79,9 @@ public class ReportPublisher implements Startable {
private final ScanProperties properties;
private final CeTaskReportDataHolder ceTaskReportDataHolder;
- private Path reportDir;
- private ScannerReportWriter writer;
- private ScannerReportReader reader;
+ private final Path reportDir;
+ private final ScannerReportWriter writer;
+ private final ScannerReportReader reader;
public ReportPublisher(ScanProperties properties, DefaultScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher,
InputModuleHierarchy moduleHierarchy, GlobalAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers, BranchConfiguration branchConfiguration,
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
index f55559e9100..daca66ffacd 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
@@ -22,32 +22,34 @@ package org.sonar.scanner.issue;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputComponent;
-import org.sonar.api.batch.rule.internal.NewActiveRule;
-import org.sonar.api.batch.rule.internal.RulesBuilder;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.DefaultInputProject;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.rule.internal.NewActiveRule;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssueFlow;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
-import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.FlowType;
import org.sonar.scanner.report.ReportPublisher;
-import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -58,26 +60,19 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class IssuePublisherTest {
-
static final RuleKey JAVA_RULE_KEY = RuleKey.of("java", "AvoidCycle");
- static final String JAVA_RULE_NAME = "Avoid Cycle";
private static final RuleKey NOSONAR_RULE_KEY = RuleKey.of("java", "NoSonarCheck");
private DefaultInputProject project;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+ public IssueFilters filters = mock(IssueFilters.class);
- @Mock
- IssueFilters filters;
-
- ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
- RulesBuilder ruleBuilder = new RulesBuilder();
-
- IssuePublisher moduleIssues;
-
- DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php").initMetadata("Foo\nBar\nBiz\n").build();
- ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS);
+ private final ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
+ private IssuePublisher moduleIssues;
+ private final DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php").initMetadata("Foo\nBar\nBiz\n").build();
+ private final ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS);
@Before
public void prepare() throws IOException {
@@ -85,15 +80,22 @@ public class IssuePublisherTest {
.setKey("foo")
.setBaseDir(temp.newFolder())
.setWorkDir(temp.newFolder()));
+
+ activeRulesBuilder.addRule(new NewActiveRule.Builder()
+ .setRuleKey(JAVA_RULE_KEY)
+ .setSeverity(Severity.INFO)
+ .setQProfileKey("qp-1")
+ .build());
+ initModuleIssues();
}
@Test
public void ignore_null_active_rule() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
+ RuleKey INACTIVE_RULE_KEY = RuleKey.of("repo", "inactive");
initModuleIssues();
DefaultIssue issue = new DefaultIssue(project)
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
- .forRule(JAVA_RULE_KEY);
+ .forRule(INACTIVE_RULE_KEY);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isFalse();
@@ -102,8 +104,6 @@ public class IssuePublisherTest {
@Test
public void ignore_null_rule_of_active_rule() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
- activeRulesBuilder.addRule(new NewActiveRule.Builder().setRuleKey(JAVA_RULE_KEY).setQProfileKey("qp-1").build());
initModuleIssues();
DefaultIssue issue = new DefaultIssue(project)
@@ -117,12 +117,6 @@ public class IssuePublisherTest {
@Test
public void add_issue_to_cache() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(JAVA_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setQProfileKey("qp-1")
- .build());
initModuleIssues();
final String ruleDescriptionContextKey = "spring";
@@ -146,8 +140,46 @@ public class IssuePublisherTest {
}
@Test
+ public void add_issue_flows_to_cache() {
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue(project)
+ .at(new DefaultIssueLocation().on(file))
+ // Flow without type
+ .addFlow(List.of(new DefaultIssueLocation().on(file).at(file.selectLine(1)).message("Foo1"), new DefaultIssueLocation().on(file).at(file.selectLine(2)).message("Foo2")))
+ // Flow with type and description
+ .addFlow(List.of(new DefaultIssueLocation().on(file)), DefaultIssueFlow.Type.DATA, "description")
+ // Flow with execution type and no description
+ .addFlow(List.of(new DefaultIssueLocation().on(file)), DefaultIssueFlow.Type.EXECUTION, null)
+ .forRule(JAVA_RULE_KEY);
+
+ when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+ moduleIssues.initAndAddIssue(issue);
+
+ ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
+ List<ScannerReport.Flow> writtenFlows = argument.getValue().getFlowList();
+
+ assertThat(writtenFlows)
+ .extracting(ScannerReport.Flow::getDescription, ScannerReport.Flow::getType)
+ .containsExactly(tuple("", FlowType.UNDEFINED), tuple("description", FlowType.DATA), tuple("", FlowType.EXECUTION));
+
+ assertThat(writtenFlows.get(0).getLocationCount()).isEqualTo(2);
+ assertThat(writtenFlows.get(0).getLocationList()).containsExactly(
+ ScannerReport.IssueLocation.newBuilder()
+ .setComponentRef(file.scannerId())
+ .setMsg("Foo1")
+ .setTextRange(ScannerReport.TextRange.newBuilder().setStartLine(1).setEndLine(1).setEndOffset(3).build())
+ .build(),
+ ScannerReport.IssueLocation.newBuilder()
+ .setComponentRef(file.scannerId())
+ .setMsg("Foo2")
+ .setTextRange(ScannerReport.TextRange.newBuilder().setStartLine(2).setEndLine(2).setEndOffset(3).build())
+ .build());
+ }
+
+ @Test
public void add_external_issue_to_cache() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
initModuleIssues();
DefaultExternalIssue issue = new DefaultExternalIssue(project)
@@ -165,12 +197,6 @@ public class IssuePublisherTest {
@Test
public void use_severity_from_active_rule_if_no_severity_on_issue() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(JAVA_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setQProfileKey("qp-1")
- .build());
initModuleIssues();
DefaultIssue issue = new DefaultIssue(project)
@@ -186,14 +212,6 @@ public class IssuePublisherTest {
@Test
public void filter_issue() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(JAVA_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setQProfileKey("qp-1")
- .build());
- initModuleIssues();
-
DefaultIssue issue = new DefaultIssue(project)
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
.forRule(JAVA_RULE_KEY);
@@ -208,12 +226,6 @@ public class IssuePublisherTest {
@Test
public void should_ignore_lines_commented_with_nosonar() {
- ruleBuilder.add(JAVA_RULE_KEY).setName(JAVA_RULE_NAME);
- activeRulesBuilder.addRule(new NewActiveRule.Builder()
- .setRuleKey(JAVA_RULE_KEY)
- .setSeverity(Severity.INFO)
- .setQProfileKey("qp-1")
- .build());
initModuleIssues();
DefaultIssue issue = new DefaultIssue(project)
@@ -231,7 +243,6 @@ public class IssuePublisherTest {
@Test
public void should_accept_issues_on_no_sonar_rules() {
// The "No Sonar" rule logs violations on the lines that are flagged with "NOSONAR" !!
- ruleBuilder.add(NOSONAR_RULE_KEY).setName("No Sonar");
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(NOSONAR_RULE_KEY)
.setSeverity(Severity.INFO)
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index 4870c034282..da7c32d0cfc 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -236,6 +236,14 @@ message IssueLocation {
message Flow {
repeated IssueLocation location = 1;
+ string description = 2;
+ FlowType type = 3;
+}
+
+enum FlowType {
+ DATA = 0;
+ EXECUTION = 1;
+ UNKNOWN = 2;
}
message Changesets {