]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11465 NoSonarFilter moved to project level
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 13 Nov 2018 14:38:22 +0000 (15:38 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Jan 2019 08:42:59 +0000 (09:42 +0100)
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/NoSonarFilter.java
sonar-plugin-api/src/test/java/org/sonar/api/issue/NoSonarFilterTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java

index 278d288d18525c69c1005bcd42bb72a60043977a..f7d640a4dbbbd07f790aecb1bacf52d148d74e1e 100644 (file)
@@ -30,6 +30,8 @@ import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.function.Consumer;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -56,6 +58,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
   private Metadata metadata;
   private boolean published;
   private boolean excludedForCoverage;
+  private final Set<Integer> noSonarLines = new HashSet<>();
 
   public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) {
     this(indexedFile, metadataGenerator, null);
@@ -82,7 +85,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
   public InputStream inputStream() throws IOException {
     return contents != null ? new ByteArrayInputStream(contents.getBytes(charset()))
       : new BOMInputStream(Files.newInputStream(path()),
-        ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
+      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
   }
 
   @Override
@@ -242,7 +245,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
     checkMetadata();
     Preconditions.checkState(metadata.originalLineEndOffsets() != null, "InputFile is not properly initialized.");
     Preconditions.checkState(metadata.originalLineEndOffsets().length == metadata.lines(),
-            "InputFile is not properly initialized. 'originalLineEndOffsets' property length should be equal to 'lines'");
+      "InputFile is not properly initialized. 'originalLineEndOffsets' property length should be equal to 'lines'");
     return metadata.originalLineEndOffsets();
   }
 
@@ -299,7 +302,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
     int line = findLine(globalOffset);
     int startLineOffset = originalLineStartOffsets()[line - 1];
     // In case the global offset is between \r and \n, move the pointer to a valid location
-    return new DefaultTextPointer(line, Math.min(globalOffset, originalLineEndOffsets()[line -1]) - startLineOffset);
+    return new DefaultTextPointer(line, Math.min(globalOffset, originalLineEndOffsets()[line - 1]) - startLineOffset);
   }
 
   public DefaultInputFile setStatus(Status status) {
@@ -369,4 +372,12 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
     return indexedFile.uri();
   }
 
+  public void noSonarAt(Set<Integer> noSonarLines) {
+    this.noSonarLines.addAll(noSonarLines);
+  }
+
+  public boolean hasNoSonarAt(int line) {
+    return this.noSonarLines.contains(line);
+  }
+
 }
index 947b6d28c18e74d21be687110774e0e26bbfecdc..a58e5f02e4f1b019d293ebc27ed6cac0a82ca44e 100644 (file)
  */
 package org.sonar.api.issue;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Set;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.scan.issue.filter.FilterableIssue;
-import org.sonar.api.scan.issue.filter.IssueFilter;
-import org.sonar.api.scan.issue.filter.IssueFilterChain;
+import org.sonar.api.scanner.ScannerSide;
 
 /**
  * Issue filter used to ignore issues created on lines commented with the tag "NOSONAR".
  * <br>
- * Plugins, via {@link ScannerSide}s, must feed this filter by registering the
+ * Plugins, via {@link org.sonar.api.batch.sensor.Sensor}s, must feed this filter by registering the
  * lines that contain "NOSONAR". Note that filters are disabled for the issues reported by
  * end-users from UI or web services.
  *
  * @since 3.6
  */
-public class NoSonarFilter implements IssueFilter {
-
-  private final Map<String, Set<Integer>> noSonarLinesByResource = new HashMap<>();
-
-  /**
-   * @deprecated since 5.0 use {@link #noSonarInFile(InputFile, Set)}
-   */
-  @Deprecated
-  public NoSonarFilter addComponent(String componentKey, Set<Integer> noSonarLines) {
-    noSonarLinesByResource.put(componentKey, noSonarLines);
-    return this;
-  }
+@ScannerSide
+public class NoSonarFilter {
 
   /**
    * Register lines in a file that contains the NOSONAR flag.
+   *
    * @param inputFile
    * @param noSonarLines Line number starts at 1 in a file
    * @since 5.0
+   * @since 7.6 the method can be called multiple times by different sensors, and NOSONAR lines are merged
    */
   public NoSonarFilter noSonarInFile(InputFile inputFile, Set<Integer> noSonarLines) {
-    noSonarLinesByResource.put(((DefaultInputFile) inputFile).key(), noSonarLines);
+    ((DefaultInputFile) inputFile).noSonarAt(noSonarLines);
     return this;
   }
 
-  @Override
-  public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
-    boolean accepted = true;
-    if (issue.line() != null) {
-      Set<Integer> noSonarLines = noSonarLinesByResource.get(issue.componentKey());
-      accepted = noSonarLines == null || !noSonarLines.contains(issue.line());
-      if (!accepted && StringUtils.containsIgnoreCase(issue.ruleKey().rule(), "nosonar")) {
-        accepted = true;
-      }
-    }
-    if (accepted) {
-      accepted = chain.accept(issue);
-    }
-    return accepted;
-  }
 }
index 3e4435f489bcc7c7470f3a75d12561c3870a8b8d..0665df118786dbab5dbd358fe0f87fb5404f717a 100644 (file)
  */
 package org.sonar.api.issue;
 
-import org.sonar.api.scan.issue.filter.FilterableIssue;
-
-import com.google.common.collect.ImmutableSet;
-import org.junit.Before;
+import java.util.Arrays;
+import java.util.HashSet;
 import org.junit.Test;
-import org.sonar.api.scan.issue.filter.IssueFilterChain;
-import org.sonar.api.rule.RuleKey;
-
-import java.util.Set;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class NoSonarFilterTest {
 
-  NoSonarFilter filter = new NoSonarFilter();
-  IssueFilterChain chain = mock(IssueFilterChain.class);
-
-  @Before
-  public void setupChain() {
-    when(chain.accept(isA(FilterableIssue.class))).thenReturn(true);
-  }
-
-  @Test
-  public void should_ignore_lines_commented_with_nosonar() {
-    FilterableIssue issue = mock(FilterableIssue.class);
-    when(issue.componentKey()).thenReturn("struts:org.apache.Action");
-    when(issue.ruleKey()).thenReturn(RuleKey.of("squid", "AvoidCycles"));
-
-    Set<Integer> noSonarLines = ImmutableSet.of(31, 55);
-    filter.addComponent("struts:org.apache.Action", noSonarLines);
-
-    // issue on file
-    when(issue.line()).thenReturn(null);
-    assertThat(filter.accept(issue, chain)).isTrue();
-
-    // issue on lines
-    when(issue.line()).thenReturn(31);
-    assertThat(filter.accept(issue, chain)).isFalse();
-
-    when(issue.line()).thenReturn(222);
-    assertThat(filter.accept(issue, chain)).isTrue();
-
-    verify(chain, times(2)).accept(issue);
-  }
-
   @Test
-  public void should_accept_issues_on_no_sonar_rules() {
-    // The "No Sonar" rule logs violations on the lines that are flagged with "NOSONAR" !!
-    FilterableIssue issue = mock(FilterableIssue.class);
-    when(issue.componentKey()).thenReturn("struts:org.apache.Action");
-    when(issue.ruleKey()).thenReturn(RuleKey.of("squid", "NoSonarCheck"));
-
-    Set<Integer> noSonarLines = ImmutableSet.of(31, 55);
-    filter.addComponent("struts:org.apache.Action", noSonarLines);
-
-    when(issue.line()).thenReturn(31);
-    assertThat(filter.accept(issue, chain)).isTrue();
-
-    when(issue.line()).thenReturn(222);
-    assertThat(filter.accept(issue, chain)).isTrue();
+  public void should_store_nosonar_lines_on_inputfile() {
+    DefaultInputFile f = TestInputFileBuilder.create("module1", "myfile.java").build();
+    new NoSonarFilter().noSonarInFile(f, new HashSet<>(Arrays.asList(1,4)));
 
-    verify(chain, times(2)).accept(issue);
+     assertThat(f.hasNoSonarAt(1)).isTrue();
+     assertThat(f.hasNoSonarAt(2)).isFalse();
+     assertThat(f.hasNoSonarAt(4)).isTrue();
   }
 }
index edf5417d4f6f0a910df28f062a959313e2b69a95..ce07d5a59dba5f36e3b42993941ab4ca62ccd3ec 100644 (file)
@@ -22,8 +22,10 @@ package org.sonar.scanner.issue;
 import java.util.Collection;
 import java.util.function.Consumer;
 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;
@@ -56,6 +58,10 @@ public class ModuleIssues {
   public boolean initAndAddIssue(Issue issue) {
     DefaultInputComponent inputComponent = (DefaultInputComponent) issue.primaryLocation().inputComponent();
 
+    if (noSonar(inputComponent, issue)) {
+      return false;
+    }
+
     ActiveRule activeRule = activeRules.find(issue.ruleKey());
     if (activeRule == null) {
       // rule does not exist or is not enabled -> ignore the issue
@@ -71,6 +77,14 @@ public class ModuleIssues {
     return false;
   }
 
+  private static boolean noSonar(DefaultInputComponent inputComponent, Issue issue) {
+    TextRange textRange = issue.primaryLocation().textRange();
+    return inputComponent.isFile()
+      && textRange != null
+      && ((DefaultInputFile) inputComponent).hasNoSonarAt(textRange.start().line())
+      && !StringUtils.containsIgnoreCase(issue.ruleKey().rule(), "nosonar");
+  }
+
   public void initAndAddExternalIssue(ExternalIssue issue) {
     DefaultInputComponent inputComponent = (DefaultInputComponent) issue.primaryLocation().inputComponent();
     ScannerReport.ExternalIssue rawExternalIssue = createReportExternalIssue(issue, inputComponent.batchId());
index a8cf432e5f64c92b4aa6ff2ad6c93d839aa5bb29..68bd4a0ffdf66ea33cd628769e4099b5e81b0433 100644 (file)
@@ -23,7 +23,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.FileMetadata;
-import org.sonar.api.issue.NoSonarFilter;
 import org.sonar.api.scan.filesystem.FileExclusions;
 import org.sonar.core.extension.CoreExtensionsInstaller;
 import org.sonar.core.platform.ComponentContainer;
@@ -130,7 +129,6 @@ public class ModuleScanContainer extends ComponentContainer {
 
       // issues
       ModuleIssues.class,
-      NoSonarFilter.class,
 
       // issue exclusions
       IssueInclusionPatternInitializer.class,
index e9c8b229b26461bc202ea0eb370eabddcf7889a9..0171e117e5f959b71a2f433d81f609d56fe10bd7 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.fs.internal.SensorStrategy;
 import org.sonar.api.batch.rule.CheckFactory;
+import org.sonar.api.issue.NoSonarFilter;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.scan.filesystem.PathResolver;
@@ -175,6 +176,7 @@ public class ProjectScanContainer extends ComponentContainer {
       IssueCache.class,
       DefaultProjectIssues.class,
       IssueTransition.class,
+      NoSonarFilter.class,
 
       // metrics
       DefaultMetricFinder.class,
index ed972c1dbc94608ee1970bacd17b0a28329473b6..ab8dd62ce5280e6e416e3197771cd27363da0273 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.scanner.issue;
 
+import java.util.Collections;
+import java.util.HashSet;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -53,6 +55,7 @@ public class ModuleIssuesTest {
 
   static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
   static final String SQUID_RULE_NAME = "Avoid Cycle";
+  private static final RuleKey NOSONAR_RULE_KEY = RuleKey.of("squid", "NoSonarCheck");
 
   @Mock
   ModuleIssueFilters filters;
@@ -179,6 +182,45 @@ public class ModuleIssuesTest {
     verifyZeroInteractions(reportPublisher);
   }
 
+  @Test
+  public void should_ignore_lines_commented_with_nosonar() {
+    ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+    activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
+    initModuleIssues();
+
+    DefaultIssue issue = new DefaultIssue()
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
+      .forRule(SQUID_RULE_KEY);
+
+    file.noSonarAt(new HashSet<>(Collections.singletonList(3)));
+
+    boolean added = moduleIssues.initAndAddIssue(issue);
+
+    assertThat(added).isFalse();
+    verifyZeroInteractions(reportPublisher);
+  }
+
+  @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.create(NOSONAR_RULE_KEY).setSeverity(Severity.INFO).activate();
+    initModuleIssues();
+
+    file.noSonarAt(new HashSet<>(Collections.singletonList(3)));
+
+    DefaultIssue issue = new DefaultIssue()
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
+      .forRule(NOSONAR_RULE_KEY);
+
+    when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true);
+
+    boolean added = moduleIssues.initAndAddIssue(issue);
+
+    assertThat(added).isTrue();
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.batchId()), any());
+  }
+
   /**
    * Every rules and active rules has to be added in builders before creating ModuleIssues
    */