blob: 71c3f6bce56f69083d83748023010bdc4fca8fac [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"
Anna Zaksc035e092011-12-15 02:58:00 +000023
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000024using namespace clang;
Jordan Rose4313c012012-07-10 02:56:15 +000025typedef VerifyDiagnosticConsumer::Directive Directive;
26typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
27typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000028
David Blaikie621bc692011-09-26 00:38:03 +000029VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
Jordan Rose7c304f52012-08-10 01:06:16 +000030 : Diags(_Diags),
31 PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
32 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
33 ActiveSourceFiles(0)
Douglas Gregor78243652011-09-13 01:26:44 +000034{
35 Diags.takeClient();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000036}
37
David Blaikie621bc692011-09-26 00:38:03 +000038VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
Jordan Rose7c304f52012-08-10 01:06:16 +000039 assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
40 assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
Douglas Gregor78243652011-09-13 01:26:44 +000041 CheckDiagnostics();
42 Diags.takeClient();
43 if (OwnsPrimaryClient)
44 delete PrimaryClient;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000045}
46
Jordan Rose7c304f52012-08-10 01:06:16 +000047#ifndef NDEBUG
48namespace {
49class VerifyFileTracker : public PPCallbacks {
50 typedef VerifyDiagnosticConsumer::FilesParsedForDirectivesSet ListType;
51 ListType &FilesList;
52 SourceManager &SM;
53
54public:
55 VerifyFileTracker(ListType &FilesList, SourceManager &SM)
56 : FilesList(FilesList), SM(SM) { }
57
58 /// \brief Hook into the preprocessor and update the list of parsed
59 /// files when the preprocessor indicates a new file is entered.
60 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
61 SrcMgr::CharacteristicKind FileType,
62 FileID PrevFID) {
63 if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(Loc)))
64 FilesList.insert(E);
65 }
66};
67} // End anonymous namespace.
68#endif
69
David Blaikie78ad0b92011-09-25 23:39:51 +000070// DiagnosticConsumer interface.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000071
David Blaikie621bc692011-09-26 00:38:03 +000072void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
Douglas Gregor1f6b2b52012-01-20 16:28:04 +000073 const Preprocessor *PP) {
Jordan Rose7c304f52012-08-10 01:06:16 +000074 // Attach comment handler on first invocation.
75 if (++ActiveSourceFiles == 1) {
76 if (PP) {
77 CurrentPreprocessor = PP;
78 const_cast<Preprocessor*>(PP)->addCommentHandler(this);
79#ifndef NDEBUG
80 VerifyFileTracker *V = new VerifyFileTracker(FilesParsedForDirectives,
81 PP->getSourceManager());
82 const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
83#endif
84 }
85 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000086
Jordan Rose7c304f52012-08-10 01:06:16 +000087 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000088 PrimaryClient->BeginSourceFile(LangOpts, PP);
89}
90
David Blaikie621bc692011-09-26 00:38:03 +000091void VerifyDiagnosticConsumer::EndSourceFile() {
Jordan Rose7c304f52012-08-10 01:06:16 +000092 assert(ActiveSourceFiles && "No active source files!");
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +000093 PrimaryClient->EndSourceFile();
94
Jordan Rose7c304f52012-08-10 01:06:16 +000095 // Detach comment handler once last active source file completed.
96 if (--ActiveSourceFiles == 0) {
97 if (CurrentPreprocessor)
98 const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
99
100 // Check diagnostics once last file completed.
101 CheckDiagnostics();
102 CurrentPreprocessor = 0;
103 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000104}
Daniel Dunbar221c7212009-11-14 07:53:24 +0000105
David Blaikie621bc692011-09-26 00:38:03 +0000106void VerifyDiagnosticConsumer::HandleDiagnostic(
David Blaikie40847cf2011-09-26 01:18:08 +0000107 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000108#ifndef NDEBUG
Jordan Rose78541c42012-07-11 19:58:23 +0000109 if (Info.hasSourceManager()) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000110 FileID FID = Info.getSourceManager().getFileID(Info.getLocation());
111 if (!FID.isInvalid())
112 FilesWithDiagnostics.insert(FID);
Axel Naumann01231612011-07-25 19:18:12 +0000113 }
Jordan Rose7c304f52012-08-10 01:06:16 +0000114#endif
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000115 // Send the diagnostic to the buffer, we will check it once we reach the end
116 // of the source file (or are destructed).
117 Buffer->HandleDiagnostic(DiagLevel, Info);
118}
119
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000120//===----------------------------------------------------------------------===//
121// Checking diagnostics implementation.
122//===----------------------------------------------------------------------===//
123
124typedef TextDiagnosticBuffer::DiagList DiagList;
125typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
126
Chris Lattner60909e12010-04-28 20:02:30 +0000127namespace {
128
Chris Lattner60909e12010-04-28 20:02:30 +0000129/// StandardDirective - Directive with string matching.
130///
131class StandardDirective : public Directive {
132public:
Jordan Roseaa48fe82012-07-10 02:57:03 +0000133 StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000134 StringRef Text, unsigned Min, unsigned Max)
135 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000136
137 virtual bool isValid(std::string &Error) {
138 // all strings are considered valid; even empty ones
139 return true;
140 }
141
Jordan Rose4313c012012-07-10 02:56:15 +0000142 virtual bool match(StringRef S) {
143 return S.find(Text) != StringRef::npos;
Chris Lattner60909e12010-04-28 20:02:30 +0000144 }
145};
146
147/// RegexDirective - Directive with regular-expression matching.
148///
149class RegexDirective : public Directive {
150public:
Jordan Roseaa48fe82012-07-10 02:57:03 +0000151 RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000152 StringRef Text, unsigned Min, unsigned Max)
153 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000154
155 virtual bool isValid(std::string &Error) {
156 if (Regex.isValid(Error))
157 return true;
158 return false;
159 }
160
Jordan Rose4313c012012-07-10 02:56:15 +0000161 virtual bool match(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000162 return Regex.match(S);
163 }
164
165private:
166 llvm::Regex Regex;
167};
168
Chris Lattner60909e12010-04-28 20:02:30 +0000169class ParseHelper
170{
171public:
Jordan Rose78541c42012-07-11 19:58:23 +0000172 ParseHelper(StringRef S)
173 : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
Chris Lattner60909e12010-04-28 20:02:30 +0000174
175 // Return true if string literal is next.
Chris Lattner5f9e2722011-07-23 10:55:15 +0000176 bool Next(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000177 P = C;
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000178 PEnd = C + S.size();
Chris Lattner60909e12010-04-28 20:02:30 +0000179 if (PEnd > End)
180 return false;
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000181 return !memcmp(P, S.data(), S.size());
Chris Lattner60909e12010-04-28 20:02:30 +0000182 }
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.
Chris Lattner5f9e2722011-07-23 10:55:15 +0000202 bool Search(StringRef S) {
Chris Lattner60909e12010-04-28 20:02:30 +0000203 P = std::search(C, End, S.begin(), S.end());
Benjamin Kramer0080f0c2010-09-01 17:28:48 +0000204 PEnd = P + S.size();
Chris Lattner60909e12010-04-28 20:02:30 +0000205 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///
Jordan Rose78541c42012-07-11 19:58:23 +0000240/// Returns true if any valid directives were found.
Jordan Rose7c304f52012-08-10 01:06:16 +0000241static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
Jordan Rose4313c012012-07-10 02:56:15 +0000242 SourceLocation Pos, DiagnosticsEngine &Diags) {
Chris Lattner60909e12010-04-28 20:02:30 +0000243 // A single comment may contain multiple directives.
Jordan Rose78541c42012-07-11 19:58:23 +0000244 bool FoundDirective = false;
245 for (ParseHelper PH(S); !PH.Done();) {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000246 // Search for token: expected
Chris Lattner60909e12010-04-28 20:02:30 +0000247 if (!PH.Search("expected"))
248 break;
249 PH.Advance();
250
Jordan Roseaa48fe82012-07-10 02:57:03 +0000251 // Next token: -
Chris Lattner60909e12010-04-28 20:02:30 +0000252 if (!PH.Next("-"))
253 continue;
254 PH.Advance();
255
Jordan Roseaa48fe82012-07-10 02:57:03 +0000256 // Next token: { error | warning | note }
Chris Lattner60909e12010-04-28 20:02:30 +0000257 DirectiveList* DL = NULL;
258 if (PH.Next("error"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000259 DL = ED ? &ED->Errors : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000260 else if (PH.Next("warning"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000261 DL = ED ? &ED->Warnings : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000262 else if (PH.Next("note"))
Jordan Rose7c304f52012-08-10 01:06:16 +0000263 DL = ED ? &ED->Notes : NULL;
Chris Lattner60909e12010-04-28 20:02:30 +0000264 else
265 continue;
266 PH.Advance();
267
Jordan Rose7c304f52012-08-10 01:06:16 +0000268 // If a directive has been found but we're not interested
269 // in storing the directive information, return now.
270 if (!DL)
271 return true;
272
Jordan Roseaa48fe82012-07-10 02:57:03 +0000273 // Default directive kind.
Chris Lattner60909e12010-04-28 20:02:30 +0000274 bool RegexKind = false;
275 const char* KindStr = "string";
276
Jordan Roseaa48fe82012-07-10 02:57:03 +0000277 // Next optional token: -
Chris Lattner60909e12010-04-28 20:02:30 +0000278 if (PH.Next("-re")) {
279 PH.Advance();
280 RegexKind = true;
281 KindStr = "regex";
282 }
283
Jordan Roseaa48fe82012-07-10 02:57:03 +0000284 // Next optional token: @
285 SourceLocation ExpectedLoc;
286 if (!PH.Next("@")) {
287 ExpectedLoc = Pos;
288 } else {
289 PH.Advance();
290 unsigned Line = 0;
291 bool FoundPlus = PH.Next("+");
292 if (FoundPlus || PH.Next("-")) {
293 // Relative to current line.
294 PH.Advance();
295 bool Invalid = false;
296 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
297 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
298 if (FoundPlus) ExpectedLine += Line;
299 else ExpectedLine -= Line;
300 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
301 }
302 } else {
303 // Absolute line number.
304 if (PH.Next(Line) && Line > 0)
305 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
306 }
307
308 if (ExpectedLoc.isInvalid()) {
309 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
310 diag::err_verify_missing_line) << KindStr;
311 continue;
312 }
313 PH.Advance();
314 }
315
316 // Skip optional whitespace.
Chris Lattner60909e12010-04-28 20:02:30 +0000317 PH.SkipWhitespace();
318
Jordan Roseaa48fe82012-07-10 02:57:03 +0000319 // Next optional token: positive integer or a '+'.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000320 unsigned Min = 1;
321 unsigned Max = 1;
322 if (PH.Next(Min)) {
Chris Lattner60909e12010-04-28 20:02:30 +0000323 PH.Advance();
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000324 // A positive integer can be followed by a '+' meaning min
325 // or more, or by a '-' meaning a range from min to max.
326 if (PH.Next("+")) {
327 Max = Directive::MaxCount;
328 PH.Advance();
329 } else if (PH.Next("-")) {
330 PH.Advance();
331 if (!PH.Next(Max) || Max < Min) {
332 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
333 diag::err_verify_invalid_range) << KindStr;
334 continue;
335 }
336 PH.Advance();
337 } else {
338 Max = Min;
339 }
340 } else if (PH.Next("+")) {
341 // '+' on its own means "1 or more".
342 Max = Directive::MaxCount;
Anna Zaks2135ebb2011-12-15 02:28:16 +0000343 PH.Advance();
344 }
Chris Lattner60909e12010-04-28 20:02:30 +0000345
Jordan Roseaa48fe82012-07-10 02:57:03 +0000346 // Skip optional whitespace.
Chris Lattner60909e12010-04-28 20:02:30 +0000347 PH.SkipWhitespace();
348
Jordan Roseaa48fe82012-07-10 02:57:03 +0000349 // Next token: {{
Chris Lattner60909e12010-04-28 20:02:30 +0000350 if (!PH.Next("{{")) {
Jordan Rose4313c012012-07-10 02:56:15 +0000351 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
352 diag::err_verify_missing_start) << KindStr;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000353 continue;
354 }
Chris Lattner60909e12010-04-28 20:02:30 +0000355 PH.Advance();
356 const char* const ContentBegin = PH.C; // mark content begin
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000357
Jordan Roseaa48fe82012-07-10 02:57:03 +0000358 // Search for token: }}
Chris Lattner60909e12010-04-28 20:02:30 +0000359 if (!PH.Search("}}")) {
Jordan Rose4313c012012-07-10 02:56:15 +0000360 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
361 diag::err_verify_missing_end) << KindStr;
Chris Lattner60909e12010-04-28 20:02:30 +0000362 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000363 }
Chris Lattner60909e12010-04-28 20:02:30 +0000364 const char* const ContentEnd = PH.P; // mark content end
365 PH.Advance();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000366
Jordan Roseaa48fe82012-07-10 02:57:03 +0000367 // Build directive text; convert \n to newlines.
Chris Lattner60909e12010-04-28 20:02:30 +0000368 std::string Text;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000369 StringRef NewlineStr = "\\n";
370 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
Chris Lattner60909e12010-04-28 20:02:30 +0000371 size_t CPos = 0;
372 size_t FPos;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000373 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
Chris Lattner60909e12010-04-28 20:02:30 +0000374 Text += Content.substr(CPos, FPos-CPos);
375 Text += '\n';
376 CPos = FPos + NewlineStr.size();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000377 }
Chris Lattner60909e12010-04-28 20:02:30 +0000378 if (Text.empty())
379 Text.assign(ContentBegin, ContentEnd);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000380
Jordan Roseaa48fe82012-07-10 02:57:03 +0000381 // Construct new directive.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000382 Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
383 Min, Max);
Chris Lattner60909e12010-04-28 20:02:30 +0000384 std::string Error;
Jordan Rose78541c42012-07-11 19:58:23 +0000385 if (D->isValid(Error)) {
Chris Lattner60909e12010-04-28 20:02:30 +0000386 DL->push_back(D);
Jordan Rose78541c42012-07-11 19:58:23 +0000387 FoundDirective = true;
388 } else {
Jordan Rose4313c012012-07-10 02:56:15 +0000389 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
390 diag::err_verify_invalid_content)
Chris Lattner60909e12010-04-28 20:02:30 +0000391 << KindStr << Error;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000392 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000393 }
Jordan Rose78541c42012-07-11 19:58:23 +0000394
395 return FoundDirective;
396}
397
398/// HandleComment - Hook into the preprocessor and extract comments containing
399/// expected errors and warnings.
400bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
401 SourceRange Comment) {
402 SourceManager &SM = PP.getSourceManager();
403 SourceLocation CommentBegin = Comment.getBegin();
404
405 const char *CommentRaw = SM.getCharacterData(CommentBegin);
406 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
407
408 if (C.empty())
409 return false;
410
411 // Fold any "\<EOL>" sequences
412 size_t loc = C.find('\\');
413 if (loc == StringRef::npos) {
Jordan Rose7c304f52012-08-10 01:06:16 +0000414 ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics());
Jordan Rose78541c42012-07-11 19:58:23 +0000415 return false;
416 }
417
418 std::string C2;
419 C2.reserve(C.size());
420
421 for (size_t last = 0;; loc = C.find('\\', last)) {
422 if (loc == StringRef::npos || loc == C.size()) {
423 C2 += C.substr(last);
424 break;
425 }
426 C2 += C.substr(last, loc-last);
427 last = loc + 1;
428
429 if (C[last] == '\n' || C[last] == '\r') {
430 ++last;
431
432 // Escape \r\n or \n\r, but not \n\n.
433 if (last < C.size())
434 if (C[last] == '\n' || C[last] == '\r')
435 if (C[last] != C[last-1])
436 ++last;
437 } else {
438 // This was just a normal backslash.
439 C2 += '\\';
440 }
441 }
442
443 if (!C2.empty())
Jordan Rose7c304f52012-08-10 01:06:16 +0000444 ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics());
Jordan Rose78541c42012-07-11 19:58:23 +0000445 return false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000446}
447
Jordan Rose7c304f52012-08-10 01:06:16 +0000448#ifndef NDEBUG
449/// \brief Lex the specified source file to determine whether it contains
450/// any expected-* directives. As a Lexer is used rather than a full-blown
451/// Preprocessor, directives inside skipped #if blocks will still be found.
452///
453/// \return true if any directives were found.
454static bool findDirectives(const Preprocessor &PP, FileID FID) {
Axel Naumann01231612011-07-25 19:18:12 +0000455 // Create a raw lexer to pull all the comments out of FID.
456 if (FID.isInvalid())
Jordan Rose7c304f52012-08-10 01:06:16 +0000457 return false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000458
Axel Naumann01231612011-07-25 19:18:12 +0000459 SourceManager& SM = PP.getSourceManager();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000460 // Create a lexer to lex all the tokens of the main file in raw mode.
Chris Lattner6e290142009-11-30 04:18:44 +0000461 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
David Blaikie4e4d0842012-03-11 07:00:24 +0000462 Lexer RawLex(FID, FromFile, SM, PP.getLangOpts());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000463
464 // Return comments as tokens, this is how we find expected diagnostics.
465 RawLex.SetCommentRetentionState(true);
466
467 Token Tok;
468 Tok.setKind(tok::comment);
Jordan Rose7c304f52012-08-10 01:06:16 +0000469 bool Found = false;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000470 while (Tok.isNot(tok::eof)) {
471 RawLex.Lex(Tok);
472 if (!Tok.is(tok::comment)) continue;
473
474 std::string Comment = PP.getSpelling(Tok);
475 if (Comment.empty()) continue;
476
Chris Lattner60909e12010-04-28 20:02:30 +0000477 // Find all expected errors/warnings/notes.
Jordan Rose7c304f52012-08-10 01:06:16 +0000478 Found |= ParseDirective(Comment, 0, SM, Tok.getLocation(),
479 PP.getDiagnostics());
480 }
481 return Found;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000482}
Jordan Rose7c304f52012-08-10 01:06:16 +0000483#endif // !NDEBUG
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000484
Jordan Roseaa48fe82012-07-10 02:57:03 +0000485/// \brief Takes a list of diagnostics that have been generated but not matched
486/// by an expected-* directive and produces a diagnostic to the user from this.
487static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
488 const_diag_iterator diag_begin,
489 const_diag_iterator diag_end,
490 const char *Kind) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000491 if (diag_begin == diag_end) return 0;
492
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000493 SmallString<256> Fmt;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000494 llvm::raw_svector_ostream OS(Fmt);
495 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
Daniel Dunbar221c7212009-11-14 07:53:24 +0000496 if (I->first.isInvalid() || !SourceMgr)
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000497 OS << "\n (frontend)";
498 else
Chandler Carruth5ef04ee2011-02-23 00:47:48 +0000499 OS << "\n Line " << SourceMgr->getPresumedLineNumber(I->first);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000500 OS << ": " << I->second;
501 }
502
Jordan Rosec6d64a22012-07-11 16:50:36 +0000503 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
Jordan Roseaa48fe82012-07-10 02:57:03 +0000504 << Kind << /*Unexpected=*/true << OS.str();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000505 return std::distance(diag_begin, diag_end);
506}
507
Jordan Roseaa48fe82012-07-10 02:57:03 +0000508/// \brief Takes a list of diagnostics that were expected to have been generated
509/// but were not and produces a diagnostic to the user from this.
510static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
511 DirectiveList &DL, const char *Kind) {
Chris Lattner60909e12010-04-28 20:02:30 +0000512 if (DL.empty())
513 return 0;
514
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000515 SmallString<256> Fmt;
Chris Lattner60909e12010-04-28 20:02:30 +0000516 llvm::raw_svector_ostream OS(Fmt);
517 for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000518 Directive &D = **I;
519 OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
520 if (D.DirectiveLoc != D.DiagnosticLoc)
521 OS << " (directive at "
522 << SourceMgr.getFilename(D.DirectiveLoc) << ":"
523 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
Chris Lattner60909e12010-04-28 20:02:30 +0000524 OS << ": " << D.Text;
525 }
526
Jordan Rosec6d64a22012-07-11 16:50:36 +0000527 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
Jordan Roseaa48fe82012-07-10 02:57:03 +0000528 << Kind << /*Unexpected=*/false << OS.str();
Chris Lattner60909e12010-04-28 20:02:30 +0000529 return DL.size();
530}
531
532/// CheckLists - Compare expected to seen diagnostic lists and return the
533/// the difference between them.
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000534///
David Blaikied6471f72011-09-25 23:23:43 +0000535static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
Chris Lattner60909e12010-04-28 20:02:30 +0000536 const char *Label,
537 DirectiveList &Left,
538 const_diag_iterator d2_begin,
539 const_diag_iterator d2_end) {
540 DirectiveList LeftOnly;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000541 DiagList Right(d2_begin, d2_end);
542
Chris Lattner60909e12010-04-28 20:02:30 +0000543 for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
544 Directive& D = **I;
Jordan Roseaa48fe82012-07-10 02:57:03 +0000545 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000546
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000547 for (unsigned i = 0; i < D.Max; ++i) {
Chris Lattner60909e12010-04-28 20:02:30 +0000548 DiagList::iterator II, IE;
549 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
Chandler Carruth5ef04ee2011-02-23 00:47:48 +0000550 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
Chris Lattner60909e12010-04-28 20:02:30 +0000551 if (LineNo1 != LineNo2)
552 continue;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000553
Chris Lattner60909e12010-04-28 20:02:30 +0000554 const std::string &RightText = II->second;
Jordan Rose4313c012012-07-10 02:56:15 +0000555 if (D.match(RightText))
Chris Lattner60909e12010-04-28 20:02:30 +0000556 break;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000557 }
Chris Lattner60909e12010-04-28 20:02:30 +0000558 if (II == IE) {
559 // Not found.
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000560 if (i >= D.Min) break;
Chris Lattner60909e12010-04-28 20:02:30 +0000561 LeftOnly.push_back(*I);
562 } else {
563 // Found. The same cannot be found twice.
564 Right.erase(II);
565 }
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000566 }
567 }
568 // Now all that's left in Right are those that were not matched.
Jordan Roseaa48fe82012-07-10 02:57:03 +0000569 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
570 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
NAKAMURA Takumiad646842011-12-17 13:00:31 +0000571 return num;
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000572}
573
574/// CheckResults - This compares the expected results to those that
575/// were actually reported. It emits any discrepencies. Return "true" if there
576/// were problems. Return "false" otherwise.
577///
David Blaikied6471f72011-09-25 23:23:43 +0000578static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000579 const TextDiagnosticBuffer &Buffer,
Chris Lattner60909e12010-04-28 20:02:30 +0000580 ExpectedData &ED) {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000581 // We want to capture the delta between what was expected and what was
582 // seen.
583 //
584 // Expected \ Seen - set expected but not seen
585 // Seen \ Expected - set seen but not expected
586 unsigned NumProblems = 0;
587
588 // See if there are error mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000589 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
590 Buffer.err_begin(), Buffer.err_end());
Daniel Dunbar221c7212009-11-14 07:53:24 +0000591
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000592 // See if there are warning mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000593 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
594 Buffer.warn_begin(), Buffer.warn_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000595
596 // See if there are note mismatches.
Chris Lattner60909e12010-04-28 20:02:30 +0000597 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
598 Buffer.note_begin(), Buffer.note_end());
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000599
600 return NumProblems;
601}
602
David Blaikie621bc692011-09-26 00:38:03 +0000603void VerifyDiagnosticConsumer::CheckDiagnostics() {
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000604 // Ensure any diagnostics go to the primary client.
Douglas Gregor78243652011-09-13 01:26:44 +0000605 bool OwnsCurClient = Diags.ownsClient();
David Blaikie78ad0b92011-09-25 23:39:51 +0000606 DiagnosticConsumer *CurClient = Diags.takeClient();
Douglas Gregor78243652011-09-13 01:26:44 +0000607 Diags.setClient(PrimaryClient, false);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000608
609 // If we have a preprocessor, scan the source for expected diagnostic
610 // markers. If not then any diagnostics are unexpected.
611 if (CurrentPreprocessor) {
Axel Naumann01231612011-07-25 19:18:12 +0000612 SourceManager &SM = CurrentPreprocessor->getSourceManager();
Jordan Rose7c304f52012-08-10 01:06:16 +0000613
614#ifndef NDEBUG
615 // In a debug build, scan through any files that may have been missed
616 // during parsing and issue a fatal error if directives are contained
617 // within these files. If a fatal error occurs, this suggests that
618 // this file is being parsed separately from the main file.
619 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
Jordan Rose78541c42012-07-11 19:58:23 +0000620 for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(),
621 End = FilesWithDiagnostics.end();
622 I != End; ++I) {
623 const FileEntry *E = SM.getFileEntryForID(*I);
Jordan Rose7c304f52012-08-10 01:06:16 +0000624 // Don't check files already parsed or those handled as modules.
625 if (E && (FilesParsedForDirectives.count(E)
626 || HS.findModuleForHeader(E)))
627 continue;
628
629 if (findDirectives(*CurrentPreprocessor, *I))
630 llvm::report_fatal_error(Twine("-verify directives found after rather"
631 " than during normal parsing of ",
632 StringRef(E ? E->getName() : "(unknown)")));
Axel Naumann84c05e32011-08-24 13:36:19 +0000633 }
Jordan Rose7c304f52012-08-10 01:06:16 +0000634#endif
Daniel Dunbar221c7212009-11-14 07:53:24 +0000635
636 // Check that the expected diagnostics occurred.
Axel Naumann01231612011-07-25 19:18:12 +0000637 NumErrors += CheckResults(Diags, SM, *Buffer, ED);
Daniel Dunbar221c7212009-11-14 07:53:24 +0000638 } else {
Jordan Roseaa48fe82012-07-10 02:57:03 +0000639 NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
640 Buffer->err_end(), "error") +
641 PrintUnexpected(Diags, 0, Buffer->warn_begin(),
642 Buffer->warn_end(), "warn") +
643 PrintUnexpected(Diags, 0, Buffer->note_begin(),
644 Buffer->note_end(), "note"));
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000645 }
646
Douglas Gregorbdbb0042010-08-18 22:29:43 +0000647 Diags.takeClient();
Douglas Gregor78243652011-09-13 01:26:44 +0000648 Diags.setClient(CurClient, OwnsCurClient);
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000649
650 // Reset the buffer, we have processed all the diagnostics in it.
651 Buffer.reset(new TextDiagnosticBuffer());
Axel Naumanne445e5d2012-07-10 16:24:07 +0000652 ED.Errors.clear();
653 ED.Warnings.clear();
654 ED.Notes.clear();
Daniel Dunbar81f5a1e2009-11-14 03:23:19 +0000655}
Chris Lattner60909e12010-04-28 20:02:30 +0000656
Douglas Gregoraee526e2011-09-29 00:38:00 +0000657DiagnosticConsumer *
658VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
659 if (!Diags.getClient())
660 Diags.setClient(PrimaryClient->clone(Diags));
661
662 return new VerifyDiagnosticConsumer(Diags);
663}
664
Jordan Roseaa48fe82012-07-10 02:57:03 +0000665Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
666 SourceLocation DiagnosticLoc, StringRef Text,
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000667 unsigned Min, unsigned Max) {
Chris Lattner60909e12010-04-28 20:02:30 +0000668 if (RegexKind)
Jordan Rose3b81b7d2012-07-10 02:57:26 +0000669 return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
670 return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
Chris Lattner60909e12010-04-28 20:02:30 +0000671}