diff --git a/README.md b/README.md index b478742a..02acb509 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ It supports: * [_JCReport_](https://github.com/jCoderZ/fawkez/wiki/JcReport) * [_Klocwork_](http://www.klocwork.com/products-services/klocwork/static-code-analysis) * [_MyPy_](https://pypi.python.org/pypi/mypy-lang) + * [_PCLint_](http://www.gimpel.com/html/pcl.htm) PC-Lint using the same output format as the Jenkins warnings plugin, [_details here_](https://wiki.jenkins.io/display/JENKINS/PcLint+options) * [_PerlCritic_](https://github.com/Perl-Critic) * [_PiTest_](http://pitest.org/) * [_PyDocStyle_](https://pypi.python.org/pypi/pydocstyle) diff --git a/src/main/java/se/bjurr/violations/lib/ViolationsApi.java b/src/main/java/se/bjurr/violations/lib/ViolationsApi.java index 544778a7..00db0faf 100644 --- a/src/main/java/se/bjurr/violations/lib/ViolationsApi.java +++ b/src/main/java/se/bjurr/violations/lib/ViolationsApi.java @@ -84,8 +84,12 @@ public List violations() { return foundViolations; } + private String makeWindowsFriendly(String regularExpression) { + return regularExpression.replace("/", "(?:/|\\\\)"); + } + public ViolationsApi withPattern(String regularExpression) { - pattern = regularExpression; + pattern = makeWindowsFriendly(regularExpression); return this; } } diff --git a/src/main/java/se/bjurr/violations/lib/parsers/PCLintParser.java b/src/main/java/se/bjurr/violations/lib/parsers/PCLintParser.java new file mode 100644 index 00000000..794e2958 --- /dev/null +++ b/src/main/java/se/bjurr/violations/lib/parsers/PCLintParser.java @@ -0,0 +1,116 @@ +package se.bjurr.violations.lib.parsers; + +import static java.lang.Integer.parseInt; +import static se.bjurr.violations.lib.model.SEVERITY.ERROR; +import static se.bjurr.violations.lib.model.SEVERITY.INFO; +import static se.bjurr.violations.lib.model.SEVERITY.WARN; +import static se.bjurr.violations.lib.model.Violation.violationBuilder; +import static se.bjurr.violations.lib.reports.Parser.PCLINT; +import static se.bjurr.violations.lib.util.ViolationParserUtils.getLines; +import static se.bjurr.violations.lib.util.ViolationParserUtils.getParts; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.*; +import se.bjurr.violations.lib.model.SEVERITY; +import se.bjurr.violations.lib.model.Violation; + +public class PCLintParser implements ViolationsParser { + + @Override + public List parseReportOutput(String string) throws Exception { + List violations = new ArrayList<>(); + List lines = getLines(string); + Pattern misraPattern = Pattern.compile("\\[MISRA.*\\]"); + for (String line : lines) { + Matcher misraMatcher = misraPattern.matcher(line); + if (misraMatcher.find()) { + parseMisraViolation(line, violations); + } else { + parseGeneralViolation(line, violations); + } + } + return violations; + } + + private void parseMisraViolation(String line, List violations) { + List parts = + getParts( + line, + "^([^\\(]+)\\(", + "^([\\d]+)\\): ", + "^(?:Error|Warning|Info|Note) [\\d]+: ([^\\[]*)", + "^\\[(.*),", + "(mandatory|required|advisory)\\]", + "^(.*)$"); + if (parts.isEmpty()) { + return; + } + String filename = parts.get(0); + Integer lineNumber = parseInt(parts.get(1)); + + String severityString = parts.get(4); + SEVERITY severity = toMisraSeverity(severityString); + String rule = parts.get(3) + ", " + severityString; + String message = parts.get(2) + " " + parts.get(5); + violations.add( // + violationBuilder() // + .setParser(PCLINT) // + .setStartLine(lineNumber) // + .setFile(filename) // + .setRule(rule) // + .setSeverity(severity) // + .setMessage(message) // + .build() // + ); + } + + private void parseGeneralViolation(String line, List violations) { + List parts = + getParts( + line, + "^([^\\(]+)\\(", + "^([\\d]+)\\): ", + "^(Error|Warning|Info|Note) ", + "^([\\d]+): ", + "^(.*)$"); + if (parts.isEmpty()) { + return; + } + String filename = parts.get(0); + Integer lineNumber = parseInt(parts.get(1)); + SEVERITY severity = toSeverity(parts.get(2)); + String rule = parts.get(3); + String message = parts.get(4); + violations.add( // + violationBuilder() // + .setParser(PCLINT) // + .setStartLine(lineNumber) // + .setFile(filename) // + .setRule(rule) // + .setSeverity(severity) // + .setMessage(message) // + .build() // + ); + } + + private SEVERITY toSeverity(String severity) { + if (severity.equals("Error")) { + return ERROR; + } + if (severity.equals("Warning")) { + return WARN; + } + return INFO; + } + + private SEVERITY toMisraSeverity(String severity) { + if (severity.equals("mandatory")) { + return ERROR; + } + if (severity.equals("required")) { + return WARN; + } + return INFO; + } +} diff --git a/src/main/java/se/bjurr/violations/lib/parsers/PerlCriticParser.java b/src/main/java/se/bjurr/violations/lib/parsers/PerlCriticParser.java index 88792811..11799a72 100644 --- a/src/main/java/se/bjurr/violations/lib/parsers/PerlCriticParser.java +++ b/src/main/java/se/bjurr/violations/lib/parsers/PerlCriticParser.java @@ -5,7 +5,7 @@ import static se.bjurr.violations.lib.model.SEVERITY.INFO; import static se.bjurr.violations.lib.model.SEVERITY.WARN; import static se.bjurr.violations.lib.model.Violation.violationBuilder; -import static se.bjurr.violations.lib.reports.Parser.CPPLINT; +import static se.bjurr.violations.lib.reports.Parser.PERLCRITIC; import static se.bjurr.violations.lib.util.ViolationParserUtils.getLines; import static se.bjurr.violations.lib.util.ViolationParserUtils.getParts; @@ -41,7 +41,7 @@ public List parseReportOutput(String string) throws Exception { violations.add( // violationBuilder() // - .setParser(CPPLINT) // + .setParser(PERLCRITIC) // .setStartLine(lineNumber) // .setColumn(columnNumber) // .setFile(filename) // diff --git a/src/main/java/se/bjurr/violations/lib/reports/Parser.java b/src/main/java/se/bjurr/violations/lib/reports/Parser.java index 6d638e17..aeb921fe 100644 --- a/src/main/java/se/bjurr/violations/lib/reports/Parser.java +++ b/src/main/java/se/bjurr/violations/lib/reports/Parser.java @@ -28,6 +28,7 @@ import se.bjurr.violations.lib.parsers.KlocworkParser; import se.bjurr.violations.lib.parsers.LintParser; import se.bjurr.violations.lib.parsers.MyPyParser; +import se.bjurr.violations.lib.parsers.PCLintParser; import se.bjurr.violations.lib.parsers.PMDParser; import se.bjurr.violations.lib.parsers.PerlCriticParser; import se.bjurr.violations.lib.parsers.PiTestParser; @@ -73,7 +74,8 @@ public enum Parser { STYLECOP(new StyleCopParser()), // XMLLINT(new XMLLintParser()), // ZPTLINT(new ZPTLintParser()), // - DOCFX(new DocFXParser()); + DOCFX(new DocFXParser()), // + PCLINT(new PCLintParser()); private static Logger LOG = Logger.getLogger(Parser.class.getSimpleName()); private transient ViolationsParser violationsParser; diff --git a/src/test/java/se/bjurr/violations/lib/PCLintTest.java b/src/test/java/se/bjurr/violations/lib/PCLintTest.java new file mode 100644 index 00000000..ce8319a6 --- /dev/null +++ b/src/test/java/se/bjurr/violations/lib/PCLintTest.java @@ -0,0 +1,109 @@ +package se.bjurr.violations.lib; + +import static org.assertj.core.api.Assertions.assertThat; +import static se.bjurr.violations.lib.TestUtils.getRootFolder; +import static se.bjurr.violations.lib.ViolationsApi.violationsApi; +import static se.bjurr.violations.lib.model.SEVERITY.ERROR; +import static se.bjurr.violations.lib.model.SEVERITY.INFO; +import static se.bjurr.violations.lib.model.SEVERITY.WARN; +import static se.bjurr.violations.lib.model.Violation.violationBuilder; +import static se.bjurr.violations.lib.reports.Parser.PCLINT; + +import java.util.List; +import org.junit.Test; +import se.bjurr.violations.lib.model.Violation; + +public class PCLintTest { + + @Test + public void testThatViolationsCanBeParsed() { + String rootFolder = getRootFolder(); + + List actual = + violationsApi() // + .withPattern(".*/pclint/.*\\.txt$") // + .inFolder(rootFolder) // + .findAll(PCLINT) // + .violations(); + + assertThat(actual) // + .hasSize(8); + + assertThat(actual.get(0)) // + .isEqualTo( // + violationBuilder() // + .setParser(PCLINT) // + .setFile("C:\\UST3\\qse30\\Drivers\\drvADC.c") // + .setStartLine(84) // + .setRule("9029") // + .setMessage("Mismatched essential type categories for binary operator") // + .setSeverity(INFO) // + .build() // + ); + + assertThat(actual.get(3)) // + .isEqualTo( // + violationBuilder() // + .setParser(PCLINT) // + .setFile("C:\\UST3\\qse30\\Drivers\\drvCAN.c") // + .setStartLine(73) // + .setRule("534") // + .setMessage( + "Ignoring return value of function 'PIC_CAN_Transmit(can_frame_t *)' (compare with line 68, file C:\\UST3\\qse30\\HAL\\hal_ext.h, module C:\\UST3\\qse30\\Drivers\\drvADC.c)") // + .setSeverity(WARN) // + .build() // + ); + assertThat(actual.get(5)) // + .isEqualTo( // + violationBuilder() // + .setParser(PCLINT) // + .setFile("C:\\UST3\\qse30\\Drivers\\drvCAN.c") // + .setStartLine(134) // + .setRule("818") // + .setMessage( + "Pointer parameter 'txFrame' (line 100) could be declared as pointing to const") // + .setSeverity(INFO) // + .build() // + ); + assertThat(actual.get(6)) // + .isEqualTo( // + violationBuilder() // + .setParser(PCLINT) // + .setFile("C:\\UST3\\qse30\\Modules\\COMM\\J1939\\Broadcast\\dm13.c") // + .setStartLine(123) // + .setRule("48") // + .setMessage("Bad type") // + .setSeverity(ERROR) // + .build() // + ); + } + + @Test + public void testThatSeverityAndRulenumberFromMisraTakesPrecedence() { + String rootFolder = getRootFolder(); + + List actual = + violationsApi() // + .withPattern(".*/pclint/.*\\.txt$") // + .inFolder(rootFolder) // + .findAll(PCLINT) // + .violations(); + + Violation violation = actual.get(1); + + assertThat(violation.getRule().get()).isEqualTo("MISRA 2012 Rule 10.4, mandatory"); + assertThat(violation.getSeverity()).isEqualTo(ERROR); + + violation = actual.get(2); + + assertThat(violation.getRule().get()).isEqualTo("MISRA 2012 Rule 1.3, required"); + assertThat(violation.getSeverity()).isEqualTo(WARN); + + violation = actual.get(7); + + assertThat(violation.getRule().get()).isEqualTo("MISRA 2012 Rule 10.1, advisory"); + assertThat(violation.getSeverity()).isEqualTo(INFO); + assertThat(violation.getMessage()) + .isEqualTo("Bad type (Error 48)"); + } +} diff --git a/src/test/resources/pclint/pclint.txt b/src/test/resources/pclint/pclint.txt new file mode 100644 index 00000000..6ae722d0 --- /dev/null +++ b/src/test/resources/pclint/pclint.txt @@ -0,0 +1,26 @@ +PC-lint for C/C++ (NT) Vers. 9.00L, Copyright Gimpel Software 1985-2014 + +--- Module: C:\UST3\qse30\Drivers\drvADC.c (C) +C:\UST3\qse30\Drivers\drvADC.c(84): Note 9029: Mismatched essential type categories for binary operator + +--- Module: C:\UST3\qse30\Drivers\drvCAN.c (C) +C:\UST3\qse30\Drivers\drvCAN.c(68): Note 9029: Mismatched essential type categories for binary operator [MISRA 2012 Rule 10.4, mandatory] (Note 9029) +C:\UST3\qse30\Drivers\drvCAN.c(72): Note 931: Both sides have side effects [MISRA 2012 Rule 1.3, required] +C:\UST3\qse30\Drivers\drvCAN.c(73): Warning 534: Ignoring return value of function 'PIC_CAN_Transmit(can_frame_t *)' (compare with line 68, file C:\UST3\qse30\HAL\hal_ext.h, module C:\UST3\qse30\Drivers\drvADC.c) +C:\UST3\qse30\Drivers\drvCAN.c(128): Warning 534: Ignoring return value of function 'PIC_CAN_Transmit(can_frame_t *)' (compare with line 68, file C:\UST3\qse30\HAL\hal_ext.h, module C:\UST3\qse30\Drivers\drvADC.c) +C:\UST3\qse30\Drivers\drvCAN.c(134): Info 818: Pointer parameter 'txFrame' (line 100) could be declared as pointing to const + + +--- Module: C:\UST3\qse30\Drivers\drvNTC.c (C) + +--- Module: C:\UST3\qse30\Drivers\drvPWM.c (C) +(0): Info 766: Header file 'C:\UST3\qse30\Kernel\Common\common_types.h' not used in module 'C:\UST3\qse30\Drivers\drvPWM.c' (Info 766) + + +--- Module: C:\UST3\qse30\Modules\COMM\J1939\Broadcast\dm13.c (C) +C:\UST3\qse30\Modules\COMM\J1939\Broadcast\dm13.c(123): Error 48: Bad type +C:\UST3\qse30\Modules\COMM\J1939\Broadcast\dm13.c(123): Error 48: Bad type [MISRA 2012 Rule 10.1, advisory] (Error 48) + + + +