blob: a9378a117d6bd14f59b49d7c8ed200bca7e039e1 [file] [log] [blame]
David Blaikie621bc692011-09-26 00:38:03 +00001//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +00002//
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
Jordan Rose78541c42012-07-11 19:58:23 +000014#include "clang/Basic/FileManager.h"
David Blaikie621bc692011-09-26 00:38:03 +000015#include "clang/Frontend/VerifyDiagnosticConsumer.h"
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000016#include "clang/Frontend/FrontendDiagnostic.h"
17#include "clang/Frontend/TextDiagnosticBuffer.h"
Jordan Rose7c304f52012-08-10 01:06:16 +000018#include "clang/Lex/HeaderSearch.h"
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000019#include "clang/Lex/Preprocessor.h"
20#include "llvm/ADT/SmallString.h"
Chris Lattner60909e12010-04-28 20:02:30 +000021#include "llvm/Support/Regex.h"
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000022#include "llvm/Support/raw_ostream.h"
Joerg Sonnenberger7094dee2012-08-10 10:58:18 +000023#include <cctype>
Anna Zaksc035e092011-12-15 02:58:00 +000024
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000025using namespace clang;
Jordan Rose4313c012012-07-10 02:56:15 +000026typedef VerifyDiagnosticConsumer::Directive Directive;
27typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
28typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000029
David Blaikie621bc692011-09-26 00:38:03 +000030VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
Jordan Rose7c304f52012-08-10 01:06:16 +000031 : Diags(_Diags),
32 PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
33 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
34 ActiveSourceFiles(0)
Douglas Gregor78243652011-09-13 01:26:44 +000035{
36 Diags.takeClient();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000037}
38
David Blaikie621bc692011-09-26 00:38:03 +000039VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
Jordan Rose7c304f52012-08-10 01:06:16 +000040 assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
41 assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
Douglas Gregor78243652011-09-13 01:26:44 +000042 CheckDiagnostics();
43 Diags.takeClient();
44 if (OwnsPrimaryClient)
45 delete PrimaryClient;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000046}
47
Jordan Rose7c304f52012-08-10 01:06:16 +000048#ifndef NDEBUG
49namespace {
50class VerifyFileTracker : public PPCallbacks {
51 typedef VerifyDiagnosticConsumer::FilesParsedForDirectivesSet ListType;
52 ListType &FilesList;
53 SourceManager &SM;
54
55public:
56 VerifyFileTracker(ListType &FilesList, SourceManager &SM)
57 : FilesList(FilesList), SM(SM) { }
58
59 /// \brief Hook into the preprocessor and update the list of parsed
60 /// files when the preprocessor indicates a new file is entered.
61 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
62 SrcMgr::CharacteristicKind FileType,
63 FileID PrevFID) {
64 if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(Loc)))
65 FilesList.insert(E);
66 }
67};
68} // End anonymous namespace.
69#endif
70
David Blaikie78ad0b92011-09-25 23:39:51 +000071// DiagnosticConsumer interface.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000072
David Blaikie621bc692011-09-26 00:38:03 +000073void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
Douglas Gregor1f6b2b52012-01-20 16:28:04 +000074 const Preprocessor *PP) {
Jordan Rose7c304f52012-08-10 01:06:16 +000075 // Attach comment handler on first invocation.
76 if (++ActiveSourceFiles == 1) {
77 if (PP) {
78 CurrentPreprocessor = PP;
79 const_cast<Preprocessor*>(PP)->addCommentHandler(this);
80#ifndef NDEBUG
81 VerifyFileTracker *V = new VerifyFileTracker(FilesParsedForDirectives,
82 PP->getSourceManager());
83 const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
84#endif
85 }
86 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000087
Jordan Rose7c304f52012-08-10 01:06:16 +000088 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000089 PrimaryClient->BeginSourceFile(LangOpts, PP);
90}
91
David Blaikie621bc692011-09-26 00:38:03 +000092void VerifyDiagnosticConsumer::EndSourceFile() {
Jordan Rose7c304f52012-08-10 01:06:16 +000093 assert(ActiveSourceFiles && "No active source files!");
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000094 PrimaryClient->EndSourceFile();
95
Jordan Rose7c304f52012-08-10 01:06:16 +000096 // Detach comment handler once last active source file completed.
97 if (--ActiveSourceFiles == 0) {
98 if (CurrentPreprocessor)
99 const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
100
101 // Check diagnostics once last file completed.
102 CheckDiagnostics();
103 CurrentPreprocessor = 0;
104 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000105}
Daniel Dunbar221c7212009-11-14 07:53:24 +0000106
David Blaikie621bc692011-09-26 00:38:03 +0000107void VerifyDiagnosticConsumer::HandleDiagnostic(
David Blaikie40847cf2011-09-26 01:18:08 +0000108 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000109#ifndef NDEBUG
Jordan Rose78541c42012-07-11 19:58:23 +0000110 if (Info.hasSourceManager()) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000111 FileID FID = Info.getSourceManager().getFileID(Info.getLocation());
112 if (!FID.isInvalid())
113 FilesWithDiagnostics.insert(FID);
Axel Naumann01231612011-07-25 19:18:12 +0000114 }
Jordan Rose7c304f52012-08-10 01:06:16 +0000115#endif
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000116 // Send the diagnostic to the buffer, we will check it once we reach the end
117 // of the source file (or are destructed).
118 Buffer->HandleDiagnostic(DiagLevel, Info);
119}
120
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000121//===----------------------------------------------------------------------===//
122// Checking diagnostics implementation.
123//===----------------------------------------------------------------------===//
124
125typedef TextDiagnosticBuffer::DiagList DiagList;
126typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
127
Chris Lattner60909e12010-04-28 20:02:30 +0000128namespace {
129
Chris Lattner60909e12010-04-28 20:02:30 +0000130/// StandardDirective - Directive with string matching.
131///
132class StandardDirective : public Directive {
133public:
Jordan Roseaa48fe82012-07-10 02:57:03 +0000134 StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000135 StringRef Text, unsigned Min, unsigned Max)
136 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000137
138 virtual bool isValid(std::string &Error) {
139 // all strings are considered valid; even empty ones
140 return true;
141 }
142
Jordan Rose4313c012012-07-10 02:56:15 +0000143 virtual bool match(StringRef S) {
144 return S.find(Text) != StringRef::npos;
Chris Lattner60909e12010-04-28 20:02:30 +0000145 }
146};
147
148/// RegexDirective - Directive with regular-expression matching.
149///
150class RegexDirective : public Directive {
151public:
Jordan Roseaa48fe82012-07-10 02:57:03 +0000152 RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000153 StringRef Text, unsigned Min, unsigned Max)
154 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000155
156 virtual bool isValid(std::string &Error) {
157 if (Regex.isValid(Error))
158 return true;
159 return false;
160 }
161
Jordan Rose4313c012012-07-10 02:56:15 +0000162 virtual bool match(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000163 return Regex.match(S);
164 }
165
166private:
167 llvm::Regex Regex;
168};
169
Chris Lattner60909e12010-04-28 20:02:30 +0000170class ParseHelper
171{
172public:
Jordan Rose78541c42012-07-11 19:58:23 +0000173 ParseHelper(StringRef S)
174 : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000175
176 // Return true if string literal is next.
Chris Lattner5f9e2722011-07-23 10:55:15 +0000177 bool Next(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000178 P = C;
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000179 PEnd = C + S.size();
Chris Lattner60909e12010-04-28 20:02:30 +0000180 if (PEnd > End)
181 return false;
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000182 return !memcmp(P, S.data(), S.size());
Chris Lattner60909e12010-04-28 20:02:30 +0000183 }
184
185 // Return true if number is next.
186 // Output N only if number is next.
187 bool Next(unsigned &N) {
188 unsigned TMP = 0;
189 P = C;
190 for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
191 TMP *= 10;
192 TMP += P[0] - '0';
193 }
194 if (P == C)
195 return false;
196 PEnd = P;
197 N = TMP;
198 return true;
199 }
200
201 // Return true if string literal is found.
202 // When true, P marks begin-position of S in content.
Chris Lattner5f9e2722011-07-23 10:55:15 +0000203 bool Search(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000204 P = std::search(C, End, S.begin(), S.end());
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000205 PEnd = P + S.size();
Chris Lattner60909e12010-04-28 20:02:30 +0000206 return P != End;
207 }
208
209 // Advance 1-past previous next/search.
210 // Behavior is undefined if previous next/search failed.
211 bool Advance() {
212 C = PEnd;
213 return C < End;
214 }
215
216 // Skip zero or more whitespace.
217 void SkipWhitespace() {
218 for (; C < End && isspace(*C); ++C)
219 ;
220 }
221
222 // Return true if EOF reached.
223 bool Done() {
224 return !(C < End);
225 }
226
227 const char * const Begin; // beginning of expected content
228 const char * const End; // end of expected content (1-past)
229 const char *C; // position of next char in content
230 const char *P;
231
232private:
233 const char *PEnd; // previous next/search subject end (1-past)
234};
235
236} // namespace anonymous
237
238/// ParseDirective - Go through the comment and see if it indicates expected
239/// diagnostics. If so, then put them in the appropriate directive list.
240///
Jordan Rose78541c42012-07-11 19:58:23 +0000241/// Returns true if any valid directives were found.
Jordan Rose7c304f52012-08-10 01:06:16 +0000242static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
Jordan Rose4313c012012-07-10 02:56:15 +0000243 SourceLocation Pos, DiagnosticsEngine &Diags) {
Chris Lattner60909e12010-04-28 20:02:30 +0000244 // A single comment may contain multiple directives.
Jordan Rose78541c42012-07-11 19:58:23 +0000245 bool FoundDirective = false;
246 for (ParseHelper PH(S); !PH.Done();) {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000247 // Search for token: expected
Chris Lattner60909e12010-04-28 20:02:30 +0000248 if (!PH.Search("expected"))
249 break;
250 PH.Advance();
251
Jordan Roseaa48fe82012-07-10 02:57:03 +0000252 // Next token: -
Chris Lattner60909e12010-04-28 20:02:30 +0000253 if (!PH.Next("-"))
254 continue;
255 PH.Advance();
256
Jordan Roseaa48fe82012-07-10 02:57:03 +0000257 // Next token: { error | warning | note }
Chris Lattner60909e12010-04-28 20:02:30 +0000258 DirectiveList* DL = NULL;
259 if (PH.Next("error"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000260 DL = ED ? &ED->Errors : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000261 else if (PH.Next("warning"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000262 DL = ED ? &ED->Warnings : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000263 else if (PH.Next("note"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000264 DL = ED ? &ED->Notes : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000265 else
266 continue;
267 PH.Advance();
268
Jordan Rose7c304f52012-08-10 01:06:16 +0000269 // If a directive has been found but we're not interested
270 // in storing the directive information, return now.
271 if (!DL)
272 return true;
273
Jordan Roseaa48fe82012-07-10 02:57:03 +0000274 // Default directive kind.
Chris Lattner60909e12010-04-28 20:02:30 +0000275 bool RegexKind = false;
276 const char* KindStr = "string";
277
Jordan Roseaa48fe82012-07-10 02:57:03 +0000278 // Next optional token: -
Chris Lattner60909e12010-04-28 20:02:30 +0000279 if (PH.Next("-re")) {
280 PH.Advance();
281 RegexKind = true;
282 KindStr = "regex";
283 }
284
Jordan Roseaa48fe82012-07-10 02:57:03 +0000285 // Next optional token: @
286 SourceLocation ExpectedLoc;
287 if (!PH.Next("@")) {
288 ExpectedLoc = Pos;
289 } else {
290 PH.Advance();
291 unsigned Line = 0;
292 bool FoundPlus = PH.Next("+");
293 if (FoundPlus || PH.Next("-")) {
294 // Relative to current line.
295 PH.Advance();
296 bool Invalid = false;
297 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
298 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
299 if (FoundPlus) ExpectedLine += Line;
300 else ExpectedLine -= Line;
301 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
302 }
303 } else {
304 // Absolute line number.
305 if (PH.Next(Line) && Line > 0)
306 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
307 }
308
309 if (ExpectedLoc.isInvalid()) {
310 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
311 diag::err_verify_missing_line) << KindStr;
312 continue;
313 }
314 PH.Advance();
315 }
316
317 // Skip optional whitespace.
Chris Lattner60909e12010-04-28 20:02:30 +0000318 PH.SkipWhitespace();
319
Jordan Roseaa48fe82012-07-10 02:57:03 +0000320 // Next optional token: positive integer or a '+'.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000321 unsigned Min = 1;
322 unsigned Max = 1;
323 if (PH.Next(Min)) {
Chris Lattner60909e12010-04-28 20:02:30 +0000324 PH.Advance();
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000325 // A positive integer can be followed by a '+' meaning min
326 // or more, or by a '-' meaning a range from min to max.
327 if (PH.Next("+")) {
328 Max = Directive::MaxCount;
329 PH.Advance();
330 } else if (PH.Next("-")) {
331 PH.Advance();
332 if (!PH.Next(Max) || Max < Min) {
333 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
334 diag::err_verify_invalid_range) << KindStr;
335 continue;
336 }
337 PH.Advance();
338 } else {
339 Max = Min;
340 }
341 } else if (PH.Next("+")) {
342 // '+' on its own means "1 or more".
343 Max = Directive::MaxCount;
Anna Zaks2135ebb2011-12-15 02:28:16 +0000344 PH.Advance();
345 }
Chris Lattner60909e12010-04-28 20:02:30 +0000346
Jordan Roseaa48fe82012-07-10 02:57:03 +0000347 // Skip optional whitespace.
Chris Lattner60909e12010-04-28 20:02:30 +0000348 PH.SkipWhitespace();
349
Jordan Roseaa48fe82012-07-10 02:57:03 +0000350 // Next token: {{
Chris Lattner60909e12010-04-28 20:02:30 +0000351 if (!PH.Next("{{")) {
Jordan Rose4313c012012-07-10 02:56:15 +0000352 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
353 diag::err_verify_missing_start) << KindStr;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000354 continue;
355 }
Chris Lattner60909e12010-04-28 20:02:30 +0000356 PH.Advance();
357 const char* const ContentBegin = PH.C; // mark content begin
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000358
Jordan Roseaa48fe82012-07-10 02:57:03 +0000359 // Search for token: }}
Chris Lattner60909e12010-04-28 20:02:30 +0000360 if (!PH.Search("}}")) {
Jordan Rose4313c012012-07-10 02:56:15 +0000361 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
362 diag::err_verify_missing_end) << KindStr;
Chris Lattner60909e12010-04-28 20:02:30 +0000363 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000364 }
Chris Lattner60909e12010-04-28 20:02:30 +0000365 const char* const ContentEnd = PH.P; // mark content end
366 PH.Advance();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000367
Jordan Roseaa48fe82012-07-10 02:57:03 +0000368 // Build directive text; convert \n to newlines.
Chris Lattner60909e12010-04-28 20:02:30 +0000369 std::string Text;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000370 StringRef NewlineStr = "\\n";
371 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
Chris Lattner60909e12010-04-28 20:02:30 +0000372 size_t CPos = 0;
373 size_t FPos;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000374 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
Chris Lattner60909e12010-04-28 20:02:30 +0000375 Text += Content.substr(CPos, FPos-CPos);
376 Text += '\n';
377 CPos = FPos + NewlineStr.size();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000378 }
Chris Lattner60909e12010-04-28 20:02:30 +0000379 if (Text.empty())
380 Text.assign(ContentBegin, ContentEnd);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000381
Jordan Roseaa48fe82012-07-10 02:57:03 +0000382 // Construct new directive.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000383 Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
384 Min, Max);
Chris Lattner60909e12010-04-28 20:02:30 +0000385 std::string Error;
Jordan Rose78541c42012-07-11 19:58:23 +0000386 if (D->isValid(Error)) {
Chris Lattner60909e12010-04-28 20:02:30 +0000387 DL->push_back(D);
Jordan Rose78541c42012-07-11 19:58:23 +0000388 FoundDirective = true;
389 } else {
Jordan Rose4313c012012-07-10 02:56:15 +0000390 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
391 diag::err_verify_invalid_content)
Chris Lattner60909e12010-04-28 20:02:30 +0000392 << KindStr << Error;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000393 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000394 }
Jordan Rose78541c42012-07-11 19:58:23 +0000395
396 return FoundDirective;
397}
398
399/// HandleComment - Hook into the preprocessor and extract comments containing
400/// expected errors and warnings.
401bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
402 SourceRange Comment) {
403 SourceManager &SM = PP.getSourceManager();
404 SourceLocation CommentBegin = Comment.getBegin();
405
406 const char *CommentRaw = SM.getCharacterData(CommentBegin);
407 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
408
409 if (C.empty())
410 return false;
411
412 // Fold any "\<EOL>" sequences
413 size_t loc = C.find('\\');
414 if (loc == StringRef::npos) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000415 ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics());
Jordan Rose78541c42012-07-11 19:58:23 +0000416 return false;
417 }
418
419 std::string C2;
420 C2.reserve(C.size());
421
422 for (size_t last = 0;; loc = C.find('\\', last)) {
423 if (loc == StringRef::npos || loc == C.size()) {
424 C2 += C.substr(last);
425 break;
426 }
427 C2 += C.substr(last, loc-last);
428 last = loc + 1;
429
430 if (C[last] == '\n' || C[last] == '\r') {
431 ++last;
432
433 // Escape \r\n or \n\r, but not \n\n.
434 if (last < C.size())
435 if (C[last] == '\n' || C[last] == '\r')
436 if (C[last] != C[last-1])
437 ++last;
438 } else {
439 // This was just a normal backslash.
440 C2 += '\\';
441 }
442 }
443
444 if (!C2.empty())
Jordan Rose7c304f52012-08-10 01:06:16 +0000445 ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics());
Jordan Rose78541c42012-07-11 19:58:23 +0000446 return false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000447}
448
Jordan Rose7c304f52012-08-10 01:06:16 +0000449#ifndef NDEBUG
450/// \brief Lex the specified source file to determine whether it contains
451/// any expected-* directives. As a Lexer is used rather than a full-blown
452/// Preprocessor, directives inside skipped #if blocks will still be found.
453///
454/// \return true if any directives were found.
455static bool findDirectives(const Preprocessor &PP, FileID FID) {
Axel Naumann01231612011-07-25 19:18:12 +0000456 // Create a raw lexer to pull all the comments out of FID.
457 if (FID.isInvalid())
Jordan Rose7c304f52012-08-10 01:06:16 +0000458 return false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000459
Axel Naumann01231612011-07-25 19:18:12 +0000460 SourceManager& SM = PP.getSourceManager();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000461 // Create a lexer to lex all the tokens of the main file in raw mode.
Chris Lattner6e290142009-11-30 04:18:44 +0000462 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
David Blaikie4e4d0842012-03-11 07:00:24 +0000463 Lexer RawLex(FID, FromFile, SM, PP.getLangOpts());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000464
465 // Return comments as tokens, this is how we find expected diagnostics.
466 RawLex.SetCommentRetentionState(true);
467
468 Token Tok;
469 Tok.setKind(tok::comment);
Jordan Rose7c304f52012-08-10 01:06:16 +0000470 bool Found = false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000471 while (Tok.isNot(tok::eof)) {
472 RawLex.Lex(Tok);
473 if (!Tok.is(tok::comment)) continue;
474
475 std::string Comment = PP.getSpelling(Tok);
476 if (Comment.empty()) continue;
477
Chris Lattner60909e12010-04-28 20:02:30 +0000478 // Find all expected errors/warnings/notes.
Jordan Rose7c304f52012-08-10 01:06:16 +0000479 Found |= ParseDirective(Comment, 0, SM, Tok.getLocation(),
480 PP.getDiagnostics());
481 }
482 return Found;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000483}
Jordan Rose7c304f52012-08-10 01:06:16 +0000484#endif // !NDEBUG
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000485
Jordan Roseaa48fe82012-07-10 02:57:03 +0000486/// \brief Takes a list of diagnostics that have been generated but not matched
487/// by an expected-* directive and produces a diagnostic to the user from this.
488static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
489 const_diag_iterator diag_begin,
490 const_diag_iterator diag_end,
491 const char *Kind) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000492 if (diag_begin == diag_end) return 0;
493
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000494 SmallString<256> Fmt;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000495 llvm::raw_svector_ostream OS(Fmt);
496 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
Daniel Dunbar221c7212009-11-14 07:53:24 +0000497 if (I->first.isInvalid() || !SourceMgr)
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000498 OS << "\n (frontend)";
499 else
Chandler Carruth5ef04ee2011-02-23 00:47:48 +0000500 OS << "\n Line " << SourceMgr->getPresumedLineNumber(I->first);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000501 OS << ": " << I->second;
502 }
503
Jordan Rosec6d64a22012-07-11 16:50:36 +0000504 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
Jordan Roseaa48fe82012-07-10 02:57:03 +0000505 << Kind << /*Unexpected=*/true << OS.str();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000506 return std::distance(diag_begin, diag_end);
507}
508
Jordan Roseaa48fe82012-07-10 02:57:03 +0000509/// \brief Takes a list of diagnostics that were expected to have been generated
510/// but were not and produces a diagnostic to the user from this.
511static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
512 DirectiveList &DL, const char *Kind) {
Chris Lattner60909e12010-04-28 20:02:30 +0000513 if (DL.empty())
514 return 0;
515
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000516 SmallString<256> Fmt;
Chris Lattner60909e12010-04-28 20:02:30 +0000517 llvm::raw_svector_ostream OS(Fmt);
518 for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000519 Directive &D = **I;
520 OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
521 if (D.DirectiveLoc != D.DiagnosticLoc)
522 OS << " (directive at "
523 << SourceMgr.getFilename(D.DirectiveLoc) << ":"
524 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
Chris Lattner60909e12010-04-28 20:02:30 +0000525 OS << ": " << D.Text;
526 }
527
Jordan Rosec6d64a22012-07-11 16:50:36 +0000528 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
Jordan Roseaa48fe82012-07-10 02:57:03 +0000529 << Kind << /*Unexpected=*/false << OS.str();
Chris Lattner60909e12010-04-28 20:02:30 +0000530 return DL.size();
531}
532
533/// CheckLists - Compare expected to seen diagnostic lists and return the
534/// the difference between them.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000535///
David Blaikied6471f72011-09-25 23:23:43 +0000536static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
Chris Lattner60909e12010-04-28 20:02:30 +0000537 const char *Label,
538 DirectiveList &Left,
539 const_diag_iterator d2_begin,
540 const_diag_iterator d2_end) {
541 DirectiveList LeftOnly;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000542 DiagList Right(d2_begin, d2_end);
543
Chris Lattner60909e12010-04-28 20:02:30 +0000544 for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
545 Directive& D = **I;
Jordan Roseaa48fe82012-07-10 02:57:03 +0000546 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000547
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000548 for (unsigned i = 0; i < D.Max; ++i) {
Chris Lattner60909e12010-04-28 20:02:30 +0000549 DiagList::iterator II, IE;
550 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
Chandler Carruth5ef04ee2011-02-23 00:47:48 +0000551 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
Chris Lattner60909e12010-04-28 20:02:30 +0000552 if (LineNo1 != LineNo2)
553 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000554
Chris Lattner60909e12010-04-28 20:02:30 +0000555 const std::string &RightText = II->second;
Jordan Rose4313c012012-07-10 02:56:15 +0000556 if (D.match(RightText))
Chris Lattner60909e12010-04-28 20:02:30 +0000557 break;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000558 }
Chris Lattner60909e12010-04-28 20:02:30 +0000559 if (II == IE) {
560 // Not found.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000561 if (i >= D.Min) break;
Chris Lattner60909e12010-04-28 20:02:30 +0000562 LeftOnly.push_back(*I);
563 } else {
564 // Found. The same cannot be found twice.
565 Right.erase(II);
566 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000567 }
568 }
569 // Now all that's left in Right are those that were not matched.
Jordan Roseaa48fe82012-07-10 02:57:03 +0000570 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
571 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
NAKAMURA Takumiad646842011-12-17 13:00:31 +0000572 return num;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000573}
574
575/// CheckResults - This compares the expected results to those that
576/// were actually reported. It emits any discrepencies. Return "true" if there
577/// were problems. Return "false" otherwise.
578///
David Blaikied6471f72011-09-25 23:23:43 +0000579static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000580 const TextDiagnosticBuffer &Buffer,
Chris Lattner60909e12010-04-28 20:02:30 +0000581 ExpectedData &ED) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000582 // We want to capture the delta between what was expected and what was
583 // seen.
584 //
585 // Expected \ Seen - set expected but not seen
586 // Seen \ Expected - set seen but not expected
587 unsigned NumProblems = 0;
588
589 // See if there are error mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000590 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
591 Buffer.err_begin(), Buffer.err_end());
Daniel Dunbar221c7212009-11-14 07:53:24 +0000592
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000593 // See if there are warning mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000594 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
595 Buffer.warn_begin(), Buffer.warn_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000596
597 // See if there are note mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000598 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
599 Buffer.note_begin(), Buffer.note_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000600
601 return NumProblems;
602}
603
David Blaikie621bc692011-09-26 00:38:03 +0000604void VerifyDiagnosticConsumer::CheckDiagnostics() {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000605 // Ensure any diagnostics go to the primary client.
Douglas Gregor78243652011-09-13 01:26:44 +0000606 bool OwnsCurClient = Diags.ownsClient();
David Blaikie78ad0b92011-09-25 23:39:51 +0000607 DiagnosticConsumer *CurClient = Diags.takeClient();
Douglas Gregor78243652011-09-13 01:26:44 +0000608 Diags.setClient(PrimaryClient, false);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000609
610 // If we have a preprocessor, scan the source for expected diagnostic
611 // markers. If not then any diagnostics are unexpected.
612 if (CurrentPreprocessor) {
Axel Naumann01231612011-07-25 19:18:12 +0000613 SourceManager &SM = CurrentPreprocessor->getSourceManager();
Jordan Rose7c304f52012-08-10 01:06:16 +0000614
615#ifndef NDEBUG
616 // In a debug build, scan through any files that may have been missed
617 // during parsing and issue a fatal error if directives are contained
618 // within these files. If a fatal error occurs, this suggests that
619 // this file is being parsed separately from the main file.
620 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
Jordan Rose78541c42012-07-11 19:58:23 +0000621 for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(),
622 End = FilesWithDiagnostics.end();
623 I != End; ++I) {
624 const FileEntry *E = SM.getFileEntryForID(*I);
Jordan Rose7c304f52012-08-10 01:06:16 +0000625 // Don't check files already parsed or those handled as modules.
626 if (E && (FilesParsedForDirectives.count(E)
627 || HS.findModuleForHeader(E)))
628 continue;
629
630 if (findDirectives(*CurrentPreprocessor, *I))
631 llvm::report_fatal_error(Twine("-verify directives found after rather"
632 " than during normal parsing of ",
633 StringRef(E ? E->getName() : "(unknown)")));
Axel Naumann84c05e32011-08-24 13:36:19 +0000634 }
Jordan Rose7c304f52012-08-10 01:06:16 +0000635#endif
Daniel Dunbar221c7212009-11-14 07:53:24 +0000636
637 // Check that the expected diagnostics occurred.
Axel Naumann01231612011-07-25 19:18:12 +0000638 NumErrors += CheckResults(Diags, SM, *Buffer, ED);
Daniel Dunbar221c7212009-11-14 07:53:24 +0000639 } else {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000640 NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
641 Buffer->err_end(), "error") +
642 PrintUnexpected(Diags, 0, Buffer->warn_begin(),
643 Buffer->warn_end(), "warn") +
644 PrintUnexpected(Diags, 0, Buffer->note_begin(),
645 Buffer->note_end(), "note"));
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000646 }
647
Douglas Gregorbdbb0042010-08-18 22:29:43 +0000648 Diags.takeClient();
Douglas Gregor78243652011-09-13 01:26:44 +0000649 Diags.setClient(CurClient, OwnsCurClient);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000650
651 // Reset the buffer, we have processed all the diagnostics in it.
652 Buffer.reset(new TextDiagnosticBuffer());
Axel Naumanne445e5d2012-07-10 16:24:07 +0000653 ED.Errors.clear();
654 ED.Warnings.clear();
655 ED.Notes.clear();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000656}
Chris Lattner60909e12010-04-28 20:02:30 +0000657
Douglas Gregoraee526e2011-09-29 00:38:00 +0000658DiagnosticConsumer *
659VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
660 if (!Diags.getClient())
661 Diags.setClient(PrimaryClient->clone(Diags));
662
663 return new VerifyDiagnosticConsumer(Diags);
664}
665
Jordan Roseaa48fe82012-07-10 02:57:03 +0000666Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
667 SourceLocation DiagnosticLoc, StringRef Text,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000668 unsigned Min, unsigned Max) {
Chris Lattner60909e12010-04-28 20:02:30 +0000669 if (RegexKind)
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000670 return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
671 return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
Chris Lattner60909e12010-04-28 20:02:30 +0000672}