11package edu .hm .hafner .analysis .parser ;
22
33import org .apache .commons .lang3 .Strings ;
4+ import org .dom4j .util .StringUtils ;
45
56import edu .hm .hafner .analysis .Issue ;
67import edu .hm .hafner .analysis .IssueBuilder ;
7- import edu .hm .hafner .analysis .Location ;
88import edu .hm .hafner .analysis .LookaheadParser ;
9- import edu .hm .hafner .analysis .Severity ;
109import edu .hm .hafner .analysis .util .IntegerParser ;
1110import edu .hm .hafner .util .LookaheadStream ;
12- import edu .hm .hafner .util .TreeStringBuilder ;
1311
1412import java .io .Serial ;
13+ import java .util .ArrayList ;
1514import java .util .Optional ;
1615import java .util .regex .Matcher ;
1716import java .util .regex .Pattern ;
@@ -25,12 +24,13 @@ public class Gcc4CompilerParser extends LookaheadParser {
2524 @ Serial
2625 private static final long serialVersionUID = 5490211629355204910L ;
2726
27+ private static final String LOCATION = "(?<file>.+?):(?<line>\\ d+):(?:(?<column>\\ d+):)?" ;
2828 private static final String GCC_WARNING_PATTERN =
29- ANT_TASK + "(.+?):( \\ d+):(?:( \\ d+):)? ?( [wW]arning|.*[Ee]rror|[Nn]ote ): (.*)$" ;
29+ ANT_TASK + LOCATION + " ?(?<severity> [wW]arning|.*[Ee]rror): (?<message> .*)$" ;
3030 private static final Pattern CLASS_PATTERN = Pattern .compile ("\\ [-W(.+)]$" );
3131 private static final Pattern GCC_OPTION_PATTERN = Pattern .compile ("\\ [-(f[^\\ ]]+)]$" );
3232 private static final Pattern CLANG_TIDY_PATTERN = Pattern .compile ("\\ [[^\\ s\\ ]]+\\ ]$" );
33- private static final Pattern NOTE_PATTERN = Pattern .compile ("^(?<file>.+?):(?<line> \\ d+):(?:(?<column> \\ d+):)? ?[Nn]ote: (?<message>.*)$" );
33+ private static final Pattern NOTE_PATTERN = Pattern .compile ("^" + LOCATION + " ?[Nn]ote: (?<message>.*)$" );
3434
3535 /**
3636 * Creates a new instance of {@link Gcc4CompilerParser}.
@@ -40,8 +40,10 @@ public Gcc4CompilerParser() {
4040 }
4141
4242 /**
43- * Creates a new instance of {@link Gcc4CompilerParser} with specified pattern.
44- * @param pattern a regex pattern to be used instead of the default one
43+ * Creates a new instance of {@link Gcc4CompilerParser} with the specified pattern.
44+ *
45+ * @param pattern
46+ * a regex pattern to be used instead of the default one
4547 */
4648 Gcc4CompilerParser (final String pattern ) {
4749 super (pattern );
@@ -55,64 +57,72 @@ protected boolean isLineInteresting(final String line) {
5557 @ Override
5658 protected Optional <Issue > createIssue (final Matcher matcher , final LookaheadStream lookahead ,
5759 final IssueBuilder builder ) {
58- if (isNoteMessage (matcher )) {
60+ var originalMessage = matcher .group ("message" );
61+ if (isClangTidyWarning (originalMessage )) {
5962 return Optional .empty ();
6063 }
6164
62- var message = new StringBuilder (matcher .group (5 ));
63- var originalMessage = message .toString ();
64-
65- setCategory (builder , originalMessage );
65+ addLocation (matcher , builder );
6666
67+ var message = new StringBuilder (originalMessage );
6768 boolean hasCodeSnippet = false ;
6869 while (lookahead .hasNext () && isMessageContinuation (lookahead , hasCodeSnippet )) {
6970 var continuation = lookahead .next ();
70- if (continuation . length () > 0 && Character . isWhitespace ( continuation . charAt ( 0 ) )) {
71+ if (StringUtils . startsWithWhitespace ( continuation )) {
7172 hasCodeSnippet = true ;
7273 }
7374 message .append ('\n' );
7475 message .append (continuation );
7576 }
7677
77- var treeStringBuilder = new TreeStringBuilder ();
78- var notes = collectNotes (lookahead , builder , treeStringBuilder );
78+ var notes = collectNotes (lookahead , builder );
7979 if (!notes .isEmpty ()) {
8080 message .append ('\n' );
8181 message .append (notes );
8282 }
8383
84- // Reject clang-tidy warnings that have [check-name] but not [-Wwarning-name]
85- // clang-tidy warnings should be handled by ClangTidyParser
86- var messageStr = message .toString ();
87- if (CLANG_TIDY_PATTERN .matcher (messageStr ).find () && !CLASS_PATTERN .matcher (messageStr ).find ()) {
88- return Optional .empty ();
89- }
90-
91- return builder .setFileName (matcher .group (1 ))
92- .setLineStart (matcher .group (2 ))
93- .setColumnStart (matcher .group (3 ))
94- .setMessage (messageStr )
95- .setSeverity (Severity .guessFromString (matcher .group (4 )))
84+ setCategory (builder , originalMessage );
85+ return builder .setMessage (message )
86+ .guessSeverity (matcher .group ("severity" ))
9687 .buildOptional ();
9788 }
9889
90+ private void addLocation (final Matcher matcher , final IssueBuilder builder ) {
91+ var fileName = matcher .group ("file" );
92+ var line = toInt (matcher .group ("line" ));
93+ var column = toInt (matcher .group ("column" ));
94+
95+ builder .addLocation (fileName , line , line , column , column );
96+ }
97+
98+ private int toInt (final String number ) {
99+ return IntegerParser .parseInt (number );
100+ }
101+
99102 /**
100- * Checks if the matched line is a note message.
103+ * Check if the warning is from clang-tidy. Those warnings are quite similar and have [check-name] but not
104+ * [-Wwarning-name] or [-fcompiler-option]. Since a separate parser handles clang-tidy warnings, we can skip them
105+ * here.
106+ *
107+ * @param message
108+ * the original message to check
101109 *
102- * @param matcher the matcher for the current line
103- * @return true if the line is a note message, false otherwise
110+ * @return {@code true} if the message is a clang-tidy warning, {@code false} otherwise
104111 */
105- private boolean isNoteMessage (final Matcher matcher ) {
106- var messageType = matcher .group (4 );
107- return "note" .equals (messageType ) || "Note" .equals (messageType );
112+ private boolean isClangTidyWarning (final String message ) {
113+ return CLANG_TIDY_PATTERN .matcher (message ).find ()
114+ && !CLASS_PATTERN .matcher (message ).find ()
115+ && !GCC_OPTION_PATTERN .matcher (message ).find ();
108116 }
109117
110118 /**
111- * Sets the category based on the original message content.
112- * Checks for GCC warning categories (e.g., [-Wunused-variable]) or compiler options (e.g., [-fpermissive]).
119+ * Sets the category based on the original message content. Checks for GCC warning categories (e.g.,
120+ * [-Wunused-variable]) or compiler options (e.g., [-fpermissive]).
113121 *
114- * @param builder the issue builder
115- * @param originalMessage the original message to extract the category from
122+ * @param builder
123+ * the issue builder
124+ * @param originalMessage
125+ * the original message to extract the category from
116126 */
117127 private void setCategory (final IssueBuilder builder , final String originalMessage ) {
118128 var classMatcher = CLASS_PATTERN .matcher (originalMessage );
@@ -128,38 +138,28 @@ private void setCategory(final IssueBuilder builder, final String originalMessag
128138 }
129139
130140 /**
131- * Collects any subsequent note lines from the lookahead stream and adds them as additional locations.
141+ * Collects any further note lines from the lookahead stream and adds them as additional locations.
142+ *
143+ * @param lookahead
144+ * the lookahead stream
145+ * @param builder
146+ * the issue builder to add locations to
132147 *
133- * @param lookahead the lookahead stream
134- * @param builder the issue builder to add locations to
135- * @param treeStringBuilder the tree string builder for interning file names
136148 * @return a string containing all collected note lines, or an empty string if no notes were found
137149 */
138- private String collectNotes (final LookaheadStream lookahead , final IssueBuilder builder ,
139- final TreeStringBuilder treeStringBuilder ) {
140- var notes = new StringBuilder ();
150+ private String collectNotes (final LookaheadStream lookahead , final IssueBuilder builder ) {
151+ var notes = new ArrayList <String >();
141152 while (lookahead .hasNext ()) {
142- var nextLine = lookahead .peekNext ();
143- var noteMatcher = NOTE_PATTERN .matcher (nextLine );
153+ var noteMatcher = NOTE_PATTERN .matcher (lookahead .peekNext ());
144154 if (noteMatcher .matches ()) {
145- if (!notes .isEmpty ()) {
146- notes .append ('\n' );
147- }
148- notes .append (lookahead .next ());
149-
150- // Add the note as an additional location
151- var file = noteMatcher .group ("file" );
152- var line = IntegerParser .parseInt (noteMatcher .group ("line" ));
153- var column = IntegerParser .parseInt (noteMatcher .group ("column" ));
154-
155- var fileName = treeStringBuilder .intern (file );
156- builder .addLocation (new Location (fileName , line , line , column , column ));
155+ notes .add (lookahead .next ());
156+ addLocation (noteMatcher , builder );
157157 }
158158 else {
159159 break ;
160160 }
161161 }
162- return notes . toString ( );
162+ return String . join ( " \n " , notes );
163163 }
164164
165165 @ SuppressWarnings ("PMD.AvoidLiteralsInIfCondition" )
0 commit comments