diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7cf170..8c8ffdb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
# SonarQube Plugin for Flutter / Dart
+## 0.3.2
+
+#### Breaking
+
+- None.
+
+#### Experimental
+
+- None.
+
+#### Enhancements
+
+- Allow re-using an existing dartanalyzer report with `sonar.dart.analysis.reportPath` (thanks to [Peter Leibiger](https://github.com/kuhnroyal))
+- Add missing dart keywords `extension`, `on`, `mixin` (thanks to [Peter Leibiger](https://github.com/kuhnroyal))
+- Add pedantic 1.9.0 profile (thanks to [Daniel Morawetz](https://github.com/dmorawetz))
+- Faster analysis with 'flutter analyze' and support for different analysis modes with `sonar.flutter.analyzer.mode` (thanks to [Marc Reichelt](https://github.com/mreichelt))
+
+#### Bug Fixes
+
+- Ensure analyzer encoding is UTF-8 (thanks to [Daniel Morawetz](https://github.com/dmorawetz))
+
+
## 0.3.1
#### Breaking
diff --git a/README.md b/README.md
index 87e3acc..9bc6b68 100644
--- a/README.md
+++ b/README.md
@@ -69,9 +69,19 @@ sonar.projectVersion=1.0
# Use commas to specify more than one folder.
sonar.sources=lib
sonar.tests=test
-
+
# Encoding of the source code. Default is default system encoding.
sonar.sourceEncoding=UTF-8
+
+# Allows reuse of an existing analyzer report
+# sonar.dart.analysis.reportPath=
+
+# Analyzer mode
+# Can be:
+# - flutter (flutter analyze) - default
+# - dart (dart analyze)
+# - legacy (dartanalyzer)
+# sonar.flutter.analyzer.mode=
```
*For a complete list of available options, please refer to the [SonarQube documentation](https://docs.sonarqube.org/latest/analysis/analysis-parameters/).*
@@ -84,10 +94,11 @@ Use the following commands from the root folder to start an analysis:
```console
# Download dependencies
flutter pub get
-# Run tests
-flutter test --machine > tests.output
-# Compute coverage (--machine and --coverage cannot be run at once...)
-flutter test --coverage
+# Run tests with User feedback (in case some test are failing)
+flutter test
+# Run tests without user feedback regeneration tests.output and coverage/lcov.info
+flutter test --machine --coverage > tests.output
+
# Run the analysis and publish to the SonarQube server
sonar-scanner
```
diff --git a/dart-lang/pom.xml b/dart-lang/pom.xml
index 57104c3..9307f67 100644
--- a/dart-lang/pom.xml
+++ b/dart-lang/pom.xml
@@ -3,7 +3,7 @@
sonar-flutter
fr.insideapp.sonarqube
- 0.3.1
+ 0.3.2
4.0.0
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/DartSensor.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/DartSensor.java
index 6aaa9f2..abef2ed 100644
--- a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/DartSensor.java
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/DartSensor.java
@@ -45,6 +45,7 @@ public class DartSensor implements Sensor {
private static final Logger LOGGER = LoggerFactory.getLogger(DartSensor.class);
private static final int EXECUTOR_TIMEOUT = 10000;
public static final String DART_ANALYSIS_USE_EXISTING_OPTIONS_KEY = "sonar.dart.analysis.useExistingOptions";
+ public static final String DART_ANALYSIS_USE_EXISTING_REPORT_PATH_KEY = "sonar.dart.analysis.reportPath";
@Override
public void describe(SensorDescriptor sensorDescriptor) {
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/SourceLinesProvider.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/SourceLinesProvider.java
index 493f506..d9c3a6a 100644
--- a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/SourceLinesProvider.java
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/SourceLinesProvider.java
@@ -59,7 +59,7 @@ public SourceLine[] getLines(final InputStream inputStream, final Charset charse
}
sourceLines.add(new SourceLine(totalLines, count, global - count, global));
} catch (final Throwable e) {
- LOGGER.warn("Error occured reading file", e);
+ LOGGER.warn("Error occurred reading file", e);
}
return sourceLines.toArray(new SourceLine[0]);
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/DartProfilePedantic190.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/DartProfilePedantic190.java
new file mode 100644
index 0000000..03d866d
--- /dev/null
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/DartProfilePedantic190.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube Flutter Plugin
+ * Copyright (C) 2020 inside|app
+ * contact@insideapp.fr
+ *
+ * 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 fr.insideapp.sonarqube.dart.lang.issues;
+
+import fr.insideapp.sonarqube.dart.lang.Dart;
+import fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer.DartAnalyzerRulesDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
+
+public class DartProfilePedantic190 implements BuiltInQualityProfilesDefinition {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DartProfile.class);
+ public static final String DARTANALYZER_PROFILE_PATH = "fr/insideapp/sonarqube/dart/dartanalyzer/profile-pedantic.1.9.0.xml";
+
+ @Override
+ public void define(BuiltInQualityProfilesDefinition.Context context) {
+
+ // dartanalyzer profile
+ LOGGER.info("Creating dartanalyzer pedantic 1.9.0 Profile");
+ NewBuiltInQualityProfile nbiqp = context.createBuiltInQualityProfile("dartanalyzer pedantic 1.9.0", Dart.KEY);
+ XmlProfileParser.parse(DARTANALYZER_PROFILE_PATH, nbiqp);
+ nbiqp.done();
+ }
+}
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/XmlProfileParser.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/XmlProfileParser.java
index bc53661..df2e009 100644
--- a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/XmlProfileParser.java
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/XmlProfileParser.java
@@ -48,18 +48,23 @@ public static void parse(String path, NewBuiltInQualityProfile profile) {
String repositoryKey = getChildContent(ruleElement, "repositoryKey");
String key = getChildContent(ruleElement, "key");
- NewBuiltInActiveRule newActiveRule = profile.activateRule(repositoryKey, key);
+ try {
+ NewBuiltInActiveRule newActiveRule = profile.activateRule(repositoryKey, key);
- NodeList parameterNodeList = ruleElement.getElementsByTagName("parameter");
- XmlFile.asList(parameterNodeList).forEach(parameter -> {
- Element parameterElement = (Element) parameter;
- String paramKey = getChildContent(parameterElement, "key");
- String paramValue = getChildContent(parameterElement, "value");
- newActiveRule.overrideParam(paramKey, paramValue);
- });
+ NodeList parameterNodeList = ruleElement.getElementsByTagName("parameter");
+ XmlFile.asList(parameterNodeList).forEach(parameter -> {
+ Element parameterElement = (Element) parameter;
+ String paramKey = getChildContent(parameterElement, "key");
+ String paramValue = getChildContent(parameterElement, "value");
+ newActiveRule.overrideParam(paramKey, paramValue);
+ });
- Optional priority = getOptionalChildContent(ruleElement, "priority");
- priority.ifPresent(newActiveRule::overrideSeverity);
+ Optional priority = getOptionalChildContent(ruleElement, "priority");
+ priority.ifPresent(newActiveRule::overrideSeverity);
+ } catch (IllegalArgumentException e) {
+ // ignore, if rule was already registered in another profile
+ }
+
});
} catch (IOException e) {
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/AnalyzerMode.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/AnalyzerMode.java
new file mode 100644
index 0000000..4f271d0
--- /dev/null
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/AnalyzerMode.java
@@ -0,0 +1,29 @@
+/*
+ * SonarQube Flutter Plugin
+ * Copyright (C) 2020 inside|app
+ * contact@insideapp.fr
+ *
+ * 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 fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer;
+
+public enum AnalyzerMode {
+ flutter,
+ dart,
+ legacy;
+
+ public static final AnalyzerMode defaultMode = flutter;
+}
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParser.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParser.java
index db0027b..176ee4a 100644
--- a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParser.java
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParser.java
@@ -31,7 +31,7 @@ public List parse(String input) {
List issues = new ArrayList<>();
String[] lines = input.split(System.getProperty("line.separator"));
- Pattern pattern = Pattern.compile("(error|hint|lint)(.*)(-|•)(.*)(-|•)(.*):(.*):(.*)(-|•)(.*)");
+ Pattern pattern = Pattern.compile("(hint|lint|info|warning|error)(.*)(-|•)(.*)(-|•)(.*):(.*):(.*)(-|•)(.*)");
for (int i = 0; i < lines.length; i++) {
Matcher matcher = pattern.matcher(lines[i]);
while (matcher.find()) {
diff --git a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerSensor.java b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerSensor.java
index 1cf7d4e..16d8ab6 100644
--- a/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerSensor.java
+++ b/dart-lang/src/main/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerSensor.java
@@ -23,6 +23,7 @@
import com.google.common.io.Resources;
import fr.insideapp.sonarqube.dart.lang.Dart;
import fr.insideapp.sonarqube.dart.lang.DartSensor;
+import org.apache.commons.lang.NotImplementedException;
import org.buildobjects.process.ProcBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,42 +47,99 @@
import java.util.Set;
import java.util.stream.Collectors;
-public class DartAnalyzerSensor implements Sensor {
- private static final Logger LOGGER = LoggerFactory.getLogger(DartAnalyzerSensor.class);
- private static final String ANALYZER_COMMAND = System.getProperty("os.name").toUpperCase().contains("WINDOWS")
- ? "dartanalyzer.bat"
- : "dartanalyzer";
- private static final int ANALYZER_TIMEOUT = 10 * 60 * 1000;
- private static final String ANALYSIS_OPTIONS_FILENAME = "analysis_options.yaml";
- private static final String ANALYSIS_OPTIONS_FILE = "/fr/insideapp/sonarqube/dart/dartanalyzer/analysis_options.yaml";
- private static final Integer PAGE_SIZE = 50;
- private boolean useExistingAnalysisOptions;
-
- @Override
- public void describe(SensorDescriptor sensorDescriptor) {
- sensorDescriptor.onlyOnLanguage(Dart.KEY).name("dartanalyzer sensor").onlyOnFileType(Type.MAIN);
- }
+import static java.util.Arrays.asList;
- @Override
- public void execute(SensorContext sensorContext) {
- try {
- verifyIfDartAnalyzerExists();
-
- selectOptionFileToUse(sensorContext);
-
- recordIssues(sensorContext, buildIssues(getFilesWithAbsolutePath(sensorContext)));
-
- } catch (Exception e) {
- LOGGER.error(e.getMessage(), e);
- } finally {
- if (!useExistingAnalysisOptions) {
- restoreCurrentAnalysisOptionsFile(sensorContext);
- }
- }
- }
- private void selectOptionFileToUse(SensorContext sensorContext) throws IOException {
- useExistingAnalysisOptions = getUseExistingAnalysisOptions(sensorContext);
+public class DartAnalyzerSensor implements Sensor {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DartAnalyzerSensor.class);
+ private static final String LEGACY_COMMAND = System.getProperty("os.name").toUpperCase().contains("WINDOWS")
+ ? "dartanalyzer.bat"
+ : "dartanalyzer";
+ private static final String FLUTTER_COMMAND = System.getProperty("os.name").toUpperCase().contains("WINDOWS")
+ ? "flutter.bat"
+ : "flutter";
+ private static final String DART_COMMAND = System.getProperty("os.name").toUpperCase().contains("WINDOWS")
+ ? "dart.bat"
+ : "dart";
+ private static final int ANALYZER_TIMEOUT = 10 * 60 * 1000;
+ private static final String ANALYSIS_OPTIONS_FILENAME = "analysis_options.yaml";
+ private static final String ANALYSIS_OPTIONS_FILE = "/fr/insideapp/sonarqube/dart/dartanalyzer/analysis_options.yaml";
+ private static final Integer PAGE_SIZE = 50;
+ private boolean useExistingAnalysisOptions;
+ public static final String FLUTTER_ANALYZER_MODE = "sonar.flutter.analyzer.mode";
+ public static final List FLUTTER_ANALYZER_MODE_OPTIONS = asList(AnalyzerMode.values());
+
+ @Override
+ public void describe(SensorDescriptor sensorDescriptor) {
+ sensorDescriptor.onlyOnLanguage(Dart.KEY).name("dartanalyzer sensor").onlyOnFileType(Type.MAIN);
+ }
+
+ @Override
+ public void execute(SensorContext sensorContext) {
+
+ AnalyzerMode analyzerMode = getAnalyzerMode(sensorContext);
+ LOGGER.info("Chosen analyzer mode: {}", analyzerMode);
+
+ switch (analyzerMode) {
+ case legacy:
+ try {
+
+ verifyIfDartAnalyzerExists();
+
+ selectOptionFileToUse(sensorContext);
+
+ recordIssues(sensorContext, buildIssues(getFilesWithAbsolutePath(sensorContext)));
+
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ } finally {
+ if (!useExistingAnalysisOptions) {
+ restoreCurrentAnalysisOptionsFile(sensorContext);
+ }
+ }
+ break;
+
+ case flutter:
+ recordIssuesFromAnalyzer(sensorContext, FLUTTER_COMMAND);
+ break;
+
+ case dart:
+ recordIssuesFromAnalyzer(sensorContext, DART_COMMAND);
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ private void recordIssuesFromAnalyzer(SensorContext sensorContext, String analyzerCommand) {
+ try {
+ final List issues = getIssuesFromAnalyzer(analyzerCommand);
+ recordIssues(sensorContext, issues);
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ }
+
+ private List getIssuesFromAnalyzer(String analyzerCommand) throws IOException {
+ try {
+ LOGGER.info("Running '{} analyze'...", analyzerCommand);
+ String output = new ProcBuilder(analyzerCommand, "analyze")
+ .withTimeoutMillis(ANALYZER_TIMEOUT)
+ .ignoreExitStatus()
+ .run()
+ .getOutputString();
+
+ List issues = new DartAnalyzerReportParser().parse(output);
+ LOGGER.info("Found issues: {}", issues.size());
+ return issues;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void selectOptionFileToUse(SensorContext sensorContext) throws IOException {
+ useExistingAnalysisOptions = getUseExistingAnalysisOptions(sensorContext);
// Usage of existing option is required but file is missing
// Or usage of existing option is not required
@@ -90,199 +148,207 @@ private void selectOptionFileToUse(SensorContext sensorContext) throws IOExcepti
}
}
- private void useDefaultAnalysisOptionsFile(SensorContext sensorContext) throws IOException {
- LOGGER.debug("Either {} option is not set to true or {} file does not exists, use default analysis options instead",
- DartSensor.DART_ANALYSIS_USE_EXISTING_OPTIONS_KEY, ANALYSIS_OPTIONS_FILENAME);
-
- useExistingAnalysisOptions = false;
-
- this.saveCurrentAnalysisOptionsFile(sensorContext);
- this.createAnalysisOptionsFile(sensorContext);
- }
+ private void useDefaultAnalysisOptionsFile(SensorContext sensorContext) throws IOException {
+ LOGGER.debug("Either {} option is not set to true or {} file does not exists, use default analysis options instead",
+ DartSensor.DART_ANALYSIS_USE_EXISTING_OPTIONS_KEY, ANALYSIS_OPTIONS_FILENAME);
- private Boolean getUseExistingAnalysisOptions(SensorContext sensorContext) {
- return sensorContext.config().getBoolean(DartSensor.DART_ANALYSIS_USE_EXISTING_OPTIONS_KEY).orElse(false);
- }
+ useExistingAnalysisOptions = false;
- private List buildIssues(List filesWithAbsolutePath)
- throws IOException {
- Set issues = new HashSet<>();
+ this.saveCurrentAnalysisOptionsFile(sensorContext);
+ this.createAnalysisOptionsFile(sensorContext);
+ }
- for (String paginatedFileBatch : getPaginatedFilesPaths(filesWithAbsolutePath)) {
+ private AnalyzerMode getAnalyzerMode(SensorContext sensorContext) {
+ return sensorContext.config()
+ .get(FLUTTER_ANALYZER_MODE)
+ .map(AnalyzerMode::valueOf)
+ .orElse(AnalyzerMode.defaultMode);
+ }
- LOGGER.debug("Current file batch: {}", paginatedFileBatch);
+ private Boolean getUseExistingAnalysisOptions(SensorContext sensorContext) {
+ return sensorContext.config().getBoolean(DartSensor.DART_ANALYSIS_USE_EXISTING_OPTIONS_KEY).orElse(false);
+ }
- try {
- String output = new ProcBuilder(ANALYZER_COMMAND)
- .withArgs(paginatedFileBatch.split(" "))
- .withTimeoutMillis(ANALYZER_TIMEOUT)
- //.withExpectedExitStatuses(0, 1, 2, 3)
- .ignoreExitStatus()
- .run()
- .getOutputString();
+ private List buildIssues(List filesWithAbsolutePath)
+ throws IOException {
+ Set issues = new HashSet<>();
- issues.addAll(new DartAnalyzerReportParser().parse(output));
- } catch (Exception e) {
- throw new IOException(e);
- }
+ for (String paginatedFileBatch : getPaginatedFilesPaths(filesWithAbsolutePath)) {
+ LOGGER.debug("Current file batch: {}", paginatedFileBatch);
- }
- LOGGER.debug("Found issues: {}", issues.size());
- List result = new ArrayList<>();
- result.addAll(issues);
- return result;
- }
+ try {
+ byte[] outputBytes = new ProcBuilder(LEGACY_COMMAND)
+ .withArgs(paginatedFileBatch.split(" "))
+ .withTimeoutMillis(ANALYZER_TIMEOUT)
+ //.withExpectedExitStatuses(0, 1, 2, 3)
+ .ignoreExitStatus()
+ .run()
+ .getOutputBytes();
- private List getPaginatedFilesPaths(List filesWithAbsolutePath) {
- List paginated = new ArrayList();
+ String output = new String(outputBytes, "UTF-8");
+ issues.addAll(new DartAnalyzerReportParser().parse(output));
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
- LOGGER.debug("Paging the files to execute the analyzer...");
- Integer pagingStart = 0;
- Integer pagingRemainder = getPaginationRemainder(filesWithAbsolutePath);
- Integer totalPages = getPaginationTotalPages(filesWithAbsolutePath, PAGE_SIZE, pagingRemainder);
+ }
+ LOGGER.debug("Found issues: {}", issues.size());
+ List result = new ArrayList<>();
+ result.addAll(issues);
+ return result;
+ }
- for (int i = 0; i < totalPages; i++) {
- LOGGER.debug("Current index: {}", i);
+ private List getPaginatedFilesPaths(List filesWithAbsolutePath) {
+ List paginated = new ArrayList();
- LOGGER.debug("Current paging start: {}", pagingStart);
+ LOGGER.debug("Paging the files to execute the analyzer...");
- Integer pagingEnd = getPagingEnd(pagingStart, pagingRemainder, totalPages, i);
+ Integer pagingStart = 0;
+ Integer pagingRemainder = getPaginationRemainder(filesWithAbsolutePath);
+ Integer totalPages = getPaginationTotalPages(filesWithAbsolutePath, PAGE_SIZE, pagingRemainder);
- paginated.add(getFilesPathsSplitBySpace(filesWithAbsolutePath, pagingStart, pagingEnd));
+ for (int i = 0; i < totalPages; i++) {
+ LOGGER.debug("Current index: {}", i);
- pagingStart += PAGE_SIZE;
- }
- return paginated;
- }
+ LOGGER.debug("Current paging start: {}", pagingStart);
- private Integer getPagingEnd(Integer pagingStart, Integer pagingRemainder, Integer totalPages, int i) {
- Integer pagingEnd = pagingStart + PAGE_SIZE;
- if (isLastPage(pagingRemainder, totalPages, i)) {
- pagingEnd = pagingStart + pagingRemainder;
- }
- LOGGER.debug("Current paging end: {}", pagingEnd);
- return pagingEnd;
- }
-
- private String getFilesPathsSplitBySpace(List filesWithAbsolutePath, Integer pagingStart,
- Integer pagingEnd) {
- return filesWithAbsolutePath.subList(pagingStart, pagingEnd).stream().collect(Collectors.joining(" "));
- }
-
- private boolean isLastPage(Integer pagingRemainder, Integer totalPages, int i) {
- return pagingRemainder != 0 && i + 1 == totalPages;
- }
-
- private Integer getPaginationRemainder(List filesWithAbsolutePath) {
- Integer remainder = filesWithAbsolutePath.size() % PAGE_SIZE;
-
- LOGGER.debug("Paging remainder: {}", remainder);
-
- return remainder;
- }
-
- private Integer getPaginationTotalPages(List filesWithAbsolutePath, final Integer PAGE_SIZE,
- Integer remainder) {
- Integer total = filesWithAbsolutePath.size() / PAGE_SIZE;
-
- if (remainder != 0) {
- total++;
- }
- LOGGER.debug("Paging total items: {}", total);
- return total;
- }
+ Integer pagingEnd = getPagingEnd(pagingStart, pagingRemainder, totalPages, i);
- private List getFilesWithAbsolutePath(SensorContext sensorContext) {
- List filesWithAbsolutePath = new ArrayList<>();
+ paginated.add(getFilesPathsSplitBySpace(filesWithAbsolutePath, pagingStart, pagingEnd));
- FileSystem fileSystem = getFileSystem(sensorContext);
+ pagingStart += PAGE_SIZE;
+ }
+ return paginated;
+ }
- FilePredicate mainFilePredicate = getFilesFilter(fileSystem);
-
- String absolutePath = fileSystem.baseDir().getAbsolutePath();
-
- LOGGER.debug("Files absolute path: {}", absolutePath);
-
- fileSystem.inputFiles(mainFilePredicate).forEach(s -> {
- LOGGER.debug("Input file path: {}", s.toString());
-
- String fullPath = new StringBuilder(absolutePath).append(File.separator).append(s.toString().replace("/", File.separator))
- .toString();
-
- LOGGER.debug("Current file full path: {}", fullPath);
-
- filesWithAbsolutePath.add(fullPath);
- });
-
- return filesWithAbsolutePath;
- }
-
- private FilePredicate getFilesFilter(FileSystem fileSystem) {
- FilePredicate mainFilePredicate = fileSystem.predicates().and(
- fileSystem.predicates().hasType(InputFile.Type.MAIN), fileSystem.predicates().hasLanguage(Dart.KEY));
- return mainFilePredicate;
- }
-
- private FileSystem getFileSystem(SensorContext sensorContext) {
- FileSystem fileSystem = sensorContext.fileSystem();
- return fileSystem;
- }
-
- private void recordIssues(SensorContext sensorContext, List issues) {
- // Record issues
- issues.forEach(i -> {
- File file = new File(sensorContext.fileSystem().baseDir(), i.getFilePath());
- LOGGER.debug("Inside issue forEach, file absolute path: {}", file.getAbsolutePath());
-
- FilePredicate fp = sensorContext.fileSystem().predicates().hasAbsolutePath(file.getAbsolutePath());
- if (!sensorContext.fileSystem().hasFiles(fp)) {
- LOGGER.warn("File not included in SonarQube {}", file.getAbsoluteFile());
- } else {
- InputFile inputFile = sensorContext.fileSystem().inputFile(fp);
- NewIssueLocation nil = new DefaultIssueLocation().on(inputFile)
- .at(inputFile.selectLine(i.getLineNumber())).message(i.getMessage());
- sensorContext.newIssue().forRule(RuleKey.of(DartAnalyzerRulesDefinition.REPOSITORY_KEY, i.getRuleId()))
- .at(nil).save();
- }
- });
- }
-
- private void verifyIfDartAnalyzerExists() {
- LOGGER.debug("Verify dart analyser...");
- new ProcBuilder(ANALYZER_COMMAND).withArg("-h").run();
- LOGGER.debug("Verify dart analyser done");
- }
-
- private boolean existsAnalysisOptionsFile(SensorContext sensorContext) {
- return new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME).exists();
- }
-
- private void saveCurrentAnalysisOptionsFile(SensorContext sensorContext) {
- File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
- if (analysisOptionsFile.exists()) {
- analysisOptionsFile
- .renameTo(new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME + ".sonar"));
- LOGGER.info("Backup of original analysis_options.yaml file to {}", ANALYSIS_OPTIONS_FILENAME + ".sonar");
- }
- }
-
- private void createAnalysisOptionsFile(SensorContext sensorContext) throws IOException {
- File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
- URL inputUrl = getClass().getResource(ANALYSIS_OPTIONS_FILE);
- Resources.asByteSource(inputUrl).copyTo(Files.asByteSink(analysisOptionsFile));
- }
-
- private void restoreCurrentAnalysisOptionsFile(SensorContext sensorContext) {
- File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
- File currentAnalysisOptionsFile = new File(sensorContext.fileSystem().baseDir(),
- ANALYSIS_OPTIONS_FILENAME + ".sonar");
- if (currentAnalysisOptionsFile.exists()) {
- currentAnalysisOptionsFile.renameTo(analysisOptionsFile);
- LOGGER.info("Restored original analysis_options.yaml file");
- } else {
- analysisOptionsFile.delete();
- }
- }
+ private Integer getPagingEnd(Integer pagingStart, Integer pagingRemainder, Integer totalPages, int i) {
+ Integer pagingEnd = pagingStart + PAGE_SIZE;
+ if (isLastPage(pagingRemainder, totalPages, i)) {
+ pagingEnd = pagingStart + pagingRemainder;
+ }
+ LOGGER.debug("Current paging end: {}", pagingEnd);
+ return pagingEnd;
+ }
+
+ private String getFilesPathsSplitBySpace(List filesWithAbsolutePath, Integer pagingStart,
+ Integer pagingEnd) {
+ return filesWithAbsolutePath.subList(pagingStart, pagingEnd).stream().collect(Collectors.joining(" "));
+ }
+
+ private boolean isLastPage(Integer pagingRemainder, Integer totalPages, int i) {
+ return pagingRemainder != 0 && i + 1 == totalPages;
+ }
+
+ private Integer getPaginationRemainder(List filesWithAbsolutePath) {
+ Integer remainder = filesWithAbsolutePath.size() % PAGE_SIZE;
+
+ LOGGER.debug("Paging remainder: {}", remainder);
+
+ return remainder;
+ }
+
+ private Integer getPaginationTotalPages(List filesWithAbsolutePath, final Integer PAGE_SIZE,
+ Integer remainder) {
+ Integer total = filesWithAbsolutePath.size() / PAGE_SIZE;
+
+ if (remainder != 0) {
+ total++;
+ }
+ LOGGER.debug("Paging total items: {}", total);
+ return total;
+ }
+
+ private List getFilesWithAbsolutePath(SensorContext sensorContext) {
+ List filesWithAbsolutePath = new ArrayList<>();
+
+ FileSystem fileSystem = getFileSystem(sensorContext);
+
+ FilePredicate mainFilePredicate = getFilesFilter(fileSystem);
+
+ String absolutePath = fileSystem.baseDir().getAbsolutePath();
+
+ LOGGER.debug("Files absolute path: {}", absolutePath);
+
+ fileSystem.inputFiles(mainFilePredicate).forEach(s -> {
+ LOGGER.debug("Input file path: {}", s.toString());
+
+ String fullPath = new StringBuilder(absolutePath).append(File.separator).append(s.toString().replace("/", File.separator))
+ .toString();
+
+ LOGGER.debug("Current file full path: {}", fullPath);
+
+ filesWithAbsolutePath.add(fullPath);
+ });
+
+ return filesWithAbsolutePath;
+ }
+
+ private FilePredicate getFilesFilter(FileSystem fileSystem) {
+ FilePredicate mainFilePredicate = fileSystem.predicates().and(
+ fileSystem.predicates().hasType(InputFile.Type.MAIN), fileSystem.predicates().hasLanguage(Dart.KEY));
+ return mainFilePredicate;
+ }
+
+ private FileSystem getFileSystem(SensorContext sensorContext) {
+ FileSystem fileSystem = sensorContext.fileSystem();
+ return fileSystem;
+ }
+
+ private void recordIssues(SensorContext sensorContext, List issues) {
+ // Record issues
+ issues.forEach(i -> {
+ File file = new File(sensorContext.fileSystem().baseDir(), i.getFilePath());
+ LOGGER.debug("Inside issue forEach, file absolute path: {}", file.getAbsolutePath());
+
+ FilePredicate fp = sensorContext.fileSystem().predicates().hasAbsolutePath(file.getAbsolutePath());
+ if (!sensorContext.fileSystem().hasFiles(fp)) {
+ LOGGER.warn("File not included in SonarQube {}", file.getAbsoluteFile());
+ } else {
+ InputFile inputFile = sensorContext.fileSystem().inputFile(fp);
+ NewIssueLocation nil = new DefaultIssueLocation().on(inputFile)
+ .at(inputFile.selectLine(i.getLineNumber())).message(i.getMessage());
+ sensorContext.newIssue().forRule(RuleKey.of(DartAnalyzerRulesDefinition.REPOSITORY_KEY, i.getRuleId()))
+ .at(nil).save();
+ }
+ });
+ }
+
+ private void verifyIfDartAnalyzerExists() {
+ LOGGER.debug("Verify dart analyser...");
+ new ProcBuilder(LEGACY_COMMAND).withArg("-h").run();
+ LOGGER.debug("Verify dart analyser done");
+ }
+
+ private boolean existsAnalysisOptionsFile(SensorContext sensorContext) {
+ return new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME).exists();
+ }
+
+ private void saveCurrentAnalysisOptionsFile(SensorContext sensorContext) {
+ File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
+ if (analysisOptionsFile.exists()) {
+ analysisOptionsFile
+ .renameTo(new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME + ".sonar"));
+ LOGGER.info("Backup of original analysis_options.yaml file to {}", ANALYSIS_OPTIONS_FILENAME + ".sonar");
+ }
+ }
+
+ private void createAnalysisOptionsFile(SensorContext sensorContext) throws IOException {
+ File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
+ URL inputUrl = DartAnalyzerSensor.class.getResource(ANALYSIS_OPTIONS_FILE);
+ Resources.asByteSource(inputUrl).copyTo(Files.asByteSink(analysisOptionsFile));
+ }
+
+ private void restoreCurrentAnalysisOptionsFile(SensorContext sensorContext) {
+ File analysisOptionsFile = new File(sensorContext.fileSystem().baseDir(), ANALYSIS_OPTIONS_FILENAME);
+ File currentAnalysisOptionsFile = new File(sensorContext.fileSystem().baseDir(),
+ ANALYSIS_OPTIONS_FILENAME + ".sonar");
+ if (currentAnalysisOptionsFile.exists()) {
+ currentAnalysisOptionsFile.renameTo(analysisOptionsFile);
+ LOGGER.info("Restored original analysis_options.yaml file");
+ } else {
+ analysisOptionsFile.delete();
+ }
+ }
}
diff --git a/dart-lang/src/main/resources/dart.keywords b/dart-lang/src/main/resources/dart.keywords
index 2a608b1..d4cadf9 100644
--- a/dart-lang/src/main/resources/dart.keywords
+++ b/dart-lang/src/main/resources/dart.keywords
@@ -2,6 +2,7 @@ abstract
as
assert
async
+async*
await
break
case
@@ -14,8 +15,9 @@ do$dynamic
else
enum
export
-external
extends
+extension
+external
factory
false
final
@@ -28,10 +30,13 @@ import
in
is
library
+mixin
new
null
+on
operator
part
+required
rethrow
return
set
@@ -39,6 +44,7 @@ static
super
switch
sync
+sync*
this
throw
true
@@ -48,4 +54,4 @@ void
while
with
yield
-
+yield*
diff --git a/dart-lang/src/main/resources/fr/insideapp/sonarqube/dart/dartanalyzer/profile-pedantic.1.9.0.xml b/dart-lang/src/main/resources/fr/insideapp/sonarqube/dart/dartanalyzer/profile-pedantic.1.9.0.xml
new file mode 100644
index 0000000..c410349
--- /dev/null
+++ b/dart-lang/src/main/resources/fr/insideapp/sonarqube/dart/dartanalyzer/profile-pedantic.1.9.0.xml
@@ -0,0 +1,341 @@
+
+
+ pedantic 1.9.0
+ dart
+
+
+ dartanalyzer
+ always_declare_return_types
+
+
+ dartanalyzer
+ always_require_non_null_named_parameters
+
+
+ dartanalyzer
+ annotate_overrides
+
+
+ dartanalyzer
+ avoid_empty_else
+
+
+ dartanalyzer
+ avoid_init_to_null
+
+
+ dartanalyzer
+ avoid_null_checks_in_equality_operators
+
+
+ dartanalyzer
+ avoid_relative_lib_imports
+
+
+ dartanalyzer
+ avoid_return_types_on_setters
+
+
+ dartanalyzer
+ avoid_shadowing_type_parameters
+
+
+ dartanalyzer
+ avoid_types_as_parameter_names
+
+
+ dartanalyzer
+ camel_case_extensions
+
+
+ dartanalyzer
+ curly_braces_in_flow_control_structures
+
+
+ dartanalyzer
+ empty_catches
+
+
+ dartanalyzer
+ empty_constructor_bodies
+
+
+ dartanalyzer
+ library_names
+
+
+ dartanalyzer
+ library_prefixes
+
+
+ dartanalyzer
+ no_duplicate_case_values
+
+
+ dartanalyzer
+ null_closures
+
+
+ dartanalyzer
+ omit_local_variable_types
+
+
+ dartanalyzer
+ prefer_adjacent_string_concatenation
+
+
+ dartanalyzer
+ prefer_collection_literals
+
+
+ dartanalyzer
+ prefer_conditional_assignment
+
+
+ dartanalyzer
+ prefer_contains
+
+
+ dartanalyzer
+ prefer_equal_for_default_values
+
+
+ dartanalyzer
+ prefer_final_fields
+
+
+ dartanalyzer
+ prefer_for_elements_to_map_fromIterable
+
+
+ dartanalyzer
+ prefer_generic_function_type_aliases
+
+
+ dartanalyzer
+ prefer_if_null_operators
+
+
+ dartanalyzer
+ prefer_is_empty
+
+
+ dartanalyzer
+ prefer_is_not_empty
+
+
+ dartanalyzer
+ prefer_iterable_whereType
+
+
+ dartanalyzer
+ prefer_single_quotes
+
+
+ dartanalyzer
+ prefer_spread_collections
+
+
+ dartanalyzer
+ recursive_getters
+
+
+ dartanalyzer
+ slash_for_doc_comments
+
+
+ dartanalyzer
+ type_init_formals
+
+
+ dartanalyzer
+ unawaited_futures
+
+
+ dartanalyzer
+ unnecessary_const
+
+
+ dartanalyzer
+ unnecessary_new
+
+
+ dartanalyzer
+ unnecessary_null_in_if_null_operators
+
+
+ dartanalyzer
+ unnecessary_this
+
+
+ dartanalyzer
+ unrelated_type_equality_checks
+
+
+ dartanalyzer
+ use_function_type_syntax_for_parameters
+
+
+ dartanalyzer
+ use_rethrow_when_possible
+
+
+ dartanalyzer
+ valid_regexps
+
+
+
+ dartanalyzer
+ avoid_returning_null_for_future
+
+
+ dartanalyzer
+ avoid_web_libraries_in_flutter (experimental)
+
+
+ dartanalyzer
+ control_flow_in_finally
+
+
+ dartanalyzer
+ hash_and_equals
+
+
+ dartanalyzer
+ invariant_booleans (experimental)
+
+
+ dartanalyzer
+ iterable_contains_unrelated_type
+
+
+ dartanalyzer
+ list_remove_unrelated_type
+
+
+ dartanalyzer
+ literal_only_boolean_expressions
+
+
+ dartanalyzer
+ no_adjacent_strings_in_list
+
+
+ dartanalyzer
+ no_duplicate_case_values
+
+
+ dartanalyzer
+ test_types_in_equals
+
+
+ dartanalyzer
+ throw_in_finally
+
+
+ dartanalyzer
+ unrelated_type_equality_checks
+
+
+ dartanalyzer
+ use_key_in_widget_constructors
+
+
+ dartanalyzer
+ valid_regexps
+
+
+ dartanalyzer
+ always_require_non_null_named_parameters
+
+
+ dartanalyzer
+ avoid_equals_and_hash_code_on_mutable_classes
+
+
+ dartanalyzer
+ avoid_implementing_value_types
+
+
+ dartanalyzer
+ avoid_js_rounded_ints
+
+
+ dartanalyzer
+ avoid_returning_null
+
+
+ dartanalyzer
+ avoid_returning_null_for_void
+
+
+ dartanalyzer
+ avoid_returning_this
+
+
+ dartanalyzer
+ avoid_void_async
+
+
+ dartanalyzer
+ await_only_futures
+
+
+ dartanalyzer
+ parameter_assignments
+
+
+ dartanalyzer
+ prefer_const_constructors
+
+
+ dartanalyzer
+ prefer_const_constructors_in_immutables
+
+
+ dartanalyzer
+ prefer_const_declarations
+
+
+ dartanalyzer
+ prefer_final_fields
+
+
+ dartanalyzer
+ prefer_final_in_for_each
+
+
+ dartanalyzer
+ prefer_final_locals
+
+
+ dartanalyzer
+ prefer_mixin
+
+
+ dartanalyzer
+ prefer_null_aware_operators
+
+
+ dartanalyzer
+ recursive_getters
+
+
+ dartanalyzer
+ unawaited_futures
+
+
+ dartanalyzer
+ use_full_hex_values_for_flutter_colors
+
+
+ dartanalyzer
+ void_checks
+
+
+
+ dartanalyzer
+ unsafe_html
+
+
+
\ No newline at end of file
diff --git a/dart-lang/src/test/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParserTest.java b/dart-lang/src/test/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParserTest.java
index 9fdf87b..4c8eb09 100644
--- a/dart-lang/src/test/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParserTest.java
+++ b/dart-lang/src/test/java/fr/insideapp/sonarqube/dart/lang/issues/dartanalyzer/DartAnalyzerReportParserTest.java
@@ -19,11 +19,11 @@
*/
package fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer;
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
import java.util.List;
-import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
public class DartAnalyzerReportParserTest {
@@ -89,7 +89,24 @@ public void parseWithTraces() {
assertRuleId(issues.get(10), RULE_ID_UNUSED_LOCAL_VARIABLE);
assertMessage(issues.get(10), "The value of the local variable 'j' isn't used.");
}
-
+
+ @Test
+ public void parseFlutterReport() {
+ String input = " info • The value of the local variable 'chocolate' isn't used • lib/api/icecream/icecream_api.dart:110:29 • unused_local_variable\n" +
+ " info • 'nothing' is deprecated and shouldn't be used. Do not use this anymore, please use [Nothing] instead. • lib/src/path/to/very_cool_widget.dart:32:59 • deprecated_member_use_from_same_package\n" +
+ " info • The parameter 'foo' is required • lib/src/path/to/this_file.dart:9:5 • missing_required_param\n" +
+ " info • Unnecessary await keyword in return • lib/path/to/other/file.dart:37:27 • unnecessary_await_in_return";
+
+ List issues = parser.parse(input);
+ assertThat(issues).hasSize(4);
+ assertThat(issues.stream().map(DartAnalyzerReportIssue::getRuleId)).containsExactly(
+ "unused_local_variable",
+ "deprecated_member_use_from_same_package",
+ "missing_required_param",
+ "unnecessary_await_in_return"
+ );
+ }
+
private void assertFilePath(DartAnalyzerReportIssue issue, String expectedPath) {
assertThat(issue.getFilePath()).isEqualTo(expectedPath);
}
diff --git a/pom.xml b/pom.xml
index e7c3558..1899b0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
fr.insideapp.sonarqube
sonar-flutter
- 0.3.1
+ 0.3.2
pom
diff --git a/sonar-flutter-plugin/pom.xml b/sonar-flutter-plugin/pom.xml
index 431b867..36a912a 100644
--- a/sonar-flutter-plugin/pom.xml
+++ b/sonar-flutter-plugin/pom.xml
@@ -4,7 +4,7 @@
sonar-flutter
fr.insideapp.sonarqube
- 0.3.1
+ 0.3.2
4.0.0
@@ -17,7 +17,7 @@
fr.insideapp.sonarqube
dart-lang
- 0.3.1
+ 0.3.2
diff --git a/sonar-flutter-plugin/src/main/java/fr/insideapp/sonarqube/flutter/FlutterPlugin.java b/sonar-flutter-plugin/src/main/java/fr/insideapp/sonarqube/flutter/FlutterPlugin.java
index 36801cd..e9bf9e5 100644
--- a/sonar-flutter-plugin/src/main/java/fr/insideapp/sonarqube/flutter/FlutterPlugin.java
+++ b/sonar-flutter-plugin/src/main/java/fr/insideapp/sonarqube/flutter/FlutterPlugin.java
@@ -19,23 +19,27 @@
*/
package fr.insideapp.sonarqube.flutter;
+import fr.insideapp.sonarqube.dart.lang.Dart;
import fr.insideapp.sonarqube.dart.lang.DartSensor;
import fr.insideapp.sonarqube.dart.lang.issues.DartProfile;
+import fr.insideapp.sonarqube.dart.lang.issues.DartProfilePedantic190;
+import fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer.AnalyzerMode;
import fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer.DartAnalyzerRulesDefinition;
import fr.insideapp.sonarqube.dart.lang.issues.dartanalyzer.DartAnalyzerSensor;
import fr.insideapp.sonarqube.flutter.coverage.FlutterCoverageSensor;
import fr.insideapp.sonarqube.flutter.tests.FlutterTestSensor;
import org.sonar.api.Plugin;
-import fr.insideapp.sonarqube.dart.lang.Dart;
+import org.sonar.api.PropertyType;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
+import java.util.stream.Collectors;
+
public class FlutterPlugin implements Plugin {
public static final String DART_CATEGORY = "Dart";
public static final String FLUTTER_CATEGORY = "Flutter";
public static final String ANALYSIS_SUBCATEGORY = "Analysis";
- public static final String GENERAL_SUBCATEGORY = "General";
public static final String TESTS_SUBCATEGORY = "Tests";
public static final String FLUTTER_TESTS_REPORT_PATH_KEY = "sonar.flutter.tests.reportPath";
@@ -44,7 +48,7 @@ public class FlutterPlugin implements Plugin {
public void define(Context context) {
// Language support
- context.addExtensions(Dart.class, DartSensor.class, DartProfile.class);
+ context.addExtensions(Dart.class, DartSensor.class, DartProfile.class, DartProfilePedantic190.class);
// dartanalyzer Sensor
context.addExtensions(DartAnalyzerSensor.class, DartAnalyzerRulesDefinition.class);
@@ -77,6 +81,27 @@ public void define(Context context) {
.defaultValue("false")
.build());
+ context.addExtension(
+ PropertyDefinition.builder(DartAnalyzerSensor.FLUTTER_ANALYZER_MODE)
+ .name("Analyzer")
+ .description("Which analyzer to use")
+ .onQualifiers(Qualifiers.MODULE, Qualifiers.PROJECT)
+ .category(DART_CATEGORY)
+ .subCategory(ANALYSIS_SUBCATEGORY)
+ .options(DartAnalyzerSensor.FLUTTER_ANALYZER_MODE_OPTIONS.stream().map(Enum::name).collect(Collectors.toList()))
+ .defaultValue(AnalyzerMode.defaultMode.name())
+ .type(PropertyType.SINGLE_SELECT_LIST)
+ .build());
+
+ context.addExtension(
+ PropertyDefinition.builder(DartSensor.DART_ANALYSIS_USE_EXISTING_REPORT_PATH_KEY)
+ .name("Use dartanalyzer report file for analysis")
+ .description("Path to Dartanalyzer report file. If null, dartanalyzer will be executed. The path may be either absolute or relative to the project base directory. Run dartanalyzer with '--write PATH' to create the report.")
+ .onQualifiers(Qualifiers.MODULE, Qualifiers.PROJECT)
+ .category(DART_CATEGORY)
+ .subCategory(ANALYSIS_SUBCATEGORY)
+ .build());
+
// Tests
context.addExtension(FlutterTestSensor.class);