forked from jenkinsci/analysis-model
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGnuFortranParser.java
117 lines (100 loc) · 4.78 KB
/
GnuFortranParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package edu.hm.hafner.analysis.parser;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.util.LookaheadStream;
/**
* A parser for (compile-time) messages from the GNU Fortran Compiler.
*
* @author Mat Cross.
*/
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 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(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 LookaheadStream lookahead, final IssueBuilder builder) {
// Gather location of the error.
// 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(matcher.group(1))
.setLineStart(matcher.group(2))
.setColumnStart(matcher.group(3))
.setColumnEnd(matcher.group(4))
.setCategory(category)
.setMessage(message)
.setSeverity(Severity.guessFromString(category))
.buildOptional();
}
}