Skip to content

Commit

Permalink
SONARGO-95 Get rid of slang-plugin (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
mstachniuk authored Jan 3, 2025
1 parent e6f48e8 commit db44e72
Show file tree
Hide file tree
Showing 41 changed files with 4,512 additions and 30 deletions.
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ sonar-analyzer-commons = { group = "org.sonarsource.analyzer-commons", name = "s
sonar-analyzer-test-commons = { group = "org.sonarsource.analyzer-commons", name = "sonar-analyzer-test-commons", version.ref = "analyzer-commons" }
slang-api = { group = "org.sonarsource.slang", name = "slang-api", version.ref = "slang-dependencies" }
slang-checks = { group = "org.sonarsource.slang", name = "slang-checks", version.ref = "slang-dependencies" }
slang-plugin = { group = "org.sonarsource.slang", name = "slang-plugin", version.ref = "slang-dependencies" }
checkstyle-import = { group = "org.sonarsource.slang", name = "checkstyle-import", version.ref = "slang-dependencies" }
minimal-json = { group = "com.eclipsesource.minimal-json", name = "minimal-json", version.ref = "minimal-json" }
sonar-plugin-api-test-fixtures = { group = "org.sonarsource.api.plugin", name = "sonar-plugin-api-test-fixtures", version.ref = "plugin-api" }
Expand Down
1 change: 0 additions & 1 deletion sonar-go-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ dependencies {
compileOnly(libs.sonar.plugin.api)

implementation(libs.sonar.analyzer.commons)
implementation(libs.slang.plugin)
implementation(libs.slang.checks)
implementation(libs.slang.api)
implementation(libs.checkstyle.import)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* SonarSource Go
* Copyright (C) 2018-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* 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 Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.go.externalreport;

import java.io.File;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonarsource.analyzer.commons.ExternalReportProvider;

public abstract class AbstractPropertyHandlerSensor implements Sensor {

private static final Logger LOG = LoggerFactory.getLogger(AbstractPropertyHandlerSensor.class);
private final AnalysisWarnings analysisWarnings;
private final String propertyKey;
private final String propertyName;
private final String configurationKey;
private final String languageKey;

protected AbstractPropertyHandlerSensor(AnalysisWarnings analysisWarnings, String propertyKey, String propertyName,
String configurationKey, String languageKey) {
this.analysisWarnings = analysisWarnings;
this.propertyKey = propertyKey;
this.propertyName = propertyName;
this.configurationKey = configurationKey;
this.languageKey = languageKey;
}

public final String propertyName() {
return propertyName;
}

public final String propertyKey() {
return propertyKey;
}

public final String configurationKey() {
return configurationKey;
}

@Override
public void describe(SensorDescriptor descriptor) {
descriptor
.onlyOnLanguage(languageKey)
.onlyWhenConfiguration(conf -> conf.hasKey(configurationKey()))
.name("Import of " + propertyName() + " issues");
}

@Override
public void execute(SensorContext context) {
executeOnFiles(reportFiles(context), reportConsumer(context));
}

public abstract Consumer<File> reportConsumer(SensorContext context);

private void executeOnFiles(List<File> reportFiles, Consumer<File> action) {
reportFiles.stream()
.filter(File::exists)
.forEach(file -> {
LOG.info("Importing {}", file);
action.accept(file);
});
reportMissingFiles(reportFiles);
}

private List<File> reportFiles(SensorContext context) {
return ExternalReportProvider.getReportFiles(context, configurationKey());
}

private void reportMissingFiles(List<File> reportFiles) {
List<String> missingFiles = reportFiles.stream()
.filter(file -> !file.exists())
.map(File::getPath)
.toList();

if (!missingFiles.isEmpty()) {
String missingFilesAsString = missingFiles.stream().collect(Collectors.joining("\n- ", "\n- ", ""));
String logWarning = String.format("Unable to import %s report file(s):%s%nThe report file(s) can not be found. Check that the property '%s' is correctly configured.",
propertyName(), missingFilesAsString, configurationKey());
LOG.warn(logWarning);

String uiWarning = String.format("Unable to import %d %s report file(s).%nPlease check that property '%s' is correctly configured and the analysis logs for more details.",
missingFiles.size(), propertyName(), configurationKey());
analysisWarnings.addUnique(uiWarning);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import org.sonar.api.server.rule.RulesDefinition.NewRepository;
import org.sonar.api.server.rule.RulesDefinition.NewRule;
import org.sonar.go.plugin.GoLanguage;
import org.sonarsource.slang.plugin.AbstractPropertyHandlerSensor;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.sonarsource.slang.utils.LogArg.lazyArg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.sonar.api.rules.RuleType;
import org.sonar.go.plugin.GoLanguage;
import org.sonarsource.slang.externalreport.CheckstyleFormatImporter;
import org.sonarsource.slang.plugin.AbstractPropertyHandlerSensor;

public class GolangCILintReportSensor extends AbstractPropertyHandlerSensor {

Expand Down
129 changes: 129 additions & 0 deletions sonar-go-plugin/src/main/java/org/sonar/go/plugin/ChecksVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SonarSource Go
* Copyright (C) 2018-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* 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 Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.go.plugin;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import org.sonar.api.batch.rule.Checks;
import org.sonar.api.rule.RuleKey;
import org.sonarsource.slang.api.HasTextRange;
import org.sonarsource.slang.api.TextRange;
import org.sonarsource.slang.api.Tree;
import org.sonarsource.slang.checks.api.CheckContext;
import org.sonarsource.slang.checks.api.InitContext;
import org.sonarsource.slang.checks.api.SecondaryLocation;
import org.sonarsource.slang.checks.api.SlangCheck;
import org.sonarsource.slang.visitors.TreeVisitor;

public class ChecksVisitor extends TreeVisitor<InputFileContext> {

private final DurationStatistics statistics;

public ChecksVisitor(Checks<SlangCheck> checks, DurationStatistics statistics) {
this.statistics = statistics;
Collection<SlangCheck> rulesActiveInSonarQube = checks.all();
for (SlangCheck check : rulesActiveInSonarQube) {
RuleKey ruleKey = checks.ruleKey(check);
Objects.requireNonNull(ruleKey);
check.initialize(new ContextAdapter(ruleKey));
}
}

public class ContextAdapter implements InitContext, CheckContext {

public final RuleKey ruleKey;
private InputFileContext currentCtx;

public ContextAdapter(RuleKey ruleKey) {
this.ruleKey = ruleKey;
}

@Override
public <T extends Tree> void register(Class<T> cls, BiConsumer<CheckContext, T> visitor) {
ChecksVisitor.this.register(cls, statistics.time(ruleKey.rule(), (ctx, tree) -> {
currentCtx = ctx;
visitor.accept(this, tree);
}));
}

@Override
public Deque<Tree> ancestors() {
return currentCtx.ancestors();
}

@Override
public String filename() {
return currentCtx.inputFile.filename();
}

@Override
public String fileContent() {
try {
return currentCtx.inputFile.contents();
} catch (IOException e) {
throw new IllegalStateException("Cannot read content of " + currentCtx.inputFile, e);
}
}

@Override
public void reportIssue(TextRange textRange, String message) {
reportIssue(textRange, message, Collections.emptyList(), null);
}

@Override
public void reportIssue(HasTextRange toHighlight, String message) {
reportIssue(toHighlight, message, Collections.emptyList());
}

@Override
public void reportIssue(HasTextRange toHighlight, String message, SecondaryLocation secondaryLocation) {
reportIssue(toHighlight, message, Collections.singletonList(secondaryLocation));
}

@Override
public void reportIssue(HasTextRange toHighlight, String message, List<SecondaryLocation> secondaryLocations) {
reportIssue(toHighlight, message, secondaryLocations, null);
}

@Override
public void reportIssue(HasTextRange toHighlight, String message, List<SecondaryLocation> secondaryLocations, @Nullable Double gap) {
reportIssue(toHighlight.textRange(), message, secondaryLocations, gap);
}

@Override
public void reportFileIssue(String message) {
reportFileIssue(message, null);
}

@Override
public void reportFileIssue(String message, @Nullable Double gap) {
reportIssue((TextRange) null, message, Collections.emptyList(), gap);
}

private void reportIssue(@Nullable TextRange textRange, String message, List<SecondaryLocation> secondaryLocations, @Nullable Double gap) {
currentCtx.reportIssue(ruleKey, textRange, message, secondaryLocations, gap);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* SonarSource Go
* Copyright (C) 2018-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* 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 Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.go.plugin;

import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.sonarsource.slang.api.Comment;
import org.sonarsource.slang.api.TextRange;

public class CommentAnalysisUtils {
public static final String NOSONAR_PREFIX = "NOSONAR";

private static final boolean[] IS_NON_BLANK_CHAR_IN_COMMENTS = new boolean[127];
static {
for (int c = 0; c < IS_NON_BLANK_CHAR_IN_COMMENTS.length; c++) {
IS_NON_BLANK_CHAR_IN_COMMENTS[c] = c > ' ' && "*#-=|".indexOf(c) == -1;
}
}

private CommentAnalysisUtils() {
}

static boolean isNosonarComment(Comment comment) {
return comment.contentText().trim().toUpperCase(Locale.ENGLISH).startsWith(NOSONAR_PREFIX);
}

static Set<Integer> findNonEmptyCommentLines(TextRange range, String content) {
Set<Integer> lineNumbers = new HashSet<>();

int startLine = range.start().line();
if (startLine == range.end().line()) {
if (isNotBlank(content)) {
lineNumbers.add(startLine);
}
} else {
String[] lines = content.split("\r\n|\n|\r", -1);
for (int i = 0; i < lines.length; i++) {
if (isNotBlank(lines[i])) {
lineNumbers.add(startLine + i);
}
}
}

return lineNumbers;
}

private static boolean isNotBlank(String line) {
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (ch >= IS_NON_BLANK_CHAR_IN_COMMENTS.length || IS_NON_BLANK_CHAR_IN_COMMENTS[ch]) {
return true;
}
}
return false;
}
}
Loading

0 comments on commit db44e72

Please sign in to comment.