diff --git a/SUPPORTED-FORMATS.md b/SUPPORTED-FORMATS.md index 7e99e7cfb..e72c9d415 100644 --- a/SUPPORTED-FORMATS.md +++ b/SUPPORTED-FORMATS.md @@ -2178,6 +2178,27 @@ analyze - iccxxxxcompiler_opts cstat2.cFor details check the IAR C- - + + + vale + + + - + + + + Vale + + + + **/vale-report.json + + + + + :bulb: Use option --output=JSON + + valgrind diff --git a/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java b/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java new file mode 100644 index 000000000..f75c5a07a --- /dev/null +++ b/src/main/java/edu/hm/hafner/analysis/parser/ValeParser.java @@ -0,0 +1,75 @@ +package edu.hm.hafner.analysis.parser; + +import java.io.Serial; + +import org.json.JSONArray; +import org.json.JSONObject; + +import edu.hm.hafner.analysis.Issue; +import edu.hm.hafner.analysis.IssueBuilder; +import edu.hm.hafner.analysis.Report; +import edu.hm.hafner.analysis.Severity; + +/** + * Parser for vale reports. + */ +public class ValeParser extends JsonIssueParser { + // Constants for JSON keys + private static final String CHECK = "Check"; + private static final String LINE_KEY = "Line"; + private static final String LINK_KEY = "Link"; + private static final String MESSAGE_KEY = "Message"; + private static final String SPAN_KEY = "Span"; + private static final String SEVERITY_KEY = "Severity"; + + @Serial + private static final long serialVersionUID = -4034450901865555017L; + + @Override + protected void parseJsonObject(final Report report, final JSONObject jsonReport, final IssueBuilder issueBuilder) { + JSONArray fileNames = jsonReport.names(); + for (Object o : fileNames) { + if (o instanceof String f) { + JSONArray jsonArray = jsonReport.getJSONArray(f); + for (Object data : jsonArray) { + if (data instanceof JSONObject dataObject) { + report.add(createIssue(issueBuilder, f, dataObject)); + } + } + } + } + } + + private Issue createIssue(final IssueBuilder issueBuilder, final String fileName, final JSONObject data) { + String checker = data.getString(CHECK); + String message = data.getString(MESSAGE_KEY); + String severity = data.getString(SEVERITY_KEY); + String link = data.getString(LINK_KEY); + int line = data.getInt(LINE_KEY); + JSONArray span = data.getJSONArray(SPAN_KEY); + int startColumn = span.getInt(0); + int endColumn = span.getInt(1); + final Severity analysisSeverity; + switch (severity) { + case "error": + analysisSeverity = Severity.ERROR; + break; + case "warning": + analysisSeverity = Severity.WARNING_NORMAL; + break; + case "suggestion": + analysisSeverity = Severity.WARNING_LOW; + break; + default: + analysisSeverity = Severity.WARNING_NORMAL; + break; + } + return issueBuilder.setFileName(fileName).setDescription(checker) + .setMessage(message).setSeverity(analysisSeverity) + .setReference(link) + .setLineStart(line) + .setLineEnd(line) + .setColumnStart(startColumn).setColumnEnd(endColumn) + .buildAndClean(); + } +} diff --git a/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java b/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java index 57bdea582..d09d9854b 100644 --- a/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java +++ b/src/main/java/edu/hm/hafner/analysis/registry/ParserRegistry.java @@ -167,6 +167,7 @@ public class ParserRegistry { new TnsdlDescriptor(), new TrivyDescriptor(), new TsLintDescriptor(), + new ValeDescriptor(), new ValgrindDescriptor(), new VeraCodePipelineScannerDescriptor(), new XlcDescriptor(), diff --git a/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java b/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java new file mode 100644 index 000000000..2b120458c --- /dev/null +++ b/src/main/java/edu/hm/hafner/analysis/registry/ValeDescriptor.java @@ -0,0 +1,36 @@ +package edu.hm.hafner.analysis.registry; + +import edu.hm.hafner.analysis.IssueParser; +import edu.hm.hafner.analysis.parser.ValeParser; + +/** + * Descriptor for the vale prose linter. + */ +public class ValeDescriptor extends ParserDescriptor { + private static final String ID = "vale"; + private static final String NAME = "Vale"; + + ValeDescriptor() { + super(ID, NAME); + } + + @Override + public IssueParser createParser(Option... options) { + return new ValeParser(); + } + + @Override + public String getPattern() { + return "**/vale-report.json"; + } + + @Override + public String getUrl() { + return "https://vale.sh/"; + } + + @Override + public String getHelp() { + return "Reads vale report files. Use the flag --output=JSON"; + } +} diff --git a/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java b/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java new file mode 100644 index 000000000..b43bbef28 --- /dev/null +++ b/src/test/java/edu/hm/hafner/analysis/parser/ValeParserTest.java @@ -0,0 +1,47 @@ +package edu.hm.hafner.analysis.parser; + +import edu.hm.hafner.analysis.IssueParser; +import edu.hm.hafner.analysis.Report; +import edu.hm.hafner.analysis.Severity; +import edu.hm.hafner.analysis.assertions.SoftAssertions; +import edu.hm.hafner.analysis.registry.AbstractParserTest; + +class ValeParserTest extends AbstractParserTest { + ValeParserTest() { + super("vale-report.json"); + } + + @Override + protected void assertThatIssuesArePresent(final Report report, final SoftAssertions softly) { + softly.assertThat(report).hasSize(3).hasDuplicatesSize(0); + softly.assertThat(report.get(0)) + .hasFileName("file1.adoc") + .hasDescription("RedHat.SentenceLength") + .hasMessage("Try to keep sentences to an average of 32 words or fewer.") + .hasLineStart(10) + .hasColumnStart(1) + .hasColumnEnd(5) + .hasSeverity(Severity.WARNING_LOW); + softly.assertThat(report.get(1)) + .hasFileName("file2.adoc") + .hasDescription("RedHat.TermsWarnings") + .hasMessage("Consider using 'might' or 'can' rather than 'may' unless updating existing content that uses the term.") + .hasLineStart(39) + .hasColumnStart(143) + .hasColumnEnd(145) + .hasSeverity(Severity.WARNING_NORMAL); + softly.assertThat(report.get(2)) + .hasFileName("file2.adoc") + .hasDescription("RedHat.Using") + .hasMessage("Use 'by using' instead of 'using' when it follows a noun for clarity and grammatical correctness.") + .hasLineStart(51) + .hasColumnStart(44) + .hasColumnEnd(64) + .hasSeverity(Severity.ERROR); + } + + @Override + protected IssueParser createParser() { + return new ValeParser(); + } +} diff --git a/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java b/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java index d16108447..24dfcb343 100644 --- a/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java +++ b/src/test/java/edu/hm/hafner/analysis/registry/ParserRegistryTest.java @@ -21,7 +21,7 @@ class ParserRegistryTest extends ResourceTest { // Note for parser developers: if you add a new parser, // please check if you are using the correct type and increment the corresponding count - private static final long WARNING_PARSERS_COUNT = 127L; + private static final long WARNING_PARSERS_COUNT = 128L; private static final long BUG_PARSERS_COUNT = 3L; private static final long VULNERABILITY_PARSERS_COUNT = 7L; private static final long DUPLICATION_PARSERS_COUNT = 3L; diff --git a/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json b/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json new file mode 100644 index 000000000..2d1a71dc3 --- /dev/null +++ b/src/test/resources/edu/hm/hafner/analysis/parser/vale-report.json @@ -0,0 +1,64 @@ +{ + "file1.adoc": [ + { + "Action": { + "Name": "", + "Params": null + }, + "Span": [ + 1, + 5 + ], + "Check": "RedHat.SentenceLength", + "Description": "", + "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/sentencelength/", + "Message": "Try to keep sentences to an average of 32 words or fewer.", + "Severity": "suggestion", + "Match": "While", + "Line": 10 + } + ], + "file2.adoc": [ + { + "Action": { + "Name": "replace", + "Params": [ + "might", + "can" + ] + }, + "Span": [ + 143, + 145 + ], + "Check": "RedHat.TermsWarnings", + "Description": "", + "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/termswarnings/", + "Message": "Consider using 'might' or 'can' rather than 'may' unless updating existing content that uses the term.", + "Severity": "warning", + "Match": "may", + "Line": 39 + }, + { + "Action": { + "Name": "edit", + "Params": [ + "regex", + "(\\w+)( using)", + "$1 by using" + ] + }, + "Span": [ + 44, + 64 + ], + "Check": "RedHat.Using", + "Description": "", + "Link": "https://redhat-documentation.github.io/vale-at-red-hat/docs/main/reference-guide/using/", + "Message": "Use 'by using' instead of 'using' when it follows a noun for clarity and grammatical correctness.", + "Severity": "error", + "Match": "functionalities using", + "Line": 51 + } + ] +} \ No newline at end of file