1//===---- VerifyDiagnosticConsumer.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/VerifyDiagnosticConsumer.h"
15#include "clang/Basic/CharInfo.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Frontend/FrontendDiagnostic.h"
18#include "clang/Frontend/TextDiagnosticBuffer.h"
19#include "clang/Lex/HeaderSearch.h"
20#include "clang/Lex/Preprocessor.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/Support/Regex.h"
23#include "llvm/Support/raw_ostream.h"
24
25using namespace clang;
26typedef VerifyDiagnosticConsumer::Directive Directive;
27typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
28typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
29
30VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
31  : Diags(_Diags),
32    PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
33    Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
34    LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives)
35{
36  Diags.takeClient();
37  if (Diags.hasSourceManager())
38    setSourceManager(Diags.getSourceManager());
39}
40
41VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
42  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
43  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
44  SrcManager = 0;
45  CheckDiagnostics();
46  Diags.takeClient();
47  if (OwnsPrimaryClient)
48    delete PrimaryClient;
49}
50
51#ifndef NDEBUG
52namespace {
53class VerifyFileTracker : public PPCallbacks {
54  VerifyDiagnosticConsumer &Verify;
55  SourceManager &SM;
56
57public:
58  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
59    : Verify(Verify), SM(SM) { }
60
61  /// \brief Hook into the preprocessor and update the list of parsed
62  /// files when the preprocessor indicates a new file is entered.
63  virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
64                           SrcMgr::CharacteristicKind FileType,
65                           FileID PrevFID) {
66    Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
67                                  VerifyDiagnosticConsumer::IsParsed);
68  }
69};
70} // End anonymous namespace.
71#endif
72
73// DiagnosticConsumer interface.
74
75void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
76                                               const Preprocessor *PP) {
77  // Attach comment handler on first invocation.
78  if (++ActiveSourceFiles == 1) {
79    if (PP) {
80      CurrentPreprocessor = PP;
81      this->LangOpts = &LangOpts;
82      setSourceManager(PP->getSourceManager());
83      const_cast<Preprocessor*>(PP)->addCommentHandler(this);
84#ifndef NDEBUG
85      // Debug build tracks parsed files.
86      VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager);
87      const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
88#endif
89    }
90  }
91
92  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
93  PrimaryClient->BeginSourceFile(LangOpts, PP);
94}
95
96void VerifyDiagnosticConsumer::EndSourceFile() {
97  assert(ActiveSourceFiles && "No active source files!");
98  PrimaryClient->EndSourceFile();
99
100  // Detach comment handler once last active source file completed.
101  if (--ActiveSourceFiles == 0) {
102    if (CurrentPreprocessor)
103      const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
104
105    // Check diagnostics once last file completed.
106    CheckDiagnostics();
107    CurrentPreprocessor = 0;
108    LangOpts = 0;
109  }
110}
111
112void VerifyDiagnosticConsumer::HandleDiagnostic(
113      DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
114  if (Info.hasSourceManager()) {
115    // If this diagnostic is for a different source manager, ignore it.
116    if (SrcManager && &Info.getSourceManager() != SrcManager)
117      return;
118
119    setSourceManager(Info.getSourceManager());
120  }
121
122#ifndef NDEBUG
123  // Debug build tracks unparsed files for possible
124  // unparsed expected-* directives.
125  if (SrcManager) {
126    SourceLocation Loc = Info.getLocation();
127    if (Loc.isValid()) {
128      ParsedStatus PS = IsUnparsed;
129
130      Loc = SrcManager->getExpansionLoc(Loc);
131      FileID FID = SrcManager->getFileID(Loc);
132
133      const FileEntry *FE = SrcManager->getFileEntryForID(FID);
134      if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
135        // If the file is a modules header file it shall not be parsed
136        // for expected-* directives.
137        HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
138        if (HS.findModuleForHeader(FE))
139          PS = IsUnparsedNoDirectives;
140      }
141
142      UpdateParsedFileStatus(*SrcManager, FID, PS);
143    }
144  }
145#endif
146
147  // Send the diagnostic to the buffer, we will check it once we reach the end
148  // of the source file (or are destructed).
149  Buffer->HandleDiagnostic(DiagLevel, Info);
150}
151
152//===----------------------------------------------------------------------===//
153// Checking diagnostics implementation.
154//===----------------------------------------------------------------------===//
155
156typedef TextDiagnosticBuffer::DiagList DiagList;
157typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
158
159namespace {
160
161/// StandardDirective - Directive with string matching.
162///
163class StandardDirective : public Directive {
164public:
165  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
166                    StringRef Text, unsigned Min, unsigned Max)
167    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
168
169  virtual bool isValid(std::string &Error) {
170    // all strings are considered valid; even empty ones
171    return true;
172  }
173
174  virtual bool match(StringRef S) {
175    return S.find(Text) != StringRef::npos;
176  }
177};
178
179/// RegexDirective - Directive with regular-expression matching.
180///
181class RegexDirective : public Directive {
182public:
183  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
184                 StringRef Text, unsigned Min, unsigned Max)
185    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
186
187  virtual bool isValid(std::string &Error) {
188    if (Regex.isValid(Error))
189      return true;
190    return false;
191  }
192
193  virtual bool match(StringRef S) {
194    return Regex.match(S);
195  }
196
197private:
198  llvm::Regex Regex;
199};
200
201class ParseHelper
202{
203public:
204  ParseHelper(StringRef S)
205    : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
206
207  // Return true if string literal is next.
208  bool Next(StringRef S) {
209    P = C;
210    PEnd = C + S.size();
211    if (PEnd > End)
212      return false;
213    return !memcmp(P, S.data(), S.size());
214  }
215
216  // Return true if number is next.
217  // Output N only if number is next.
218  bool Next(unsigned &N) {
219    unsigned TMP = 0;
220    P = C;
221    for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
222      TMP *= 10;
223      TMP += P[0] - '0';
224    }
225    if (P == C)
226      return false;
227    PEnd = P;
228    N = TMP;
229    return true;
230  }
231
232  // Return true if string literal is found.
233  // When true, P marks begin-position of S in content.
234  bool Search(StringRef S, bool EnsureStartOfWord = false) {
235    do {
236      P = std::search(C, End, S.begin(), S.end());
237      PEnd = P + S.size();
238      if (P == End)
239        break;
240      if (!EnsureStartOfWord
241            // Check if string literal starts a new word.
242            || P == Begin || isWhitespace(P[-1])
243            // Or it could be preceeded by the start of a comment.
244            || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
245                                &&  P[-2] == '/'))
246        return true;
247      // Otherwise, skip and search again.
248    } while (Advance());
249    return false;
250  }
251
252  // Advance 1-past previous next/search.
253  // Behavior is undefined if previous next/search failed.
254  bool Advance() {
255    C = PEnd;
256    return C < End;
257  }
258
259  // Skip zero or more whitespace.
260  void SkipWhitespace() {
261    for (; C < End && isWhitespace(*C); ++C)
262      ;
263  }
264
265  // Return true if EOF reached.
266  bool Done() {
267    return !(C < End);
268  }
269
270  const char * const Begin; // beginning of expected content
271  const char * const End;   // end of expected content (1-past)
272  const char *C;            // position of next char in content
273  const char *P;
274
275private:
276  const char *PEnd; // previous next/search subject end (1-past)
277};
278
279} // namespace anonymous
280
281/// ParseDirective - Go through the comment and see if it indicates expected
282/// diagnostics. If so, then put them in the appropriate directive list.
283///
284/// Returns true if any valid directives were found.
285static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
286                           Preprocessor *PP, SourceLocation Pos,
287                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
288  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
289
290  // A single comment may contain multiple directives.
291  bool FoundDirective = false;
292  for (ParseHelper PH(S); !PH.Done();) {
293    // Search for token: expected
294    if (!PH.Search("expected", true))
295      break;
296    PH.Advance();
297
298    // Next token: -
299    if (!PH.Next("-"))
300      continue;
301    PH.Advance();
302
303    // Next token: { error | warning | note }
304    DirectiveList* DL = NULL;
305    if (PH.Next("error"))
306      DL = ED ? &ED->Errors : NULL;
307    else if (PH.Next("warning"))
308      DL = ED ? &ED->Warnings : NULL;
309    else if (PH.Next("note"))
310      DL = ED ? &ED->Notes : NULL;
311    else if (PH.Next("no-diagnostics")) {
312      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
313        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
314          << /*IsExpectedNoDiagnostics=*/true;
315      else
316        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
317      continue;
318    } else
319      continue;
320    PH.Advance();
321
322    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
323      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
324        << /*IsExpectedNoDiagnostics=*/false;
325      continue;
326    }
327    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
328
329    // If a directive has been found but we're not interested
330    // in storing the directive information, return now.
331    if (!DL)
332      return true;
333
334    // Default directive kind.
335    bool RegexKind = false;
336    const char* KindStr = "string";
337
338    // Next optional token: -
339    if (PH.Next("-re")) {
340      PH.Advance();
341      RegexKind = true;
342      KindStr = "regex";
343    }
344
345    // Next optional token: @
346    SourceLocation ExpectedLoc;
347    if (!PH.Next("@")) {
348      ExpectedLoc = Pos;
349    } else {
350      PH.Advance();
351      unsigned Line = 0;
352      bool FoundPlus = PH.Next("+");
353      if (FoundPlus || PH.Next("-")) {
354        // Relative to current line.
355        PH.Advance();
356        bool Invalid = false;
357        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
358        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
359          if (FoundPlus) ExpectedLine += Line;
360          else ExpectedLine -= Line;
361          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
362        }
363      } else if (PH.Next(Line)) {
364        // Absolute line number.
365        if (Line > 0)
366          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
367      } else if (PP && PH.Search(":")) {
368        // Specific source file.
369        StringRef Filename(PH.C, PH.P-PH.C);
370        PH.Advance();
371
372        // Lookup file via Preprocessor, like a #include.
373        const DirectoryLookup *CurDir;
374        const FileEntry *FE = PP->LookupFile(Pos, Filename, false, NULL, CurDir,
375                                             NULL, NULL, 0);
376        if (!FE) {
377          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
378                       diag::err_verify_missing_file) << Filename << KindStr;
379          continue;
380        }
381
382        if (SM.translateFile(FE).isInvalid())
383          SM.createFileID(FE, Pos, SrcMgr::C_User);
384
385        if (PH.Next(Line) && Line > 0)
386          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
387      }
388
389      if (ExpectedLoc.isInvalid()) {
390        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
391                     diag::err_verify_missing_line) << KindStr;
392        continue;
393      }
394      PH.Advance();
395    }
396
397    // Skip optional whitespace.
398    PH.SkipWhitespace();
399
400    // Next optional token: positive integer or a '+'.
401    unsigned Min = 1;
402    unsigned Max = 1;
403    if (PH.Next(Min)) {
404      PH.Advance();
405      // A positive integer can be followed by a '+' meaning min
406      // or more, or by a '-' meaning a range from min to max.
407      if (PH.Next("+")) {
408        Max = Directive::MaxCount;
409        PH.Advance();
410      } else if (PH.Next("-")) {
411        PH.Advance();
412        if (!PH.Next(Max) || Max < Min) {
413          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
414                       diag::err_verify_invalid_range) << KindStr;
415          continue;
416        }
417        PH.Advance();
418      } else {
419        Max = Min;
420      }
421    } else if (PH.Next("+")) {
422      // '+' on its own means "1 or more".
423      Max = Directive::MaxCount;
424      PH.Advance();
425    }
426
427    // Skip optional whitespace.
428    PH.SkipWhitespace();
429
430    // Next token: {{
431    if (!PH.Next("{{")) {
432      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
433                   diag::err_verify_missing_start) << KindStr;
434      continue;
435    }
436    PH.Advance();
437    const char* const ContentBegin = PH.C; // mark content begin
438
439    // Search for token: }}
440    if (!PH.Search("}}")) {
441      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
442                   diag::err_verify_missing_end) << KindStr;
443      continue;
444    }
445    const char* const ContentEnd = PH.P; // mark content end
446    PH.Advance();
447
448    // Build directive text; convert \n to newlines.
449    std::string Text;
450    StringRef NewlineStr = "\\n";
451    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
452    size_t CPos = 0;
453    size_t FPos;
454    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
455      Text += Content.substr(CPos, FPos-CPos);
456      Text += '\n';
457      CPos = FPos + NewlineStr.size();
458    }
459    if (Text.empty())
460      Text.assign(ContentBegin, ContentEnd);
461
462    // Construct new directive.
463    Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
464                                     Min, Max);
465    std::string Error;
466    if (D->isValid(Error)) {
467      DL->push_back(D);
468      FoundDirective = true;
469    } else {
470      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
471                   diag::err_verify_invalid_content)
472        << KindStr << Error;
473    }
474  }
475
476  return FoundDirective;
477}
478
479/// HandleComment - Hook into the preprocessor and extract comments containing
480///  expected errors and warnings.
481bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
482                                             SourceRange Comment) {
483  SourceManager &SM = PP.getSourceManager();
484
485  // If this comment is for a different source manager, ignore it.
486  if (SrcManager && &SM != SrcManager)
487    return false;
488
489  SourceLocation CommentBegin = Comment.getBegin();
490
491  const char *CommentRaw = SM.getCharacterData(CommentBegin);
492  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
493
494  if (C.empty())
495    return false;
496
497  // Fold any "\<EOL>" sequences
498  size_t loc = C.find('\\');
499  if (loc == StringRef::npos) {
500    ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
501    return false;
502  }
503
504  std::string C2;
505  C2.reserve(C.size());
506
507  for (size_t last = 0;; loc = C.find('\\', last)) {
508    if (loc == StringRef::npos || loc == C.size()) {
509      C2 += C.substr(last);
510      break;
511    }
512    C2 += C.substr(last, loc-last);
513    last = loc + 1;
514
515    if (C[last] == '\n' || C[last] == '\r') {
516      ++last;
517
518      // Escape \r\n  or \n\r, but not \n\n.
519      if (last < C.size())
520        if (C[last] == '\n' || C[last] == '\r')
521          if (C[last] != C[last-1])
522            ++last;
523    } else {
524      // This was just a normal backslash.
525      C2 += '\\';
526    }
527  }
528
529  if (!C2.empty())
530    ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
531  return false;
532}
533
534#ifndef NDEBUG
535/// \brief Lex the specified source file to determine whether it contains
536/// any expected-* directives.  As a Lexer is used rather than a full-blown
537/// Preprocessor, directives inside skipped #if blocks will still be found.
538///
539/// \return true if any directives were found.
540static bool findDirectives(SourceManager &SM, FileID FID,
541                           const LangOptions &LangOpts) {
542  // Create a raw lexer to pull all the comments out of FID.
543  if (FID.isInvalid())
544    return false;
545
546  // Create a lexer to lex all the tokens of the main file in raw mode.
547  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
548  Lexer RawLex(FID, FromFile, SM, LangOpts);
549
550  // Return comments as tokens, this is how we find expected diagnostics.
551  RawLex.SetCommentRetentionState(true);
552
553  Token Tok;
554  Tok.setKind(tok::comment);
555  VerifyDiagnosticConsumer::DirectiveStatus Status =
556    VerifyDiagnosticConsumer::HasNoDirectives;
557  while (Tok.isNot(tok::eof)) {
558    RawLex.LexFromRawLexer(Tok);
559    if (!Tok.is(tok::comment)) continue;
560
561    std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
562    if (Comment.empty()) continue;
563
564    // Find first directive.
565    if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status))
566      return true;
567  }
568  return false;
569}
570#endif // !NDEBUG
571
572/// \brief Takes a list of diagnostics that have been generated but not matched
573/// by an expected-* directive and produces a diagnostic to the user from this.
574static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
575                                const_diag_iterator diag_begin,
576                                const_diag_iterator diag_end,
577                                const char *Kind) {
578  if (diag_begin == diag_end) return 0;
579
580  SmallString<256> Fmt;
581  llvm::raw_svector_ostream OS(Fmt);
582  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
583    if (I->first.isInvalid() || !SourceMgr)
584      OS << "\n  (frontend)";
585    else {
586      OS << "\n ";
587      if (const FileEntry *File = SourceMgr->getFileEntryForID(
588                                                SourceMgr->getFileID(I->first)))
589        OS << " File " << File->getName();
590      OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
591    }
592    OS << ": " << I->second;
593  }
594
595  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
596    << Kind << /*Unexpected=*/true << OS.str();
597  return std::distance(diag_begin, diag_end);
598}
599
600/// \brief Takes a list of diagnostics that were expected to have been generated
601/// but were not and produces a diagnostic to the user from this.
602static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
603                              DirectiveList &DL, const char *Kind) {
604  if (DL.empty())
605    return 0;
606
607  SmallString<256> Fmt;
608  llvm::raw_svector_ostream OS(Fmt);
609  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
610    Directive &D = **I;
611    OS << "\n  File " << SourceMgr.getFilename(D.DiagnosticLoc)
612          << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
613    if (D.DirectiveLoc != D.DiagnosticLoc)
614      OS << " (directive at "
615         << SourceMgr.getFilename(D.DirectiveLoc) << ':'
616         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
617    OS << ": " << D.Text;
618  }
619
620  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
621    << Kind << /*Unexpected=*/false << OS.str();
622  return DL.size();
623}
624
625/// \brief Determine whether two source locations come from the same file.
626static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
627                           SourceLocation DiagnosticLoc) {
628  while (DiagnosticLoc.isMacroID())
629    DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
630
631  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
632    return true;
633
634  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
635  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
636    return true;
637
638  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
639}
640
641/// CheckLists - Compare expected to seen diagnostic lists and return the
642/// the difference between them.
643///
644static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
645                           const char *Label,
646                           DirectiveList &Left,
647                           const_diag_iterator d2_begin,
648                           const_diag_iterator d2_end) {
649  DirectiveList LeftOnly;
650  DiagList Right(d2_begin, d2_end);
651
652  for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
653    Directive& D = **I;
654    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
655
656    for (unsigned i = 0; i < D.Max; ++i) {
657      DiagList::iterator II, IE;
658      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
659        unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
660        if (LineNo1 != LineNo2)
661          continue;
662
663        if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
664          continue;
665
666        const std::string &RightText = II->second;
667        if (D.match(RightText))
668          break;
669      }
670      if (II == IE) {
671        // Not found.
672        if (i >= D.Min) break;
673        LeftOnly.push_back(*I);
674      } else {
675        // Found. The same cannot be found twice.
676        Right.erase(II);
677      }
678    }
679  }
680  // Now all that's left in Right are those that were not matched.
681  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
682  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
683  return num;
684}
685
686/// CheckResults - This compares the expected results to those that
687/// were actually reported. It emits any discrepencies. Return "true" if there
688/// were problems. Return "false" otherwise.
689///
690static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
691                             const TextDiagnosticBuffer &Buffer,
692                             ExpectedData &ED) {
693  // We want to capture the delta between what was expected and what was
694  // seen.
695  //
696  //   Expected \ Seen - set expected but not seen
697  //   Seen \ Expected - set seen but not expected
698  unsigned NumProblems = 0;
699
700  // See if there are error mismatches.
701  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
702                            Buffer.err_begin(), Buffer.err_end());
703
704  // See if there are warning mismatches.
705  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
706                            Buffer.warn_begin(), Buffer.warn_end());
707
708  // See if there are note mismatches.
709  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
710                            Buffer.note_begin(), Buffer.note_end());
711
712  return NumProblems;
713}
714
715void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
716                                                      FileID FID,
717                                                      ParsedStatus PS) {
718  // Check SourceManager hasn't changed.
719  setSourceManager(SM);
720
721#ifndef NDEBUG
722  if (FID.isInvalid())
723    return;
724
725  const FileEntry *FE = SM.getFileEntryForID(FID);
726
727  if (PS == IsParsed) {
728    // Move the FileID from the unparsed set to the parsed set.
729    UnparsedFiles.erase(FID);
730    ParsedFiles.insert(std::make_pair(FID, FE));
731  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
732    // Add the FileID to the unparsed set if we haven't seen it before.
733
734    // Check for directives.
735    bool FoundDirectives;
736    if (PS == IsUnparsedNoDirectives)
737      FoundDirectives = false;
738    else
739      FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
740
741    // Add the FileID to the unparsed set.
742    UnparsedFiles.insert(std::make_pair(FID,
743                                      UnparsedFileStatus(FE, FoundDirectives)));
744  }
745#endif
746}
747
748void VerifyDiagnosticConsumer::CheckDiagnostics() {
749  // Ensure any diagnostics go to the primary client.
750  bool OwnsCurClient = Diags.ownsClient();
751  DiagnosticConsumer *CurClient = Diags.takeClient();
752  Diags.setClient(PrimaryClient, false);
753
754#ifndef NDEBUG
755  // In a debug build, scan through any files that may have been missed
756  // during parsing and issue a fatal error if directives are contained
757  // within these files.  If a fatal error occurs, this suggests that
758  // this file is being parsed separately from the main file, in which
759  // case consider moving the directives to the correct place, if this
760  // is applicable.
761  if (UnparsedFiles.size() > 0) {
762    // Generate a cache of parsed FileEntry pointers for alias lookups.
763    llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
764    for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
765                                End = ParsedFiles.end(); I != End; ++I) {
766      if (const FileEntry *FE = I->second)
767        ParsedFileCache.insert(FE);
768    }
769
770    // Iterate through list of unparsed files.
771    for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
772                                  End = UnparsedFiles.end(); I != End; ++I) {
773      const UnparsedFileStatus &Status = I->second;
774      const FileEntry *FE = Status.getFile();
775
776      // Skip files that have been parsed via an alias.
777      if (FE && ParsedFileCache.count(FE))
778        continue;
779
780      // Report a fatal error if this file contained directives.
781      if (Status.foundDirectives()) {
782        llvm::report_fatal_error(Twine("-verify directives found after rather"
783                                       " than during normal parsing of ",
784                                 StringRef(FE ? FE->getName() : "(unknown)")));
785      }
786    }
787
788    // UnparsedFiles has been processed now, so clear it.
789    UnparsedFiles.clear();
790  }
791#endif // !NDEBUG
792
793  if (SrcManager) {
794    // Produce an error if no expected-* directives could be found in the
795    // source file(s) processed.
796    if (Status == HasNoDirectives) {
797      Diags.Report(diag::err_verify_no_directives).setForceEmit();
798      ++NumErrors;
799      Status = HasNoDirectivesReported;
800    }
801
802    // Check that the expected diagnostics occurred.
803    NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
804  } else {
805    NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
806                                  Buffer->err_end(), "error") +
807                  PrintUnexpected(Diags, 0, Buffer->warn_begin(),
808                                  Buffer->warn_end(), "warn") +
809                  PrintUnexpected(Diags, 0, Buffer->note_begin(),
810                                  Buffer->note_end(), "note"));
811  }
812
813  Diags.takeClient();
814  Diags.setClient(CurClient, OwnsCurClient);
815
816  // Reset the buffer, we have processed all the diagnostics in it.
817  Buffer.reset(new TextDiagnosticBuffer());
818  ED.Errors.clear();
819  ED.Warnings.clear();
820  ED.Notes.clear();
821}
822
823Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
824                             SourceLocation DiagnosticLoc, StringRef Text,
825                             unsigned Min, unsigned Max) {
826  if (RegexKind)
827    return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
828  return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
829}
830