blob: f2a260631711c967b34f29cbe193c50d0bd162c8 [file] [log] [blame]
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +00001//===--- VerifyDiagnosticsClient.cpp - Verifying Diagnostic Client --------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This is a concrete diagnostic client, which buffers the diagnostic messages.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Frontend/VerifyDiagnosticsClient.h"
15#include "clang/Frontend/FrontendDiagnostic.h"
16#include "clang/Frontend/TextDiagnosticBuffer.h"
17#include "clang/Lex/Preprocessor.h"
18#include "llvm/ADT/SmallString.h"
Chris Lattner60909e12010-04-28 20:02:30 +000019#include "llvm/Support/Regex.h"
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000020#include "llvm/Support/raw_ostream.h"
Argyrios Kyrtzidised635e42010-08-15 01:15:27 +000021#include "llvm/Support/Compiler.h"
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000022using namespace clang;
23
Daniel Dunbar221c7212009-11-14 07:53:24 +000024VerifyDiagnosticsClient::VerifyDiagnosticsClient(Diagnostic &_Diags,
25 DiagnosticClient *_Primary)
26 : Diags(_Diags), PrimaryClient(_Primary),
27 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), NumErrors(0) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000028}
29
30VerifyDiagnosticsClient::~VerifyDiagnosticsClient() {
31 CheckDiagnostics();
32}
33
34// DiagnosticClient interface.
35
36void VerifyDiagnosticsClient::BeginSourceFile(const LangOptions &LangOpts,
37 const Preprocessor *PP) {
38 // FIXME: Const hack, we screw up the preprocessor but in practice its ok
39 // because it doesn't get reused. It would be better if we could make a copy
40 // though.
41 CurrentPreprocessor = const_cast<Preprocessor*>(PP);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000042
43 PrimaryClient->BeginSourceFile(LangOpts, PP);
44}
45
46void VerifyDiagnosticsClient::EndSourceFile() {
47 CheckDiagnostics();
48
49 PrimaryClient->EndSourceFile();
50
51 CurrentPreprocessor = 0;
52}
Daniel Dunbar221c7212009-11-14 07:53:24 +000053
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000054void VerifyDiagnosticsClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
55 const DiagnosticInfo &Info) {
56 // Send the diagnostic to the buffer, we will check it once we reach the end
57 // of the source file (or are destructed).
58 Buffer->HandleDiagnostic(DiagLevel, Info);
59}
60
61// FIXME: It would be nice to just get this from the primary diagnostic client
62// or something.
63bool VerifyDiagnosticsClient::HadErrors() {
64 CheckDiagnostics();
65
66 return NumErrors != 0;
67}
68
69//===----------------------------------------------------------------------===//
70// Checking diagnostics implementation.
71//===----------------------------------------------------------------------===//
72
73typedef TextDiagnosticBuffer::DiagList DiagList;
74typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
75
Chris Lattner60909e12010-04-28 20:02:30 +000076namespace {
77
78/// Directive - Abstract class representing a parsed verify directive.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000079///
Chris Lattner60909e12010-04-28 20:02:30 +000080class Directive {
81public:
82 static Directive* Create(bool RegexKind, const SourceLocation &Location,
83 const std::string &Text, unsigned Count);
84public:
85 SourceLocation Location;
86 const std::string Text;
87 unsigned Count;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000088
Chris Lattner60909e12010-04-28 20:02:30 +000089 virtual ~Directive() { }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000090
Chris Lattner60909e12010-04-28 20:02:30 +000091 // Returns true if directive text is valid.
92 // Otherwise returns false and populates E.
93 virtual bool isValid(std::string &Error) = 0;
94
95 // Returns true on match.
96 virtual bool Match(const std::string &S) = 0;
97
98protected:
99 Directive(const SourceLocation &Location, const std::string &Text,
100 unsigned Count)
101 : Location(Location), Text(Text), Count(Count) { }
102
103private:
Argyrios Kyrtzidised635e42010-08-15 01:15:27 +0000104 Directive(const Directive&) ATTRIBUTE_UNUSED; // DO NOT IMPLEMENT
105 void operator=(const Directive&) ATTRIBUTE_UNUSED; // DO NOT IMPLEMENT
Chris Lattner60909e12010-04-28 20:02:30 +0000106};
107
108/// StandardDirective - Directive with string matching.
109///
110class StandardDirective : public Directive {
111public:
112 StandardDirective(const SourceLocation &Location, const std::string &Text,
113 unsigned Count)
114 : Directive(Location, Text, Count) { }
115
116 virtual bool isValid(std::string &Error) {
117 // all strings are considered valid; even empty ones
118 return true;
119 }
120
121 virtual bool Match(const std::string &S) {
122 return S.find(Text) != std::string::npos ||
123 Text.find(S) != std::string::npos;
124 }
125};
126
127/// RegexDirective - Directive with regular-expression matching.
128///
129class RegexDirective : public Directive {
130public:
131 RegexDirective(const SourceLocation &Location, const std::string &Text,
132 unsigned Count)
133 : Directive(Location, Text, Count), Regex(Text) { }
134
135 virtual bool isValid(std::string &Error) {
136 if (Regex.isValid(Error))
137 return true;
138 return false;
139 }
140
141 virtual bool Match(const std::string &S) {
142 return Regex.match(S);
143 }
144
145private:
146 llvm::Regex Regex;
147};
148
149typedef std::vector<Directive*> DirectiveList;
150
151/// ExpectedData - owns directive objects and deletes on destructor.
152///
153struct ExpectedData {
154 DirectiveList Errors;
155 DirectiveList Warnings;
156 DirectiveList Notes;
157
158 ~ExpectedData() {
159 DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 };
160 for (DirectiveList **PL = Lists; *PL; ++PL) {
161 DirectiveList * const L = *PL;
162 for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I)
163 delete *I;
164 }
165 }
166};
167
168class ParseHelper
169{
170public:
171 ParseHelper(const char *Begin, const char *End)
172 : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
173
174 // Return true if string literal is next.
175 bool Next(const std::string &S) {
176 std::string::size_type LEN = S.length();
177 P = C;
178 PEnd = C + LEN;
179 if (PEnd > End)
180 return false;
181 return !memcmp(P, S.c_str(), LEN);
182 }
183
184 // Return true if number is next.
185 // Output N only if number is next.
186 bool Next(unsigned &N) {
187 unsigned TMP = 0;
188 P = C;
189 for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
190 TMP *= 10;
191 TMP += P[0] - '0';
192 }
193 if (P == C)
194 return false;
195 PEnd = P;
196 N = TMP;
197 return true;
198 }
199
200 // Return true if string literal is found.
201 // When true, P marks begin-position of S in content.
202 bool Search(const std::string &S) {
203 P = std::search(C, End, S.begin(), S.end());
204 PEnd = P + S.length();
205 return P != End;
206 }
207
208 // Advance 1-past previous next/search.
209 // Behavior is undefined if previous next/search failed.
210 bool Advance() {
211 C = PEnd;
212 return C < End;
213 }
214
215 // Skip zero or more whitespace.
216 void SkipWhitespace() {
217 for (; C < End && isspace(*C); ++C)
218 ;
219 }
220
221 // Return true if EOF reached.
222 bool Done() {
223 return !(C < End);
224 }
225
226 const char * const Begin; // beginning of expected content
227 const char * const End; // end of expected content (1-past)
228 const char *C; // position of next char in content
229 const char *P;
230
231private:
232 const char *PEnd; // previous next/search subject end (1-past)
233};
234
235} // namespace anonymous
236
237/// ParseDirective - Go through the comment and see if it indicates expected
238/// diagnostics. If so, then put them in the appropriate directive list.
239///
240static void ParseDirective(const char *CommentStart, unsigned CommentLen,
241 ExpectedData &ED, Preprocessor &PP,
242 SourceLocation Pos) {
243 // A single comment may contain multiple directives.
244 for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
245 // search for token: expected
246 if (!PH.Search("expected"))
247 break;
248 PH.Advance();
249
250 // next token: -
251 if (!PH.Next("-"))
252 continue;
253 PH.Advance();
254
255 // next token: { error | warning | note }
256 DirectiveList* DL = NULL;
257 if (PH.Next("error"))
258 DL = &ED.Errors;
259 else if (PH.Next("warning"))
260 DL = &ED.Warnings;
261 else if (PH.Next("note"))
262 DL = &ED.Notes;
263 else
264 continue;
265 PH.Advance();
266
267 // default directive kind
268 bool RegexKind = false;
269 const char* KindStr = "string";
270
271 // next optional token: -
272 if (PH.Next("-re")) {
273 PH.Advance();
274 RegexKind = true;
275 KindStr = "regex";
276 }
277
278 // skip optional whitespace
279 PH.SkipWhitespace();
280
281 // next optional token: positive integer
282 unsigned Count = 1;
283 if (PH.Next(Count))
284 PH.Advance();
285
286 // skip optional whitespace
287 PH.SkipWhitespace();
288
289 // next token: {{
290 if (!PH.Next("{{")) {
291 PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin),
292 diag::err_verify_missing_start) << KindStr;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000293 continue;
294 }
Chris Lattner60909e12010-04-28 20:02:30 +0000295 PH.Advance();
296 const char* const ContentBegin = PH.C; // mark content begin
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000297
Chris Lattner60909e12010-04-28 20:02:30 +0000298 // search for token: }}
299 if (!PH.Search("}}")) {
300 PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin),
301 diag::err_verify_missing_end) << KindStr;
302 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000303 }
Chris Lattner60909e12010-04-28 20:02:30 +0000304 const char* const ContentEnd = PH.P; // mark content end
305 PH.Advance();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000306
Chris Lattner60909e12010-04-28 20:02:30 +0000307 // build directive text; convert \n to newlines
308 std::string Text;
309 llvm::StringRef NewlineStr = "\\n";
310 llvm::StringRef Content(ContentBegin, ContentEnd-ContentBegin);
311 size_t CPos = 0;
312 size_t FPos;
313 while ((FPos = Content.find(NewlineStr, CPos)) != llvm::StringRef::npos) {
314 Text += Content.substr(CPos, FPos-CPos);
315 Text += '\n';
316 CPos = FPos + NewlineStr.size();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000317 }
Chris Lattner60909e12010-04-28 20:02:30 +0000318 if (Text.empty())
319 Text.assign(ContentBegin, ContentEnd);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000320
Chris Lattner60909e12010-04-28 20:02:30 +0000321 // construct new directive
322 Directive *D = Directive::Create(RegexKind, Pos, Text, Count);
323 std::string Error;
324 if (D->isValid(Error))
325 DL->push_back(D);
326 else {
327 PP.Diag(Pos.getFileLocWithOffset(ContentBegin-PH.Begin),
328 diag::err_verify_invalid_content)
329 << KindStr << Error;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000330 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000331 }
332}
333
334/// FindExpectedDiags - Lex the main source file to find all of the
335// expected errors and warnings.
Chris Lattner60909e12010-04-28 20:02:30 +0000336static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000337 // Create a raw lexer to pull all the comments out of the main file. We don't
338 // want to look in #include'd headers for expected-error strings.
Chris Lattner6e290142009-11-30 04:18:44 +0000339 SourceManager &SM = PP.getSourceManager();
340 FileID FID = SM.getMainFileID();
341 if (SM.getMainFileID().isInvalid())
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000342 return;
343
344 // Create a lexer to lex all the tokens of the main file in raw mode.
Chris Lattner6e290142009-11-30 04:18:44 +0000345 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
346 Lexer RawLex(FID, FromFile, SM, PP.getLangOptions());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000347
348 // Return comments as tokens, this is how we find expected diagnostics.
349 RawLex.SetCommentRetentionState(true);
350
351 Token Tok;
352 Tok.setKind(tok::comment);
353 while (Tok.isNot(tok::eof)) {
354 RawLex.Lex(Tok);
355 if (!Tok.is(tok::comment)) continue;
356
357 std::string Comment = PP.getSpelling(Tok);
358 if (Comment.empty()) continue;
359
Chris Lattner60909e12010-04-28 20:02:30 +0000360 // Find all expected errors/warnings/notes.
361 ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000362 };
363}
364
365/// PrintProblem - This takes a diagnostic map of the delta between expected and
366/// seen diagnostics. If there's anything in it, then something unexpected
367/// happened. Print the map out in a nice format and return "true". If the map
368/// is empty and we're not going to print things, then return "false".
369///
Daniel Dunbar221c7212009-11-14 07:53:24 +0000370static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr,
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000371 const_diag_iterator diag_begin,
372 const_diag_iterator diag_end,
373 const char *Kind, bool Expected) {
374 if (diag_begin == diag_end) return 0;
375
376 llvm::SmallString<256> Fmt;
377 llvm::raw_svector_ostream OS(Fmt);
378 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
Daniel Dunbar221c7212009-11-14 07:53:24 +0000379 if (I->first.isInvalid() || !SourceMgr)
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000380 OS << "\n (frontend)";
381 else
Daniel Dunbar221c7212009-11-14 07:53:24 +0000382 OS << "\n Line " << SourceMgr->getInstantiationLineNumber(I->first);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000383 OS << ": " << I->second;
384 }
385
386 Diags.Report(diag::err_verify_inconsistent_diags)
Daniel Dunbar221c7212009-11-14 07:53:24 +0000387 << Kind << !Expected << OS.str();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000388 return std::distance(diag_begin, diag_end);
389}
390
Chris Lattner60909e12010-04-28 20:02:30 +0000391static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr,
392 DirectiveList &DL, const char *Kind,
393 bool Expected) {
394 if (DL.empty())
395 return 0;
396
397 llvm::SmallString<256> Fmt;
398 llvm::raw_svector_ostream OS(Fmt);
399 for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
400 Directive& D = **I;
401 if (D.Location.isInvalid() || !SourceMgr)
402 OS << "\n (frontend)";
403 else
404 OS << "\n Line " << SourceMgr->getInstantiationLineNumber(D.Location);
405 OS << ": " << D.Text;
406 }
407
408 Diags.Report(diag::err_verify_inconsistent_diags)
409 << Kind << !Expected << OS.str();
410 return DL.size();
411}
412
413/// CheckLists - Compare expected to seen diagnostic lists and return the
414/// the difference between them.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000415///
Chris Lattner60909e12010-04-28 20:02:30 +0000416static unsigned CheckLists(Diagnostic &Diags, SourceManager &SourceMgr,
417 const char *Label,
418 DirectiveList &Left,
419 const_diag_iterator d2_begin,
420 const_diag_iterator d2_end) {
421 DirectiveList LeftOnly;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000422 DiagList Right(d2_begin, d2_end);
423
Chris Lattner60909e12010-04-28 20:02:30 +0000424 for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
425 Directive& D = **I;
426 unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(D.Location);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000427
Chris Lattner60909e12010-04-28 20:02:30 +0000428 for (unsigned i = 0; i < D.Count; ++i) {
429 DiagList::iterator II, IE;
430 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
431 unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first);
432 if (LineNo1 != LineNo2)
433 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000434
Chris Lattner60909e12010-04-28 20:02:30 +0000435 const std::string &RightText = II->second;
436 if (D.Match(RightText))
437 break;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000438 }
Chris Lattner60909e12010-04-28 20:02:30 +0000439 if (II == IE) {
440 // Not found.
441 LeftOnly.push_back(*I);
442 } else {
443 // Found. The same cannot be found twice.
444 Right.erase(II);
445 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000446 }
447 }
448 // Now all that's left in Right are those that were not matched.
449
Chris Lattner60909e12010-04-28 20:02:30 +0000450 return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) +
451 PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(),
452 Label, false));
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000453}
454
455/// CheckResults - This compares the expected results to those that
456/// were actually reported. It emits any discrepencies. Return "true" if there
457/// were problems. Return "false" otherwise.
458///
459static unsigned CheckResults(Diagnostic &Diags, SourceManager &SourceMgr,
460 const TextDiagnosticBuffer &Buffer,
Chris Lattner60909e12010-04-28 20:02:30 +0000461 ExpectedData &ED) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000462 // We want to capture the delta between what was expected and what was
463 // seen.
464 //
465 // Expected \ Seen - set expected but not seen
466 // Seen \ Expected - set seen but not expected
467 unsigned NumProblems = 0;
468
469 // See if there are error mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000470 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
471 Buffer.err_begin(), Buffer.err_end());
Daniel Dunbar221c7212009-11-14 07:53:24 +0000472
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000473 // See if there are warning mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000474 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
475 Buffer.warn_begin(), Buffer.warn_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000476
477 // See if there are note mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000478 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
479 Buffer.note_begin(), Buffer.note_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000480
481 return NumProblems;
482}
483
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000484void VerifyDiagnosticsClient::CheckDiagnostics() {
Chris Lattner60909e12010-04-28 20:02:30 +0000485 ExpectedData ED;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000486
487 // Ensure any diagnostics go to the primary client.
Daniel Dunbar221c7212009-11-14 07:53:24 +0000488 DiagnosticClient *CurClient = Diags.getClient();
489 Diags.setClient(PrimaryClient.get());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000490
491 // If we have a preprocessor, scan the source for expected diagnostic
492 // markers. If not then any diagnostics are unexpected.
493 if (CurrentPreprocessor) {
Chris Lattner60909e12010-04-28 20:02:30 +0000494 FindExpectedDiags(*CurrentPreprocessor, ED);
Daniel Dunbar221c7212009-11-14 07:53:24 +0000495
496 // Check that the expected diagnostics occurred.
497 NumErrors += CheckResults(Diags, CurrentPreprocessor->getSourceManager(),
Chris Lattner60909e12010-04-28 20:02:30 +0000498 *Buffer, ED);
Daniel Dunbar221c7212009-11-14 07:53:24 +0000499 } else {
500 NumErrors += (PrintProblem(Diags, 0,
501 Buffer->err_begin(), Buffer->err_end(),
502 "error", false) +
503 PrintProblem(Diags, 0,
504 Buffer->warn_begin(), Buffer->warn_end(),
505 "warn", false) +
506 PrintProblem(Diags, 0,
507 Buffer->note_begin(), Buffer->note_end(),
508 "note", false));
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000509 }
510
Daniel Dunbar221c7212009-11-14 07:53:24 +0000511 Diags.setClient(CurClient);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000512
513 // Reset the buffer, we have processed all the diagnostics in it.
514 Buffer.reset(new TextDiagnosticBuffer());
515}
Chris Lattner60909e12010-04-28 20:02:30 +0000516
517Directive* Directive::Create(bool RegexKind, const SourceLocation &Location,
518 const std::string &Text, unsigned Count) {
519 if (RegexKind)
520 return new RegexDirective(Location, Text, Count);
521 return new StandardDirective(Location, Text, Count);
522}