1212import javax .tools .JavaFileObject ;
1313import org .checkerframework .checker .nullness .qual .EnsuresNonNullIf ;
1414import org .checkerframework .checker .nullness .qual .Nullable ;
15+ import org .checkerframework .javacutil .BugInCF ;
1516import org .plumelib .util .CollectionsPlume ;
1617import org .plumelib .util .IPair ;
1718
@@ -64,8 +65,8 @@ public static TestDiagnostic fromDiagnosticFileString(String stringFromDiagnosti
6465 }
6566
6667 /**
67- * Instantiate the diagnostic from a string that would appear in a Java file, e.g.: "error:
68- * (message)"
68+ * Instantiate the diagnostic from a string that would appear in a Java test file, e.g.: "error:
69+ * (error- message-key )"
6970 *
7071 * @param filename the file containing the diagnostic (and the error)
7172 * @param lineNumber the line number of the line immediately below the diagnostic comment in the
@@ -117,17 +118,18 @@ public static TestDiagnostic fromJSpecifyFileComment(
117118 lineNumber ,
118119 DiagnosticKind .JSpecify ,
119120 stringFromJavaFile ,
120- /* isFixable= */ false ,
121- /* omitParentheses = */ true );
121+ null ,
122+ /* isFixable = */ false );
122123 }
123124
124125 /**
125- * Instantiate the diagnostic via pattern-matching against patterns.
126+ * Instantiate the diagnostic via pattern-matching against the given patterns.
126127 *
127128 * @param diagnosticPattern a pattern that matches any diagnostic
128129 * @param warningPattern a pattern that matches a warning diagnostic
129130 * @param filename the file name
130- * @param lineNumber the line number
131+ * @param lineNumber the line number. Either this is non-null, or the pattern's first matching
132+ * group is the line number.
131133 * @param diagnosticString the string to parse
132134 * @return a diagnostic parsed from the given string
133135 */
@@ -139,9 +141,9 @@ protected static TestDiagnostic fromPatternMatching(
139141 @ Nullable Long lineNumber ,
140142 String diagnosticString ) {
141143 final DiagnosticKind kind ;
144+ final String key ;
142145 final String message ;
143146 final boolean isFixable ;
144- final boolean noParentheses ;
145147 long lineNo = -1 ;
146148 int capturingGroupOffset = 1 ;
147149
@@ -152,13 +154,33 @@ protected static TestDiagnostic fromPatternMatching(
152154
153155 Matcher diagnosticMatcher = diagnosticPattern .matcher (diagnosticString );
154156 if (diagnosticMatcher .matches ()) {
155- IPair <DiagnosticKind , Boolean > categoryToFixable =
157+
158+ IPair <DiagnosticKind , Boolean > categoryAndFixable =
156159 categoryAndFixable (diagnosticMatcher .group (1 + capturingGroupOffset ));
157- kind = categoryToFixable .first ;
158- isFixable = categoryToFixable .second ;
160+ kind = categoryAndFixable .first ;
161+ isFixable = categoryAndFixable .second ;
159162 String msg = diagnosticMatcher .group (2 + capturingGroupOffset ).trim ();
160- noParentheses = msg .equals ("" ) || msg .charAt (0 ) != '(' || msg .charAt (msg .length () - 1 ) != ')' ;
161- message = noParentheses ? msg : msg .substring (1 , msg .length () - 1 );
163+
164+ char firstChar = msg .isEmpty () ? ' ' : msg .charAt (0 );
165+ if (firstChar == '(' || firstChar == '[' ) {
166+ char closeDelimiter = firstChar == '(' ? ')' : ']' ;
167+ int closeDelimiterPos = msg .indexOf (closeDelimiter );
168+ int msgLength = msg .length ();
169+ if (closeDelimiterPos == msgLength - 1 ) {
170+ key = msg .substring (1 , msgLength - 1 );
171+ message = null ;
172+ } else if (closeDelimiterPos != -1 ) {
173+ key = msg .substring (1 , closeDelimiterPos );
174+ message = msg .substring (closeDelimiterPos + 1 ).trim ();
175+ } else {
176+ throw new BugInCF (
177+ "No closing delimiter '%s' found in %s" , closeDelimiter , diagnosticString );
178+ }
179+ } else {
180+ // The message does not start with "(" or "[".
181+ key = msg ;
182+ message = null ;
183+ }
162184
163185 if (lineNumber == null ) {
164186 try {
@@ -173,8 +195,8 @@ protected static TestDiagnostic fromPatternMatching(
173195 if (warningMatcher .matches ()) {
174196 kind = DiagnosticKind .Warning ;
175197 isFixable = false ;
176- message = warningMatcher .group (1 + capturingGroupOffset );
177- noParentheses = true ;
198+ key = warningMatcher .group (1 + capturingGroupOffset );
199+ message = null ;
178200
179201 if (lineNumber == null ) {
180202 try {
@@ -187,8 +209,8 @@ protected static TestDiagnostic fromPatternMatching(
187209 } else if (diagnosticString .startsWith ("warning:" )) {
188210 kind = DiagnosticKind .Warning ;
189211 isFixable = false ;
190- message = diagnosticString .substring ("warning:" .length ()).trim ();
191- noParentheses = true ;
212+ key = diagnosticString .substring ("warning:" .length ()).trim ();
213+ message = null ;
192214 if (lineNumber != null ) {
193215 lineNo = lineNumber ;
194216 } else {
@@ -198,19 +220,23 @@ protected static TestDiagnostic fromPatternMatching(
198220 } else {
199221 kind = DiagnosticKind .Other ;
200222 isFixable = false ;
201- message = diagnosticString ;
202- noParentheses = true ;
223+ key = diagnosticString ;
224+ message = null ;
203225
204- // this should only happen if we are parsing a Java Diagnostic from the compiler
205- // that we did do not handle
226+ // This should only happen if we are parsing a Java Diagnostic from the compiler
227+ // that we did do not handle.
206228 if (lineNumber == null ) {
207229 lineNo = -1 ;
208230 }
209231 }
210232 }
211- return new TestDiagnostic (filename , lineNo , kind , message , isFixable , noParentheses );
233+ return new TestDiagnostic (filename , lineNo , kind , key , message , isFixable );
212234 }
213235
236+ /** Matches an absolute filename (with delimiters). */
237+ // TODO: This only handles Unix paths, not Windows paths.
238+ static final Pattern filenamePattern = Pattern .compile (" (?:/[^: ]*/)([^/: ]+\\ .[a-z]+):" );
239+
214240 /**
215241 * Given a javax diagnostic, return a pair of (trimmed, filename), where "trimmed" is the first
216242 * line of the message, without the leading filename.
@@ -224,17 +250,26 @@ public static IPair<String, String> messageAndFilename(String original, boolean
224250 String filename = "" ;
225251 if (noMsgText ) {
226252 if (!retainAllLines (trimmed )) {
253+
254+ // Retain only the first line.
227255 int lineSepPos = trimmed .indexOf (System .lineSeparator ());
228256 if (lineSepPos != -1 ) {
229257 trimmed = trimmed .substring (0 , lineSepPos );
230258 }
231259
232260 int extensionPos = trimmed .indexOf (".java:" );
233261 if (extensionPos != -1 ) {
234- int basenameStart = trimmed .lastIndexOf (File .separator );
262+ int basenameStart = trimmed .lastIndexOf (File .separator , extensionPos );
235263 filename = trimmed .substring (basenameStart + 1 , extensionPos + 5 ).trim ();
236264 trimmed = trimmed .substring (extensionPos + 5 ).trim ();
237265 }
266+
267+ // Retain only the file basename, without directories, when embedded in message.
268+ Matcher m = filenamePattern .matcher (trimmed );
269+ if (m .find ()) {
270+ trimmed =
271+ trimmed .substring (0 , m .start ()) + " " + m .group (1 ) + ":" + trimmed .substring (m .end ());
272+ }
238273 }
239274 }
240275
@@ -363,8 +398,8 @@ public static TestDiagnosticLine fromJavaSourceLine(
363398 lineNumber ,
364399 DiagnosticKind .Error ,
365400 "Use \" // ::\" , not \" //::\" " ,
366- false ,
367- true );
401+ null ,
402+ false );
368403 return new TestDiagnosticLine (
369404 filename , lineNumber , line , Collections .singletonList (diagnostic ));
370405 } else if (trimmedLine .startsWith ("// jspecify_" )) {
0 commit comments