aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2018-04-24 15:27:56 +0200
committerSonarTech <sonartech@sonarsource.com>2018-05-09 20:20:46 +0200
commitfa4019b992510560be8c6d1b51bc2dc2f6b41546 (patch)
tree815cf6138b9e0b3a419c27cfe98e691080264af3
parent09b3d167fa8f399e18a37d56e7c8cbb61f68f97f (diff)
downloadsonarqube-fa4019b992510560be8c6d1b51bc2dc2f6b41546.tar.gz
sonarqube-fa4019b992510560be8c6d1b51bc2dc2f6b41546.zip
SONAR-10638 Create Java API for analyzers to report significant code
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java11
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/NewSignificantCode.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCode.java76
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java18
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java6
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCodeTest.java81
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java3
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java11
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java27
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewSignificantCode.java45
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java8
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java38
-rw-r--r--sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java5
-rw-r--r--sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java6
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto6
-rw-r--r--sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java15
19 files changed, 459 insertions, 10 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index 287fc2f89b3..ed4ffe22b3c 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -26,6 +26,7 @@ import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
import org.sonar.api.batch.sensor.coverage.NewCoverage;
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
import org.sonar.api.batch.sensor.error.NewAnalysisError;
@@ -163,6 +164,16 @@ public interface SensorContext {
NewAnalysisError newAnalysisError();
/**
+ * Builder to declare which parts of the code is significant code.
+ * Ranges that are not reported as significant code will be ignored and won't be considered when calculating which lines were modified.
+ *
+ * If the significant code is not reported for a file, it is assumed that the entire file is significant code.
+ *
+ * @since 7.2
+ */
+ NewSignificantCode newSignificantCode();
+
+ /**
* Add a property to the scanner context. This context is available
* in Compute Engine when processing the report.
* <br/>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/NewSignificantCode.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/NewSignificantCode.java
new file mode 100644
index 00000000000..dcaf087ddad
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/NewSignificantCode.java
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.code;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+
+/**
+ * This object is used to report ranges of a file which contain significant code.
+ * Lines that are left out (no range reported) will be ignored and won't be considered when calculating which lines were modified.
+ * It's particularly useful when portions of the lines are automatically modified by code editors and do not contain
+ * code or comments were issues might be created.
+ *
+ * Don't forget to call {@link #save()} after setting the file and the ranges where the significant code is.
+ *
+ * @since 7.2
+ */
+public interface NewSignificantCode {
+ /**
+ * The file for which significant code is being reported. This field must be set before saving the error.
+ */
+ NewSignificantCode onFile(InputFile file);
+
+ /**
+ * Add a range of significant code. The range may only include a single line and only one range is accepted per line.
+ */
+ NewSignificantCode addRange(TextRange range);
+
+ /**
+ * Save the reported information for the given file.
+ */
+ void save();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCode.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCode.java
new file mode 100644
index 00000000000..97d2e25a9bd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCode.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.code.internal;
+
+import com.google.common.base.Preconditions;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+
+public class DefaultSignificantCode extends DefaultStorable implements NewSignificantCode {
+ private SortedMap<Integer, TextRange> significantCodePerLine = new TreeMap<>();
+ private InputFile inputFile;
+
+ public DefaultSignificantCode() {
+ super();
+ }
+
+ public DefaultSignificantCode(@Nullable SensorStorage storage) {
+ super(storage);
+ }
+
+ @Override
+ public DefaultSignificantCode onFile(InputFile inputFile) {
+ this.inputFile = inputFile;
+ return this;
+ }
+
+ @Override
+ public DefaultSignificantCode addRange(TextRange range) {
+ Preconditions.checkState(this.inputFile != null, "addRange() should be called after on()");
+
+ int line = range.start().line();
+
+ Preconditions.checkArgument(line == range.end().line(), "Ranges of significant code must be located in a single line");
+ Preconditions.checkState(!significantCodePerLine.containsKey(line), "Significant code was already reported for line '%s'. Can only report once per line.", line);
+
+ significantCodePerLine.put(line, range);
+ return this;
+ }
+
+ @Override
+ protected void doSave() {
+ Preconditions.checkState(inputFile != null, "Call onFile() first");
+ storage.store(this);
+ }
+
+ public InputFile inputFile() {
+ return inputFile;
+ }
+
+ public SortedMap<Integer, TextRange> significantCodePerLine() {
+ return significantCodePerLine;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/package-info.java
new file mode 100644
index 00000000000..3a93ee2e007
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/internal/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.code.internal;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/package-info.java
new file mode 100644
index 00000000000..e8b9a012e09
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/code/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.code;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java
index ef225532139..e46b8121830 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
import org.sonar.api.batch.sensor.error.AnalysisError;
@@ -35,7 +36,6 @@ import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
-import org.sonar.api.utils.SonarException;
import static com.google.common.base.Preconditions.checkArgument;
@@ -52,6 +52,7 @@ class InMemorySensorStorage implements SensorStorage {
Multimap<String, DefaultCoverage> coverageByComponent = ArrayListMultimap.create();
Map<String, DefaultSymbolTable> symbolsPerComponent = new HashMap<>();
Map<String, String> contextProperties = new HashMap<>();
+ Map<String, DefaultSignificantCode> significantCodePerComponent = new HashMap<>();
@Override
public void store(Measure measure) {
@@ -59,7 +60,7 @@ class InMemorySensorStorage implements SensorStorage {
String componentKey = measure.inputComponent().key();
String metricKey = measure.metric().key();
if (measuresByComponentAndMetric.contains(componentKey, metricKey)) {
- throw new SonarException("Can not add the same measure twice");
+ throw new IllegalStateException("Can not add the same measure twice");
}
measuresByComponentAndMetric.row(componentKey).put(metricKey, measure);
}
@@ -74,7 +75,7 @@ class InMemorySensorStorage implements SensorStorage {
String fileKey = highlighting.inputFile().key();
// Emulate duplicate storage check
if (highlightingByComponent.containsKey(fileKey)) {
- throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + highlighting.inputFile().relativePath());
+ throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + highlighting.inputFile());
}
highlightingByComponent.put(fileKey, highlighting);
}
@@ -90,7 +91,7 @@ class InMemorySensorStorage implements SensorStorage {
String fileKey = defaultCpdTokens.inputFile().key();
// Emulate duplicate storage check
if (cpdTokensByComponent.containsKey(fileKey)) {
- throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + defaultCpdTokens.inputFile().relativePath());
+ throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + defaultCpdTokens.inputFile());
}
cpdTokensByComponent.put(fileKey, defaultCpdTokens);
}
@@ -100,7 +101,7 @@ class InMemorySensorStorage implements SensorStorage {
String fileKey = symbolTable.inputFile().key();
// Emulate duplicate storage check
if (symbolsPerComponent.containsKey(fileKey)) {
- throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile().relativePath());
+ throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile());
}
symbolsPerComponent.put(fileKey, symbolTable);
}
@@ -121,4 +122,14 @@ class InMemorySensorStorage implements SensorStorage {
public void store(ExternalIssue issue) {
allExternalIssues.add(issue);
}
+
+ @Override
+ public void store(DefaultSignificantCode significantCode) {
+ String fileKey = significantCode.inputFile().key();
+ // Emulate duplicate storage check
+ if (significantCodePerComponent.containsKey(fileKey)) {
+ throw new UnsupportedOperationException("Trying to save significant code information twice for the same file is not supported: " + significantCode.inputFile());
+ }
+ significantCodePerComponent.put(fileKey, significantCode);
+ }
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
index b13340fb1c1..76977f55bd7 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
@@ -47,6 +47,8 @@ import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.coverage.NewCoverage;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
@@ -268,6 +270,17 @@ public class SensorContextTester implements SensorContext {
}
@CheckForNull
+ public TextRange significantCodeTextRange(String fileKey, int line) {
+ if (sensorStorage.significantCodePerComponent.containsKey(fileKey)) {
+ return sensorStorage.significantCodePerComponent.get(fileKey)
+ .significantCodePerLine()
+ .get(line);
+ }
+ return null;
+
+ }
+
+ @CheckForNull
public static Integer maxOrNull(@Nullable Integer o1, @Nullable Integer o2) {
return o1 == null ? o2 : Math.max(o1, o2);
}
@@ -366,4 +379,9 @@ public class SensorContextTester implements SensorContext {
DefaultInputFile file = (DefaultInputFile) inputFile;
file.setPublished(true);
}
+
+ @Override
+ public NewSignificantCode newSignificantCode() {
+ return new DefaultSignificantCode(sensorStorage);
+ }
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
index d8c7cb5648f..ede3c9e9910 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
@@ -20,6 +20,7 @@
package org.sonar.api.batch.sensor.internal;
import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
import org.sonar.api.batch.sensor.error.AnalysisError;
@@ -71,4 +72,9 @@ public interface SensorStorage {
* @since 6.1
*/
void storeProperty(String key, String value);
+
+ /**
+ * @since 7.2
+ */
+ void store(DefaultSignificantCode significantCode);
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCodeTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCodeTest.java
new file mode 100644
index 00000000000..73b5f57ed9b
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/code/internal/DefaultSignificantCodeTest.java
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.code.internal;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultSignificantCodeTest {
+ private SensorStorage sensorStorage = mock(SensorStorage.class);
+ private DefaultSignificantCode underTest = new DefaultSignificantCode(sensorStorage);
+ private InputFile inputFile = TestInputFileBuilder.create("module", "file1.xoo")
+ .setContents("this is\na file\n with some code")
+ .build();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void should_save_ranges() {
+ underTest.onFile(inputFile)
+ .addRange(inputFile.selectLine(1))
+ .save();
+ verify(sensorStorage).store(underTest);
+ }
+
+ @Test
+ public void fail_if_save_without_file() {
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Call onFile() first");
+ underTest.save();
+ }
+
+ @Test
+ public void fail_if_add_range_to_same_line_twice() {
+ underTest.onFile(inputFile);
+ underTest.addRange(inputFile.selectLine(1));
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Significant code was already reported for line '1'.");
+ underTest.addRange(inputFile.selectLine(1));
+ }
+
+ @Test
+ public void fail_if_range_includes_many_lines() {
+ underTest.onFile(inputFile);
+
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("Ranges of significant code must be located in a single line");
+ underTest.addRange(inputFile.newRange(1, 1, 2, 1));
+ }
+
+ @Test
+ public void fail_if_add_range_before_setting_file() {
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("addRange() should be called after on()");
+ underTest.addRange(inputFile.selectLine(1));
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
index c43f0275b34..a4d659e29f0 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
@@ -47,7 +47,6 @@ import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
-import org.sonar.api.utils.SonarException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
@@ -176,7 +175,7 @@ public class SensorContextTesterTest {
assertThat(tester.measure("foo", "directories")).isNotNull();
}
- @Test(expected = SonarException.class)
+ @Test(expected = IllegalStateException.class)
public void duplicateMeasures() {
tester.<Integer>newMeasure()
.on(new TestInputFileBuilder("foo", "src/Foo.java").build())
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
index 595a9d126fb..04b3ca16a2d 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
@@ -29,6 +29,8 @@ import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.coverage.NewCoverage;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
@@ -54,6 +56,7 @@ import org.sonar.scanner.sensor.noop.NoOpNewCoverage;
import org.sonar.scanner.sensor.noop.NoOpNewCpdTokens;
import org.sonar.scanner.sensor.noop.NoOpNewExternalIssue;
import org.sonar.scanner.sensor.noop.NoOpNewHighlighting;
+import org.sonar.scanner.sensor.noop.NoOpNewSignificantCode;
import org.sonar.scanner.sensor.noop.NoOpNewSymbolTable;
@ThreadSafe
@@ -65,6 +68,7 @@ public class DefaultSensorContext implements SensorContext {
static final NoOpNewAnalysisError NO_OP_NEW_ANALYSIS_ERROR = new NoOpNewAnalysisError();
static final NoOpNewCoverage NO_OP_NEW_COVERAGE = new NoOpNewCoverage();
static final NoOpNewExternalIssue NO_OP_NEW_EXTERNAL_ISSUE = new NoOpNewExternalIssue();
+ static final NoOpNewSignificantCode NO_OP_NEW_SIGNIFICANT_CODE = new NoOpNewSignificantCode();
private final Settings mutableSettings;
private final FileSystem fs;
@@ -196,4 +200,11 @@ public class DefaultSensorContext implements SensorContext {
file.setPublished(true);
}
+ @Override
+ public NewSignificantCode newSignificantCode() {
+ if (analysisMode.isIssues()) {
+ return NO_OP_NEW_SIGNIFICANT_CODE;
+ }
+ return new DefaultSignificantCode(sensorStorage);
+ }
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
index 1ccd219c600..c4ca921f7ea 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
@@ -36,6 +36,7 @@ import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
import org.sonar.api.batch.sensor.error.AnalysisError;
@@ -424,7 +425,7 @@ public class DefaultSensorStorage implements SensorStorage {
inputFile.setPublished(true);
int componentRef = inputFile.batchId();
if (writer.hasComponentData(FileStructure.Domain.SYMBOLS, componentRef)) {
- throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile().absolutePath());
+ throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile());
}
final ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder();
final ScannerReport.TextRange.Builder rangeBuilder = ScannerReport.TextRange.newBuilder();
@@ -507,4 +508,28 @@ public class DefaultSensorStorage implements SensorStorage {
contextPropertiesCache.put(key, value);
}
+ @Override
+ public void store(DefaultSignificantCode significantCode) {
+ ScannerReportWriter writer = reportPublisher.getWriter();
+ DefaultInputFile inputFile = (DefaultInputFile) significantCode.inputFile();
+ if (shouldSkipStorage(inputFile)) {
+ return;
+ }
+ inputFile.setPublished(true);
+ int componentRef = inputFile.batchId();
+ if (writer.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, componentRef)) {
+ throw new UnsupportedOperationException(
+ "Trying to save significant code information twice for the same file is not supported: " + significantCode.inputFile());
+ }
+
+ List<ScannerReport.LineSgnificantCode> protobuf = significantCode.significantCodePerLine().values().stream()
+ .map(range -> ScannerReport.LineSgnificantCode.newBuilder()
+ .setLine(range.start().line())
+ .setStartOffset(range.start().lineOffset())
+ .setEndOffset(range.end().lineOffset())
+ .build())
+ .collect(Collectors.toList());
+
+ writer.writeComponentSignificantCode(componentRef, protobuf);
+ }
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewSignificantCode.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewSignificantCode.java
new file mode 100644
index 00000000000..f7c7f223759
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewSignificantCode.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.sensor.noop;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.sensor.code.NewSignificantCode;
+
+public class NoOpNewSignificantCode implements NewSignificantCode {
+
+ @Override
+ public NewSignificantCode onFile(InputFile file) {
+ // no op
+ return this;
+ }
+
+ @Override
+ public NewSignificantCode addRange(TextRange range) {
+ // no op
+ return this;
+ }
+
+ @Override
+ public void save() {
+ // no op
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
index ee1c4a34872..1d0e59b46c2 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
@@ -88,6 +88,14 @@ public class DefaultSensorContextTest {
assertThat(adaptor.newMeasure()).isNotNull();
assertThat(adaptor.newAnalysisError()).isEqualTo(DefaultSensorContext.NO_OP_NEW_ANALYSIS_ERROR);
assertThat(adaptor.isCancelled()).isFalse();
+ assertThat(adaptor.newSignificantCode()).isNotNull();
+ }
+
+ @Test
+ public void shouldSkipSignificantCodeOnPreviewMode() {
+ when(analysisMode.isIssues()).thenReturn(true);
+ assertThat(adaptor.newSignificantCode()).isEqualTo(DefaultSensorContext.NO_OP_NEW_SIGNIFICANT_CODE);
+
}
@Test
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
index bb039e2da18..27b67e50220 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
@@ -34,6 +34,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.sensor.code.internal.DefaultSignificantCode;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
import org.sonar.api.batch.sensor.issue.ExternalIssue;
@@ -216,6 +217,33 @@ public class DefaultSensorStorageTest {
}
@Test
+ public void should_skip_significant_code_on_pull_request_when_file_status_is_SAME() {
+ DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
+ .setStatus(InputFile.Status.SAME)
+ .setContents("foo")
+ .build();
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
+
+ underTest.store(new DefaultSignificantCode()
+ .onFile(file)
+ .addRange(file.selectLine(1)));
+
+ assertThat(reportWriter.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, file.batchId())).isFalse();
+ }
+
+ @Test
+ public void should_save_significant_code() {
+ DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
+ .setContents("foo")
+ .build();
+ underTest.store(new DefaultSignificantCode()
+ .onFile(file)
+ .addRange(file.selectLine(1)));
+
+ assertThat(reportWriter.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, file.batchId())).isTrue();
+ }
+
+ @Test
public void should_save_project_measure() throws IOException {
String projectKey = "myProject";
DefaultInputModule module = new DefaultInputModule(ProjectDefinition.create().setKey(projectKey).setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder()));
@@ -244,6 +272,16 @@ public class DefaultSensorStorageTest {
}
@Test(expected = UnsupportedOperationException.class)
+ public void duplicateSignificantCode() throws Exception {
+ InputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java")
+ .setModuleBaseDir(temp.newFolder().toPath()).build();
+ DefaultSignificantCode h = new DefaultSignificantCode(null)
+ .onFile(inputFile);
+ underTest.store(h);
+ underTest.store(h);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
public void duplicateSymbolTable() throws Exception {
InputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java")
.setModuleBaseDir(temp.newFolder().toPath()).build();
diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java
index 019e64d169d..44ae856a6c2 100644
--- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java
+++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java
@@ -42,7 +42,8 @@ public class FileStructure {
COVERAGES("coverages-", Domain.PB),
TESTS("tests-", Domain.PB),
COVERAGE_DETAILS("coverage-details-", Domain.PB),
- SOURCE("source-", ".txt");
+ SOURCE("source-", ".txt"),
+ SGNIFICANT_CODE("sgnificant-code-", Domain.PB);
private static final String PB = ".pb";
private final String filePrefix;
@@ -82,7 +83,7 @@ public class FileStructure {
public File contextProperties() {
return new File(dir, "context-props.pb");
}
-
+
public File root() {
return dir;
}
diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java
index ebb6fe925e2..7df46f355cc 100644
--- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java
+++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java
@@ -73,6 +73,12 @@ public class ScannerReportWriter {
return file;
}
+ public File writeComponentSignificantCode(int componentRef, Iterable<ScannerReport.LineSgnificantCode> lineSignificantCode) {
+ File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, componentRef);
+ Protobuf.writeStream(lineSignificantCode, file, false);
+ return file;
+ }
+
public void appendComponentIssue(int componentRef, ScannerReport.Issue issue) {
File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) {
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index 922adca6f57..66930fb3a69 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -263,6 +263,12 @@ message TextRange {
int32 end_offset = 4;
}
+message LineSgnificantCode {
+ int32 line = 1;
+ int32 start_offset = 2;
+ int32 end_offset = 3;
+}
+
message Symbol {
TextRange declaration = 1;
repeated TextRange reference = 2;
diff --git a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java
index f4ee4dc3ed3..6d0223be28d 100644
--- a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java
+++ b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java
@@ -284,6 +284,21 @@ public class ScannerReportWriterTest {
}
@Test
+ public void write_line_significant_code() {
+ // no data yet
+ assertThat(underTest.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, 1)).isFalse();
+
+ underTest.writeComponentSignificantCode(1, asList(
+ ScannerReport.LineSgnificantCode.newBuilder()
+ .setLine(1)
+ .setStartOffset(2)
+ .setEndOffset(3)
+ .build()));
+
+ assertThat(underTest.hasComponentData(FileStructure.Domain.SGNIFICANT_CODE, 1)).isTrue();
+ }
+
+ @Test
public void write_coverage() {
// no data yet
assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGES, 1)).isFalse();