1 //===--- DiagChecker.cpp - Diagnostic Checking Functions ------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Process the input files and check that the diagnostic messages are expected.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Frontend/Utils.h"
15 #include "clang/Frontend/TextDiagnosticBuffer.h"
16 #include "clang/Parse/ParseAST.h"
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "llvm/Support/raw_ostream.h"
21 using namespace clang
;
23 typedef TextDiagnosticBuffer::DiagList DiagList
;
24 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator
;
26 static void EmitError(Preprocessor
&PP
, SourceLocation Pos
, const char *String
){
27 unsigned ID
= PP
.getDiagnostics().getCustomDiagID(Diagnostic::Error
, String
);
32 // USING THE DIAGNOSTIC CHECKER:
34 // Indicating that a line expects an error or a warning is simple. Put a comment
35 // on the line that has the diagnostic, use "expected-{error,warning}" to tag
36 // if it's an expected error or warning, and place the expected text between {{
37 // and }} markers. The full text doesn't have to be included, only enough to
38 // ensure that the correct diagnostic was emitted.
42 // int A = B; // expected-error {{use of undeclared identifier 'B'}}
44 // You can place as many diagnostics on one line as you wish. To make the code
45 // more readable, you can use slash-newline to separate out the diagnostics.
47 // The simple syntax above allows each specification to match exactly one error.
48 // You can use the extended syntax to customize this. The extended syntax is
49 // "expected-<type> <n> {{diag text}}", where <type> is one of "error",
50 // "warning" or "note", and <n> is a positive integer. This allows the
51 // diagnostic to appear as many times as specified. Example:
53 // void f(); // expected-note 2 {{previous declaration is here}}
56 /// FindDiagnostics - Go through the comment and see if it indicates expected
57 /// diagnostics. If so, then put them in a diagnostic list.
59 static void FindDiagnostics(const char *CommentStart
, unsigned CommentLen
,
60 DiagList
&ExpectedDiags
,
61 Preprocessor
&PP
, SourceLocation Pos
,
62 const char *ExpectedStr
) {
63 const char *CommentEnd
= CommentStart
+CommentLen
;
64 unsigned ExpectedStrLen
= strlen(ExpectedStr
);
66 // Find all expected-foo diagnostics in the string and add them to
68 while (CommentStart
!= CommentEnd
) {
69 CommentStart
= std::find(CommentStart
, CommentEnd
, 'e');
70 if (unsigned(CommentEnd
-CommentStart
) < ExpectedStrLen
) return;
72 // If this isn't expected-foo, ignore it.
73 if (memcmp(CommentStart
, ExpectedStr
, ExpectedStrLen
)) {
78 CommentStart
+= ExpectedStrLen
;
81 while (CommentStart
!= CommentEnd
&&
82 isspace(CommentStart
[0]))
85 // Default, if we find the '{' now, is 1 time.
88 // In extended syntax, there could be a digit now.
89 while (CommentStart
!= CommentEnd
&&
90 CommentStart
[0] >= '0' && CommentStart
[0] <= '9') {
92 Temp
+= CommentStart
[0] - '0';
98 // Skip whitespace again.
99 while (CommentStart
!= CommentEnd
&&
100 isspace(CommentStart
[0]))
103 // We should have a {{ now.
104 if (CommentEnd
-CommentStart
< 2 ||
105 CommentStart
[0] != '{' || CommentStart
[1] != '{') {
106 if (std::find(CommentStart
, CommentEnd
, '{') != CommentEnd
)
107 EmitError(PP
, Pos
, "bogus characters before '{{' in expected string");
109 EmitError(PP
, Pos
, "cannot find start ('{{') of expected string");
115 const char *ExpectedEnd
= CommentStart
;
117 ExpectedEnd
= std::find(ExpectedEnd
, CommentEnd
, '}');
118 if (CommentEnd
-ExpectedEnd
< 2) {
119 EmitError(PP
, Pos
, "cannot find end ('}}') of expected string");
123 if (ExpectedEnd
[1] == '}')
126 ++ExpectedEnd
; // Skip over singular }'s
129 std::string
Msg(CommentStart
, ExpectedEnd
);
130 std::string::size_type FindPos
;
131 while ((FindPos
= Msg
.find("\\n")) != std::string::npos
)
132 Msg
.replace(FindPos
, 2, "\n");
133 // Add is possibly multiple times.
134 for (int i
= 0; i
< Times
; ++i
)
135 ExpectedDiags
.push_back(std::make_pair(Pos
, Msg
));
137 CommentStart
= ExpectedEnd
;
141 /// FindExpectedDiags - Lex the main source file to find all of the
142 // expected errors and warnings.
143 static void FindExpectedDiags(Preprocessor
&PP
,
144 DiagList
&ExpectedErrors
,
145 DiagList
&ExpectedWarnings
,
146 DiagList
&ExpectedNotes
) {
147 // Create a raw lexer to pull all the comments out of the main file. We don't
148 // want to look in #include'd headers for expected-error strings.
149 FileID FID
= PP
.getSourceManager().getMainFileID();
151 // Create a lexer to lex all the tokens of the main file in raw mode.
152 const llvm::MemoryBuffer
*FromFile
= PP
.getSourceManager().getBuffer(FID
);
153 Lexer
RawLex(FID
, FromFile
, PP
.getSourceManager(), PP
.getLangOptions());
155 // Return comments as tokens, this is how we find expected diagnostics.
156 RawLex
.SetCommentRetentionState(true);
159 Tok
.setKind(tok::comment
);
160 while (Tok
.isNot(tok::eof
)) {
162 if (!Tok
.is(tok::comment
)) continue;
164 std::string Comment
= PP
.getSpelling(Tok
);
165 if (Comment
.empty()) continue;
168 // Find all expected errors.
169 FindDiagnostics(&Comment
[0], Comment
.size(), ExpectedErrors
, PP
,
170 Tok
.getLocation(), "expected-error");
172 // Find all expected warnings.
173 FindDiagnostics(&Comment
[0], Comment
.size(), ExpectedWarnings
, PP
,
174 Tok
.getLocation(), "expected-warning");
176 // Find all expected notes.
177 FindDiagnostics(&Comment
[0], Comment
.size(), ExpectedNotes
, PP
,
178 Tok
.getLocation(), "expected-note");
182 /// PrintProblem - This takes a diagnostic map of the delta between expected and
183 /// seen diagnostics. If there's anything in it, then something unexpected
184 /// happened. Print the map out in a nice format and return "true". If the map
185 /// is empty and we're not going to print things, then return "false".
187 static bool PrintProblem(SourceManager
&SourceMgr
,
188 const_diag_iterator diag_begin
,
189 const_diag_iterator diag_end
,
191 if (diag_begin
== diag_end
) return false;
193 llvm::errs() << Msg
<< "\n";
194 for (const_diag_iterator I
= diag_begin
, E
= diag_end
; I
!= E
; ++I
)
195 llvm::errs() << " Line " << SourceMgr
.getInstantiationLineNumber(I
->first
)
196 << " " << I
->second
<< "\n";
201 /// CompareDiagLists - Compare two diagnostic lists and return the difference
204 static bool CompareDiagLists(SourceManager
&SourceMgr
,
205 const_diag_iterator d1_begin
,
206 const_diag_iterator d1_end
,
207 const_diag_iterator d2_begin
,
208 const_diag_iterator d2_end
,
209 const char *MsgLeftOnly
,
210 const char *MsgRightOnly
) {
212 DiagList
Left(d1_begin
, d1_end
);
213 DiagList
Right(d2_begin
, d2_end
);
215 for (const_diag_iterator I
= Left
.begin(), E
= Left
.end(); I
!= E
; ++I
) {
216 unsigned LineNo1
= SourceMgr
.getInstantiationLineNumber(I
->first
);
217 const std::string
&Diag1
= I
->second
;
219 DiagList::iterator II
, IE
;
220 for (II
= Right
.begin(), IE
= Right
.end(); II
!= IE
; ++II
) {
221 unsigned LineNo2
= SourceMgr
.getInstantiationLineNumber(II
->first
);
222 if (LineNo1
!= LineNo2
) continue;
224 const std::string
&Diag2
= II
->second
;
225 if (Diag2
.find(Diag1
) != std::string::npos
||
226 Diag1
.find(Diag2
) != std::string::npos
) {
232 LeftOnly
.push_back(*I
);
234 // Found. The same cannot be found twice.
238 // Now all that's left in Right are those that were not matched.
240 return PrintProblem(SourceMgr
, LeftOnly
.begin(), LeftOnly
.end(), MsgLeftOnly
)
241 | PrintProblem(SourceMgr
, Right
.begin(), Right
.end(), MsgRightOnly
);
244 /// CheckResults - This compares the expected results to those that
245 /// were actually reported. It emits any discrepencies. Return "true" if there
246 /// were problems. Return "false" otherwise.
248 static bool CheckResults(Preprocessor
&PP
,
249 const DiagList
&ExpectedErrors
,
250 const DiagList
&ExpectedWarnings
,
251 const DiagList
&ExpectedNotes
) {
252 const DiagnosticClient
*DiagClient
= PP
.getDiagnostics().getClient();
253 assert(DiagClient
!= 0 &&
254 "DiagChecker requires a valid TextDiagnosticBuffer");
255 const TextDiagnosticBuffer
&Diags
=
256 static_cast<const TextDiagnosticBuffer
&>(*DiagClient
);
257 SourceManager
&SourceMgr
= PP
.getSourceManager();
259 // We want to capture the delta between what was expected and what was
262 // Expected \ Seen - set expected but not seen
263 // Seen \ Expected - set seen but not expected
264 bool HadProblem
= false;
266 // See if there are error mismatches.
267 HadProblem
|= CompareDiagLists(SourceMgr
,
268 ExpectedErrors
.begin(), ExpectedErrors
.end(),
269 Diags
.err_begin(), Diags
.err_end(),
270 "Errors expected but not seen:",
271 "Errors seen but not expected:");
273 // See if there are warning mismatches.
274 HadProblem
|= CompareDiagLists(SourceMgr
,
275 ExpectedWarnings
.begin(),
276 ExpectedWarnings
.end(),
277 Diags
.warn_begin(), Diags
.warn_end(),
278 "Warnings expected but not seen:",
279 "Warnings seen but not expected:");
281 // See if there are note mismatches.
282 HadProblem
|= CompareDiagLists(SourceMgr
,
283 ExpectedNotes
.begin(),
285 Diags
.note_begin(), Diags
.note_end(),
286 "Notes expected but not seen:",
287 "Notes seen but not expected:");
293 /// CheckDiagnostics - Gather the expected diagnostics and check them.
294 bool clang::CheckDiagnostics(Preprocessor
&PP
) {
295 // Gather the set of expected diagnostics.
296 DiagList ExpectedErrors
, ExpectedWarnings
, ExpectedNotes
;
297 FindExpectedDiags(PP
, ExpectedErrors
, ExpectedWarnings
, ExpectedNotes
);
299 // Check that the expected diagnostics occurred.
300 return CheckResults(PP
, ExpectedErrors
, ExpectedWarnings
, ExpectedNotes
);