Skip to content

Commit

Permalink
Merge pull request #153 from HmSebastianH/jenkins56700
Browse files Browse the repository at this point in the history
JENKINS-56700 and JENKINS-56701
  • Loading branch information
uhafner authored Apr 18, 2019
2 parents e646003 + f82f753 commit e9f972e
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 44 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ SonarQubeParser: Use `textRange` when computing affected source code line.

### Changed
- Filters now work on a substring of the property, you don't need to create a regular
expression that matches the whole property value anymore.
expression that matches the whole property value anymore.
- [JENKINS-56700](https://issues.jenkins-ci.org/browse/JENKINS-56700),
[PR#153](https://github.com/jenkinsci/analysis-model/pull/153):
GhsMultiParser: Now uses LookaheadParser instead of depricated super class.
- [JENKINS-56701](https://issues.jenkins-ci.org/browse/JENKINS-56701),
[PR#153](https://github.com/jenkinsci/analysis-model/pull/153):
GnuFortranParser: Now uses LookaheadParser instead of depricated super class.

## [4.0.0](https://github.com/jenkinsci/analysis-model/compare/analysis-model-3.0.0...analysis-model-4.0.0) - 2019-3-20

Expand Down
47 changes: 32 additions & 15 deletions src/main/java/edu/hm/hafner/analysis/parser/GhsMultiParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,64 @@

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.IssueBuilder;
import edu.hm.hafner.analysis.LookaheadParser;
import edu.hm.hafner.analysis.Severity;
import edu.hm.hafner.analysis.RegexpDocumentParser;
import edu.hm.hafner.util.LookaheadStream;

/**
* A parser for the GHS Multi compiler warnings.
*
* @author Joseph Boulos
*/
public class GhsMultiParser extends RegexpDocumentParser {
public class GhsMultiParser extends LookaheadParser {
private static final long serialVersionUID = 8149238560432255036L;

/**
* Regex Pattern to match start of Warning / Error.
* Groups are used to identify FileName, StartLine, Type, Category, Start of message.
*/
private static final String GHS_MULTI_WARNING_PATTERN =
"(?:\\.|[A-Z]:)(.*)\"\\,\\s*line\\s*(\\d+):\\s*(warning|error)\\s*([^:]+):\\s*(?m)([^\\^]*)\\s*\\^";
"(?:\\.|[A-Z]:)(.*)\"\\,\\s*line\\s*(\\d+):\\s*(warning|error)\\s*([^:]+):\\s*(?m)([^\\^]*)";

/** Regex Pattern to match the ending of the Warning / Error Message. */
private static final String MESSAGE_END_REGEX = "\\s*\\^";


/**
* Creates a new instance of {@link GhsMultiParser}.
*/
public GhsMultiParser() {
super(GHS_MULTI_WARNING_PATTERN, true);
super(GHS_MULTI_WARNING_PATTERN);
}

@Override
protected Optional<Issue> createIssue(final Matcher matcher, final IssueBuilder builder) {
protected Optional<Issue> createIssue(final Matcher matcher, final LookaheadStream lookahead, final IssueBuilder builder) {
String type = StringUtils.capitalize(matcher.group(3));
String messageStart = matcher.group(5);

String message = extractMessage(messageStart, lookahead);

return builder.setFileName(matcher.group(1))
.setLineStart(matcher.group(2))
.setCategory(matcher.group(4))
.setMessage(matcher.group(5))
.setSeverity(mapPriority(type))
.setMessage(message)
.setSeverity(Severity.guessFromString(type))
.buildOptional();
}

private Severity mapPriority(final String type) {
Severity priority;
if ("warning".equalsIgnoreCase(type)) {
priority = Severity.WARNING_NORMAL;
}
else {
priority = Severity.WARNING_HIGH;
/**
* Go through all following lines appending the message until a line with only the ^ Symbol is found.
* @param lookahead lines used for message creation
* @return concatenated message string
*/
private String extractMessage(final String messageStart, final LookaheadStream lookahead) {
StringBuilder messageBuilder = new StringBuilder(messageStart).append("\n");

while (!lookahead.hasNext(MESSAGE_END_REGEX) && lookahead.hasNext()) {
messageBuilder.append(lookahead.next()).append("\n");
}
return priority;

return messageBuilder.toString();
}
}

107 changes: 89 additions & 18 deletions src/main/java/edu/hm/hafner/analysis/parser/GnuFortranParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,117 @@

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.IssueBuilder;
import edu.hm.hafner.analysis.LookaheadParser;
import edu.hm.hafner.analysis.Severity;
import edu.hm.hafner.analysis.RegexpDocumentParser;
import edu.hm.hafner.util.LookaheadStream;

/**
* A parser for (compile-time) messages from the GNU Fortran Compiler.
*
* @author Mat Cross.
*/
public class GnuFortranParser extends RegexpDocumentParser {
public class GnuFortranParser extends LookaheadParser {
private static final long serialVersionUID = 0L;


/**
* The gfortran regex string that follows has been reverse engineered from the show_locus function in
* gcc/fortran/error.c at r204295. By inspection of the GCC release branches this regex should be compatible with
* GCC 4.2 and newer.
*/
private static final String GFORTRAN_MSG_PATTERN = "(?s)^([^\\n]+\\.[^:\\n]+):(\\d+)" // file:line.
+ "(?:\\.(\\d+)(?:-(\\d+))?)?" // Optional column (with optional range).
+ ":\\n" + "(?: Included at [^\\n]+\\n)*" // Optional " Included at file:line:", any number of times.
+ "\\n" + "[^\\n]+\\n" // The offending line itself. NOCHECKSTYLE
+ "[^\\n]+\\n" // The '1' and/or '2' corresponding to the column of the error locus.
+ "(Warning|Error|Fatal Error|Internal Error at \\(1\\)):[\\s\\n]([^\\n]+)\\n";
private static final Pattern LINE_PATTERN = Pattern.compile(" at \\(\\d\\)");
private static final String MESSAGE_START_REGEX = "(?s)^(.+\\.[^:]+):(\\d+)(?:\\.(\\d+)(?:-(\\d+))?)?:";
// file:file followed by Optional coulm and range followed by a colon.

/** Include lines between message start and end. */
private static final String INCLUDE_LINE_REGEX = "(?: {4}Included at .+)";
/** Simple regex to match any non empty lines which are required before the message end. */
private static final String NON_EMPTY_LINE_REGEX = ".+";
/** Simple regex to match any lines which are completely empty. */
private static final String EMPTY_LINE_REGEX = "^$";
/** Optional part of the category. */
private static final Pattern MESSAGE_TRIM_PATTERN = Pattern.compile(" at \\(\\d\\)");
/** Regex to match the category and the actual error message itself. */
private static final Pattern ERROR_MESSAGE_PATTERN = Pattern.compile("(Warning|Error|Fatal Error|Internal Error at \\(1\\)):\\s?(.*)");

/**
* Creates a new instance of {@link GnuFortranParser}.
*/
public GnuFortranParser() {
super(GFORTRAN_MSG_PATTERN, true);
super(MESSAGE_START_REGEX);
}

/**
* This Function is called once the start of a possible error message is detected.
* It uses the provided lookaheadStream to check if the following lines match the message too.
* The lines from the lookaheadStream are only consumed if they are successfully matched to a part
* of the error message, if they can not be matched the function returns without creating an issue.
*
* @param matcher the regular expression matcher
* @param lookahead the lookahead stream to read additional lines
* @param builder the issue builder to use
*
* @return Issue if the rest of the message was sucesfully matched too
*/
@Override
protected Optional<Issue> createIssue(final Matcher matcher, final IssueBuilder builder) {
String category = LINE_PATTERN.matcher(matcher.group(5)).replaceAll("");
return builder.setFileName(matcher.group(1))
.setColumnStart(matcher.group(3))
.setColumnEnd(matcher.group(4))
.setLineStart(matcher.group(2))
protected Optional<Issue> createIssue(final Matcher matcher, final LookaheadStream lookahead, final IssueBuilder builder) {
// Gather location of the error.
String fileName = matcher.group(1);
String lineStart = matcher.group(2);
String columnStart = matcher.group(3);
String columnEnd = matcher.group(4);

// Match all include lines
while (lookahead.hasNext(INCLUDE_LINE_REGEX)) {
lookahead.next();
}

// Optional include lines are followed by one empty line.
if (!lookahead.hasNext(EMPTY_LINE_REGEX)) {
return Optional.empty();
}
lookahead.next(); // Consume the empty line.

// Check for two non empty lines now, one for the offending line one for a numbered indicator.
if (!lookahead.hasNext(NON_EMPTY_LINE_REGEX)) {
return Optional.empty();
}
lookahead.next(); // Consume after match.

if (!lookahead.hasNext(NON_EMPTY_LINE_REGEX)) {
return Optional.empty();
}
lookahead.next(); // Consume after match.

if (!lookahead.hasNext()) {
// Missing error message line.
return Optional.empty();
}

String errorMessageLine = lookahead.peekNext();
Matcher messageMatcher = ERROR_MESSAGE_PATTERN.matcher(errorMessageLine);
if (!messageMatcher.matches()) {
// Invalid message line.
return Optional.empty();
}
lookahead.next();


// Get the category (warning, error, ...) and message and trim location references.
String category = MESSAGE_TRIM_PATTERN.matcher(messageMatcher.group(1)).replaceAll("");
String message = MESSAGE_TRIM_PATTERN.matcher(messageMatcher.group(2)).replaceAll("");

// If the message was not directly in the previous line, interpret the last line of the matched text as message.
if (message.isEmpty() && lookahead.hasNext()) {
message = MESSAGE_TRIM_PATTERN.matcher(lookahead.next()).replaceAll("");
}

return builder.setFileName(fileName)
.setColumnStart(columnStart)
.setColumnEnd(columnEnd)
.setLineStart(lineStart)
.setCategory(category)
.setMessage(LINE_PATTERN.matcher(matcher.group(6)).replaceAll(""))
.setSeverity("Warning".equals(category) ? Severity.WARNING_NORMAL : Severity.WARNING_HIGH)
.setMessage(message)
.setSeverity(Severity.guessFromString(category))
.buildOptional();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,36 @@ class GhsMultiParserTest extends AbstractParserTest {

@Override
protected void assertThatIssuesArePresent(final Report report, final SoftAssertions softly) {
softly.assertThat(report).hasSize(4);
softly.assertThat(report).hasSize(5);
softly.assertThat(report.get(0))
.hasSeverity(Severity.ERROR)
.hasCategory("#5")
.hasLineStart(2)
.hasMessage("cannot open source input file \"file.h\": No such file or directory\n #include <file.h>")
.hasFileName("/maindir/tests/TestCase_0101.cpp");

softly.assertThat(report.get(1))
.hasSeverity(Severity.WARNING_NORMAL)
.hasCategory("#546-D")
.hasLineStart(37)
.hasMessage("transfer of control bypasses initialization of:\n variable \"CF_TRY_FLAG\" (declared at line 42)\n variable \"CF_EXCEPTION_NOT_CAUGHT\" (declared at line 42)\n CF_TRY_CHECK_EX(ex2);")
.hasFileName("/maindir/tests/TestCase_0101.cpp");

softly.assertThat(report.get(1))
softly.assertThat(report.get(2))
.hasSeverity(Severity.WARNING_NORMAL)
.hasCategory("#177-D")
.hasLineStart(29)
.hasMessage("label\n \"CF_TRY_LABELex1\" was declared but never referenced\n CF_TRY_EX(ex1)")
.hasFileName("/maindir/tests/TestCase_0101.cpp");

softly.assertThat(report.get(2))
softly.assertThat(report.get(3))
.hasSeverity(Severity.WARNING_NORMAL)
.hasCategory("#381-D")
.hasLineStart(9)
.hasMessage("extra\n \";\" ignored\n TEST_DSS( CHECK_4TH_CONFIG_DATA, 18, 142, 'F');")
.hasFileName("/maindir/tests/TestCase_1601.cpp");

softly.assertThat(report.get(3))
softly.assertThat(report.get(4))
.hasSeverity(Severity.WARNING_NORMAL)
.hasCategory("#177-D")
.hasLineStart(23)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected void assertThatIssuesArePresent(final Report report, final SoftAsserti
.hasFileName("C:/zlaror.f");

softly.assertThat(iterator.next())
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Fatal Error")
.hasLineStart(7)
.hasLineEnd(7)
Expand All @@ -47,7 +47,7 @@ protected void assertThatIssuesArePresent(final Report report, final SoftAsserti
.hasColumnStart(10);

softly.assertThat(iterator.next())
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Error")
.hasLineStart(81)
.hasLineEnd(81)
Expand All @@ -56,7 +56,7 @@ protected void assertThatIssuesArePresent(final Report report, final SoftAsserti
.hasColumnStart(24);

softly.assertThat(iterator.next())
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Internal Error")
.hasLineStart(5)
.hasLineEnd(5)
Expand Down Expand Up @@ -96,7 +96,7 @@ void testErrorParser() {

assertSoftly(softly -> {
softly.assertThat(warnings.get(0))
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Error")
.hasLineStart(81)
.hasLineEnd(81)
Expand All @@ -117,7 +117,7 @@ void testFatalErrorParser() {

assertSoftly(softly -> {
softly.assertThat(warnings.get(0))
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Fatal Error")
.hasLineStart(7)
.hasLineEnd(7)
Expand All @@ -138,7 +138,7 @@ void testInternalErrorParser() {

assertSoftly(softly -> {
softly.assertThat(warnings.get(0))
.hasSeverity(Severity.WARNING_HIGH)
.hasSeverity(Severity.ERROR)
.hasCategory("Internal Error")
.hasLineStart(5)
.hasLineEnd(5)
Expand All @@ -147,4 +147,15 @@ void testInternalErrorParser() {
.hasColumnStart(8);
});
}

/**
* Test some inputs which are not valid error messages.
* For these no issue should be created.
*/
@Test
void testInvalidParser() {
Report warnings = parse("GnuFortranInvalid.txt");

assertThat(warnings).hasSize(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@


C:/aaaa.f:318:
Included at inc1.inc:4:
Included at foo.f90:4:

IF( XABS.NE.ZERO ) THEN

Warning: Inequality comparison for REAL(8) at (1)


C:/bbbb.f:318:
Included at inc1.inc:4:


1
Warning: Inequality comparison for REAL(8) at (1)


C:/cccc.f:318:
Included at inc1.inc:4:
Included at foo.f90:4:

IF( XABS.NE.ZERO ) THEN
1
Bla: Inequality comparison for REAL(8) at (1)

C:/dddd.f:318:
5 changes: 5 additions & 0 deletions src/test/resources/edu/hm/hafner/analysis/parser/ghsmulti.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
Compiling TestCase_0101.cpp because TestCase_0101.o does not exist
".\maindir\tests\TestCase_0101.cpp", line 2: error #5:
cannot open source input file "file.h": No such file or directory
#include <file.h>
^

".\maindir\tests\TestCase_0101.cpp", line 37: warning #546-D:
transfer of control bypasses initialization of:
variable "CF_TRY_FLAG" (declared at line 42)
Expand Down

0 comments on commit e9f972e

Please sign in to comment.