1226586Sdim//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
2226586Sdim//
3226586Sdim//                     The LLVM Compiler Infrastructure
4226586Sdim//
5226586Sdim// This file is distributed under the University of Illinois Open Source
6226586Sdim// License. See LICENSE.TXT for details.
7226586Sdim//
8226586Sdim//===----------------------------------------------------------------------===//
9226586Sdim//
10226586Sdim// This is a concrete diagnostic client, which buffers the diagnostic messages.
11226586Sdim//
12226586Sdim//===----------------------------------------------------------------------===//
13226586Sdim
14249423Sdim#include "clang/Frontend/VerifyDiagnosticConsumer.h"
15249423Sdim#include "clang/Basic/CharInfo.h"
16239462Sdim#include "clang/Basic/FileManager.h"
17226586Sdim#include "clang/Frontend/FrontendDiagnostic.h"
18226586Sdim#include "clang/Frontend/TextDiagnosticBuffer.h"
19239462Sdim#include "clang/Lex/HeaderSearch.h"
20226586Sdim#include "clang/Lex/Preprocessor.h"
21226586Sdim#include "llvm/ADT/SmallString.h"
22226586Sdim#include "llvm/Support/Regex.h"
23226586Sdim#include "llvm/Support/raw_ostream.h"
24234353Sdim
25226586Sdimusing namespace clang;
26239462Sdimtypedef VerifyDiagnosticConsumer::Directive Directive;
27239462Sdimtypedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
28239462Sdimtypedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
29226586Sdim
30226586SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
31239462Sdim  : Diags(_Diags),
32239462Sdim    PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
33239462Sdim    Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
34243830Sdim    LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives)
35226586Sdim{
36226586Sdim  Diags.takeClient();
37243830Sdim  if (Diags.hasSourceManager())
38243830Sdim    setSourceManager(Diags.getSourceManager());
39226586Sdim}
40226586Sdim
41226586SdimVerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
42239462Sdim  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
43239462Sdim  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
44243830Sdim  SrcManager = 0;
45226586Sdim  CheckDiagnostics();
46226586Sdim  Diags.takeClient();
47226586Sdim  if (OwnsPrimaryClient)
48226586Sdim    delete PrimaryClient;
49226586Sdim}
50226586Sdim
51239462Sdim#ifndef NDEBUG
52239462Sdimnamespace {
53239462Sdimclass VerifyFileTracker : public PPCallbacks {
54243830Sdim  VerifyDiagnosticConsumer &Verify;
55239462Sdim  SourceManager &SM;
56239462Sdim
57239462Sdimpublic:
58243830Sdim  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
59243830Sdim    : Verify(Verify), SM(SM) { }
60239462Sdim
61239462Sdim  /// \brief Hook into the preprocessor and update the list of parsed
62239462Sdim  /// files when the preprocessor indicates a new file is entered.
63239462Sdim  virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
64239462Sdim                           SrcMgr::CharacteristicKind FileType,
65239462Sdim                           FileID PrevFID) {
66243830Sdim    Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
67243830Sdim                                  VerifyDiagnosticConsumer::IsParsed);
68239462Sdim  }
69239462Sdim};
70239462Sdim} // End anonymous namespace.
71239462Sdim#endif
72239462Sdim
73226586Sdim// DiagnosticConsumer interface.
74226586Sdim
75226586Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
76234353Sdim                                               const Preprocessor *PP) {
77239462Sdim  // Attach comment handler on first invocation.
78239462Sdim  if (++ActiveSourceFiles == 1) {
79239462Sdim    if (PP) {
80239462Sdim      CurrentPreprocessor = PP;
81243830Sdim      this->LangOpts = &LangOpts;
82243830Sdim      setSourceManager(PP->getSourceManager());
83239462Sdim      const_cast<Preprocessor*>(PP)->addCommentHandler(this);
84239462Sdim#ifndef NDEBUG
85243830Sdim      // Debug build tracks parsed files.
86243830Sdim      VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager);
87239462Sdim      const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
88239462Sdim#endif
89239462Sdim    }
90239462Sdim  }
91226586Sdim
92239462Sdim  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
93226586Sdim  PrimaryClient->BeginSourceFile(LangOpts, PP);
94226586Sdim}
95226586Sdim
96226586Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() {
97239462Sdim  assert(ActiveSourceFiles && "No active source files!");
98226586Sdim  PrimaryClient->EndSourceFile();
99226586Sdim
100239462Sdim  // Detach comment handler once last active source file completed.
101239462Sdim  if (--ActiveSourceFiles == 0) {
102239462Sdim    if (CurrentPreprocessor)
103239462Sdim      const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
104239462Sdim
105239462Sdim    // Check diagnostics once last file completed.
106239462Sdim    CheckDiagnostics();
107239462Sdim    CurrentPreprocessor = 0;
108243830Sdim    LangOpts = 0;
109239462Sdim  }
110226586Sdim}
111226586Sdim
112226586Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic(
113226586Sdim      DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
114251662Sdim  if (Info.hasSourceManager()) {
115251662Sdim    // If this diagnostic is for a different source manager, ignore it.
116251662Sdim    if (SrcManager && &Info.getSourceManager() != SrcManager)
117251662Sdim      return;
118251662Sdim
119243830Sdim    setSourceManager(Info.getSourceManager());
120251662Sdim  }
121243830Sdim
122239462Sdim#ifndef NDEBUG
123243830Sdim  // Debug build tracks unparsed files for possible
124243830Sdim  // unparsed expected-* directives.
125243830Sdim  if (SrcManager) {
126243830Sdim    SourceLocation Loc = Info.getLocation();
127243830Sdim    if (Loc.isValid()) {
128243830Sdim      ParsedStatus PS = IsUnparsed;
129243830Sdim
130243830Sdim      Loc = SrcManager->getExpansionLoc(Loc);
131243830Sdim      FileID FID = SrcManager->getFileID(Loc);
132243830Sdim
133243830Sdim      const FileEntry *FE = SrcManager->getFileEntryForID(FID);
134243830Sdim      if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
135243830Sdim        // If the file is a modules header file it shall not be parsed
136243830Sdim        // for expected-* directives.
137243830Sdim        HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
138243830Sdim        if (HS.findModuleForHeader(FE))
139243830Sdim          PS = IsUnparsedNoDirectives;
140243830Sdim      }
141243830Sdim
142243830Sdim      UpdateParsedFileStatus(*SrcManager, FID, PS);
143243830Sdim    }
144226586Sdim  }
145239462Sdim#endif
146243830Sdim
147226586Sdim  // Send the diagnostic to the buffer, we will check it once we reach the end
148226586Sdim  // of the source file (or are destructed).
149226586Sdim  Buffer->HandleDiagnostic(DiagLevel, Info);
150226586Sdim}
151226586Sdim
152226586Sdim//===----------------------------------------------------------------------===//
153226586Sdim// Checking diagnostics implementation.
154226586Sdim//===----------------------------------------------------------------------===//
155226586Sdim
156226586Sdimtypedef TextDiagnosticBuffer::DiagList DiagList;
157226586Sdimtypedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
158226586Sdim
159226586Sdimnamespace {
160226586Sdim
161226586Sdim/// StandardDirective - Directive with string matching.
162226586Sdim///
163226586Sdimclass StandardDirective : public Directive {
164226586Sdimpublic:
165239462Sdim  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
166239462Sdim                    StringRef Text, unsigned Min, unsigned Max)
167239462Sdim    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
168226586Sdim
169226586Sdim  virtual bool isValid(std::string &Error) {
170226586Sdim    // all strings are considered valid; even empty ones
171226586Sdim    return true;
172226586Sdim  }
173226586Sdim
174239462Sdim  virtual bool match(StringRef S) {
175239462Sdim    return S.find(Text) != StringRef::npos;
176226586Sdim  }
177226586Sdim};
178226586Sdim
179226586Sdim/// RegexDirective - Directive with regular-expression matching.
180226586Sdim///
181226586Sdimclass RegexDirective : public Directive {
182226586Sdimpublic:
183239462Sdim  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
184239462Sdim                 StringRef Text, unsigned Min, unsigned Max)
185239462Sdim    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
186226586Sdim
187226586Sdim  virtual bool isValid(std::string &Error) {
188226586Sdim    if (Regex.isValid(Error))
189226586Sdim      return true;
190226586Sdim    return false;
191226586Sdim  }
192226586Sdim
193239462Sdim  virtual bool match(StringRef S) {
194226586Sdim    return Regex.match(S);
195226586Sdim  }
196226586Sdim
197226586Sdimprivate:
198226586Sdim  llvm::Regex Regex;
199226586Sdim};
200226586Sdim
201226586Sdimclass ParseHelper
202226586Sdim{
203226586Sdimpublic:
204239462Sdim  ParseHelper(StringRef S)
205239462Sdim    : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
206226586Sdim
207226586Sdim  // Return true if string literal is next.
208226586Sdim  bool Next(StringRef S) {
209226586Sdim    P = C;
210226586Sdim    PEnd = C + S.size();
211226586Sdim    if (PEnd > End)
212226586Sdim      return false;
213226586Sdim    return !memcmp(P, S.data(), S.size());
214226586Sdim  }
215226586Sdim
216226586Sdim  // Return true if number is next.
217226586Sdim  // Output N only if number is next.
218226586Sdim  bool Next(unsigned &N) {
219226586Sdim    unsigned TMP = 0;
220226586Sdim    P = C;
221226586Sdim    for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
222226586Sdim      TMP *= 10;
223226586Sdim      TMP += P[0] - '0';
224226586Sdim    }
225226586Sdim    if (P == C)
226226586Sdim      return false;
227226586Sdim    PEnd = P;
228226586Sdim    N = TMP;
229226586Sdim    return true;
230226586Sdim  }
231226586Sdim
232226586Sdim  // Return true if string literal is found.
233226586Sdim  // When true, P marks begin-position of S in content.
234243830Sdim  bool Search(StringRef S, bool EnsureStartOfWord = false) {
235243830Sdim    do {
236243830Sdim      P = std::search(C, End, S.begin(), S.end());
237243830Sdim      PEnd = P + S.size();
238243830Sdim      if (P == End)
239243830Sdim        break;
240243830Sdim      if (!EnsureStartOfWord
241243830Sdim            // Check if string literal starts a new word.
242249423Sdim            || P == Begin || isWhitespace(P[-1])
243243830Sdim            // Or it could be preceeded by the start of a comment.
244243830Sdim            || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
245243830Sdim                                &&  P[-2] == '/'))
246243830Sdim        return true;
247243830Sdim      // Otherwise, skip and search again.
248243830Sdim    } while (Advance());
249243830Sdim    return false;
250226586Sdim  }
251226586Sdim
252226586Sdim  // Advance 1-past previous next/search.
253226586Sdim  // Behavior is undefined if previous next/search failed.
254226586Sdim  bool Advance() {
255226586Sdim    C = PEnd;
256226586Sdim    return C < End;
257226586Sdim  }
258226586Sdim
259226586Sdim  // Skip zero or more whitespace.
260226586Sdim  void SkipWhitespace() {
261249423Sdim    for (; C < End && isWhitespace(*C); ++C)
262226586Sdim      ;
263226586Sdim  }
264226586Sdim
265226586Sdim  // Return true if EOF reached.
266226586Sdim  bool Done() {
267226586Sdim    return !(C < End);
268226586Sdim  }
269226586Sdim
270226586Sdim  const char * const Begin; // beginning of expected content
271226586Sdim  const char * const End;   // end of expected content (1-past)
272226586Sdim  const char *C;            // position of next char in content
273226586Sdim  const char *P;
274226586Sdim
275226586Sdimprivate:
276226586Sdim  const char *PEnd; // previous next/search subject end (1-past)
277226586Sdim};
278226586Sdim
279226586Sdim} // namespace anonymous
280226586Sdim
281226586Sdim/// ParseDirective - Go through the comment and see if it indicates expected
282226586Sdim/// diagnostics. If so, then put them in the appropriate directive list.
283226586Sdim///
284239462Sdim/// Returns true if any valid directives were found.
285239462Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
286251662Sdim                           Preprocessor *PP, SourceLocation Pos,
287243830Sdim                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
288251662Sdim  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
289251662Sdim
290226586Sdim  // A single comment may contain multiple directives.
291239462Sdim  bool FoundDirective = false;
292239462Sdim  for (ParseHelper PH(S); !PH.Done();) {
293239462Sdim    // Search for token: expected
294243830Sdim    if (!PH.Search("expected", true))
295226586Sdim      break;
296226586Sdim    PH.Advance();
297226586Sdim
298239462Sdim    // Next token: -
299226586Sdim    if (!PH.Next("-"))
300226586Sdim      continue;
301226586Sdim    PH.Advance();
302226586Sdim
303239462Sdim    // Next token: { error | warning | note }
304226586Sdim    DirectiveList* DL = NULL;
305226586Sdim    if (PH.Next("error"))
306239462Sdim      DL = ED ? &ED->Errors : NULL;
307226586Sdim    else if (PH.Next("warning"))
308239462Sdim      DL = ED ? &ED->Warnings : NULL;
309226586Sdim    else if (PH.Next("note"))
310239462Sdim      DL = ED ? &ED->Notes : NULL;
311243830Sdim    else if (PH.Next("no-diagnostics")) {
312243830Sdim      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
313243830Sdim        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
314243830Sdim          << /*IsExpectedNoDiagnostics=*/true;
315243830Sdim      else
316243830Sdim        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
317226586Sdim      continue;
318243830Sdim    } else
319243830Sdim      continue;
320226586Sdim    PH.Advance();
321226586Sdim
322243830Sdim    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
323243830Sdim      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
324243830Sdim        << /*IsExpectedNoDiagnostics=*/false;
325243830Sdim      continue;
326243830Sdim    }
327243830Sdim    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
328243830Sdim
329239462Sdim    // If a directive has been found but we're not interested
330239462Sdim    // in storing the directive information, return now.
331239462Sdim    if (!DL)
332239462Sdim      return true;
333239462Sdim
334239462Sdim    // Default directive kind.
335226586Sdim    bool RegexKind = false;
336226586Sdim    const char* KindStr = "string";
337226586Sdim
338239462Sdim    // Next optional token: -
339226586Sdim    if (PH.Next("-re")) {
340226586Sdim      PH.Advance();
341226586Sdim      RegexKind = true;
342226586Sdim      KindStr = "regex";
343226586Sdim    }
344226586Sdim
345239462Sdim    // Next optional token: @
346239462Sdim    SourceLocation ExpectedLoc;
347239462Sdim    if (!PH.Next("@")) {
348239462Sdim      ExpectedLoc = Pos;
349239462Sdim    } else {
350239462Sdim      PH.Advance();
351239462Sdim      unsigned Line = 0;
352239462Sdim      bool FoundPlus = PH.Next("+");
353239462Sdim      if (FoundPlus || PH.Next("-")) {
354239462Sdim        // Relative to current line.
355239462Sdim        PH.Advance();
356239462Sdim        bool Invalid = false;
357239462Sdim        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
358239462Sdim        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
359239462Sdim          if (FoundPlus) ExpectedLine += Line;
360239462Sdim          else ExpectedLine -= Line;
361239462Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
362239462Sdim        }
363251662Sdim      } else if (PH.Next(Line)) {
364239462Sdim        // Absolute line number.
365251662Sdim        if (Line > 0)
366251662Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
367251662Sdim      } else if (PP && PH.Search(":")) {
368251662Sdim        // Specific source file.
369251662Sdim        StringRef Filename(PH.C, PH.P-PH.C);
370251662Sdim        PH.Advance();
371251662Sdim
372251662Sdim        // Lookup file via Preprocessor, like a #include.
373251662Sdim        const DirectoryLookup *CurDir;
374263508Sdim        const FileEntry *FE = PP->LookupFile(Pos, Filename, false, NULL, CurDir,
375251662Sdim                                             NULL, NULL, 0);
376251662Sdim        if (!FE) {
377251662Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
378251662Sdim                       diag::err_verify_missing_file) << Filename << KindStr;
379251662Sdim          continue;
380251662Sdim        }
381251662Sdim
382251662Sdim        if (SM.translateFile(FE).isInvalid())
383251662Sdim          SM.createFileID(FE, Pos, SrcMgr::C_User);
384251662Sdim
385239462Sdim        if (PH.Next(Line) && Line > 0)
386251662Sdim          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
387239462Sdim      }
388239462Sdim
389239462Sdim      if (ExpectedLoc.isInvalid()) {
390239462Sdim        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
391239462Sdim                     diag::err_verify_missing_line) << KindStr;
392239462Sdim        continue;
393239462Sdim      }
394239462Sdim      PH.Advance();
395239462Sdim    }
396239462Sdim
397239462Sdim    // Skip optional whitespace.
398226586Sdim    PH.SkipWhitespace();
399226586Sdim
400239462Sdim    // Next optional token: positive integer or a '+'.
401239462Sdim    unsigned Min = 1;
402239462Sdim    unsigned Max = 1;
403239462Sdim    if (PH.Next(Min)) {
404226586Sdim      PH.Advance();
405239462Sdim      // A positive integer can be followed by a '+' meaning min
406239462Sdim      // or more, or by a '-' meaning a range from min to max.
407239462Sdim      if (PH.Next("+")) {
408239462Sdim        Max = Directive::MaxCount;
409239462Sdim        PH.Advance();
410239462Sdim      } else if (PH.Next("-")) {
411239462Sdim        PH.Advance();
412239462Sdim        if (!PH.Next(Max) || Max < Min) {
413239462Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
414239462Sdim                       diag::err_verify_invalid_range) << KindStr;
415239462Sdim          continue;
416239462Sdim        }
417239462Sdim        PH.Advance();
418239462Sdim      } else {
419239462Sdim        Max = Min;
420239462Sdim      }
421239462Sdim    } else if (PH.Next("+")) {
422239462Sdim      // '+' on its own means "1 or more".
423239462Sdim      Max = Directive::MaxCount;
424234353Sdim      PH.Advance();
425234353Sdim    }
426226586Sdim
427239462Sdim    // Skip optional whitespace.
428226586Sdim    PH.SkipWhitespace();
429226586Sdim
430239462Sdim    // Next token: {{
431226586Sdim    if (!PH.Next("{{")) {
432239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
433239462Sdim                   diag::err_verify_missing_start) << KindStr;
434226586Sdim      continue;
435226586Sdim    }
436226586Sdim    PH.Advance();
437226586Sdim    const char* const ContentBegin = PH.C; // mark content begin
438226586Sdim
439239462Sdim    // Search for token: }}
440226586Sdim    if (!PH.Search("}}")) {
441239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
442239462Sdim                   diag::err_verify_missing_end) << KindStr;
443226586Sdim      continue;
444226586Sdim    }
445226586Sdim    const char* const ContentEnd = PH.P; // mark content end
446226586Sdim    PH.Advance();
447226586Sdim
448239462Sdim    // Build directive text; convert \n to newlines.
449226586Sdim    std::string Text;
450226586Sdim    StringRef NewlineStr = "\\n";
451226586Sdim    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
452226586Sdim    size_t CPos = 0;
453226586Sdim    size_t FPos;
454226586Sdim    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
455226586Sdim      Text += Content.substr(CPos, FPos-CPos);
456226586Sdim      Text += '\n';
457226586Sdim      CPos = FPos + NewlineStr.size();
458226586Sdim    }
459226586Sdim    if (Text.empty())
460226586Sdim      Text.assign(ContentBegin, ContentEnd);
461226586Sdim
462239462Sdim    // Construct new directive.
463239462Sdim    Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
464239462Sdim                                     Min, Max);
465226586Sdim    std::string Error;
466239462Sdim    if (D->isValid(Error)) {
467226586Sdim      DL->push_back(D);
468239462Sdim      FoundDirective = true;
469239462Sdim    } else {
470239462Sdim      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
471239462Sdim                   diag::err_verify_invalid_content)
472226586Sdim        << KindStr << Error;
473226586Sdim    }
474226586Sdim  }
475239462Sdim
476239462Sdim  return FoundDirective;
477226586Sdim}
478226586Sdim
479239462Sdim/// HandleComment - Hook into the preprocessor and extract comments containing
480239462Sdim///  expected errors and warnings.
481239462Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
482239462Sdim                                             SourceRange Comment) {
483239462Sdim  SourceManager &SM = PP.getSourceManager();
484251662Sdim
485251662Sdim  // If this comment is for a different source manager, ignore it.
486251662Sdim  if (SrcManager && &SM != SrcManager)
487251662Sdim    return false;
488251662Sdim
489239462Sdim  SourceLocation CommentBegin = Comment.getBegin();
490239462Sdim
491239462Sdim  const char *CommentRaw = SM.getCharacterData(CommentBegin);
492239462Sdim  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
493239462Sdim
494239462Sdim  if (C.empty())
495239462Sdim    return false;
496239462Sdim
497239462Sdim  // Fold any "\<EOL>" sequences
498239462Sdim  size_t loc = C.find('\\');
499239462Sdim  if (loc == StringRef::npos) {
500251662Sdim    ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
501239462Sdim    return false;
502239462Sdim  }
503239462Sdim
504239462Sdim  std::string C2;
505239462Sdim  C2.reserve(C.size());
506239462Sdim
507239462Sdim  for (size_t last = 0;; loc = C.find('\\', last)) {
508239462Sdim    if (loc == StringRef::npos || loc == C.size()) {
509239462Sdim      C2 += C.substr(last);
510239462Sdim      break;
511239462Sdim    }
512239462Sdim    C2 += C.substr(last, loc-last);
513239462Sdim    last = loc + 1;
514239462Sdim
515239462Sdim    if (C[last] == '\n' || C[last] == '\r') {
516239462Sdim      ++last;
517239462Sdim
518239462Sdim      // Escape \r\n  or \n\r, but not \n\n.
519239462Sdim      if (last < C.size())
520239462Sdim        if (C[last] == '\n' || C[last] == '\r')
521239462Sdim          if (C[last] != C[last-1])
522239462Sdim            ++last;
523239462Sdim    } else {
524239462Sdim      // This was just a normal backslash.
525239462Sdim      C2 += '\\';
526239462Sdim    }
527239462Sdim  }
528239462Sdim
529239462Sdim  if (!C2.empty())
530251662Sdim    ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
531239462Sdim  return false;
532239462Sdim}
533239462Sdim
534239462Sdim#ifndef NDEBUG
535239462Sdim/// \brief Lex the specified source file to determine whether it contains
536239462Sdim/// any expected-* directives.  As a Lexer is used rather than a full-blown
537239462Sdim/// Preprocessor, directives inside skipped #if blocks will still be found.
538239462Sdim///
539239462Sdim/// \return true if any directives were found.
540243830Sdimstatic bool findDirectives(SourceManager &SM, FileID FID,
541243830Sdim                           const LangOptions &LangOpts) {
542226586Sdim  // Create a raw lexer to pull all the comments out of FID.
543226586Sdim  if (FID.isInvalid())
544239462Sdim    return false;
545226586Sdim
546226586Sdim  // Create a lexer to lex all the tokens of the main file in raw mode.
547226586Sdim  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
548243830Sdim  Lexer RawLex(FID, FromFile, SM, LangOpts);
549226586Sdim
550226586Sdim  // Return comments as tokens, this is how we find expected diagnostics.
551226586Sdim  RawLex.SetCommentRetentionState(true);
552226586Sdim
553226586Sdim  Token Tok;
554226586Sdim  Tok.setKind(tok::comment);
555243830Sdim  VerifyDiagnosticConsumer::DirectiveStatus Status =
556243830Sdim    VerifyDiagnosticConsumer::HasNoDirectives;
557226586Sdim  while (Tok.isNot(tok::eof)) {
558263508Sdim    RawLex.LexFromRawLexer(Tok);
559226586Sdim    if (!Tok.is(tok::comment)) continue;
560226586Sdim
561243830Sdim    std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
562226586Sdim    if (Comment.empty()) continue;
563226586Sdim
564243830Sdim    // Find first directive.
565251662Sdim    if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status))
566243830Sdim      return true;
567239462Sdim  }
568243830Sdim  return false;
569226586Sdim}
570239462Sdim#endif // !NDEBUG
571226586Sdim
572239462Sdim/// \brief Takes a list of diagnostics that have been generated but not matched
573239462Sdim/// by an expected-* directive and produces a diagnostic to the user from this.
574239462Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
575239462Sdim                                const_diag_iterator diag_begin,
576239462Sdim                                const_diag_iterator diag_end,
577239462Sdim                                const char *Kind) {
578226586Sdim  if (diag_begin == diag_end) return 0;
579226586Sdim
580234353Sdim  SmallString<256> Fmt;
581226586Sdim  llvm::raw_svector_ostream OS(Fmt);
582226586Sdim  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
583226586Sdim    if (I->first.isInvalid() || !SourceMgr)
584226586Sdim      OS << "\n  (frontend)";
585251662Sdim    else {
586251662Sdim      OS << "\n ";
587251662Sdim      if (const FileEntry *File = SourceMgr->getFileEntryForID(
588251662Sdim                                                SourceMgr->getFileID(I->first)))
589251662Sdim        OS << " File " << File->getName();
590251662Sdim      OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
591251662Sdim    }
592226586Sdim    OS << ": " << I->second;
593226586Sdim  }
594226586Sdim
595239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
596239462Sdim    << Kind << /*Unexpected=*/true << OS.str();
597226586Sdim  return std::distance(diag_begin, diag_end);
598226586Sdim}
599226586Sdim
600239462Sdim/// \brief Takes a list of diagnostics that were expected to have been generated
601239462Sdim/// but were not and produces a diagnostic to the user from this.
602239462Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
603239462Sdim                              DirectiveList &DL, const char *Kind) {
604226586Sdim  if (DL.empty())
605226586Sdim    return 0;
606226586Sdim
607234353Sdim  SmallString<256> Fmt;
608226586Sdim  llvm::raw_svector_ostream OS(Fmt);
609226586Sdim  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
610239462Sdim    Directive &D = **I;
611251662Sdim    OS << "\n  File " << SourceMgr.getFilename(D.DiagnosticLoc)
612251662Sdim          << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
613239462Sdim    if (D.DirectiveLoc != D.DiagnosticLoc)
614239462Sdim      OS << " (directive at "
615251662Sdim         << SourceMgr.getFilename(D.DirectiveLoc) << ':'
616251662Sdim         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
617226586Sdim    OS << ": " << D.Text;
618226586Sdim  }
619226586Sdim
620239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
621239462Sdim    << Kind << /*Unexpected=*/false << OS.str();
622226586Sdim  return DL.size();
623226586Sdim}
624226586Sdim
625251662Sdim/// \brief Determine whether two source locations come from the same file.
626251662Sdimstatic bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
627251662Sdim                           SourceLocation DiagnosticLoc) {
628251662Sdim  while (DiagnosticLoc.isMacroID())
629251662Sdim    DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
630251662Sdim
631263508Sdim  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
632251662Sdim    return true;
633251662Sdim
634251662Sdim  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
635263508Sdim  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
636251662Sdim    return true;
637251662Sdim
638251662Sdim  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
639251662Sdim}
640251662Sdim
641226586Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the
642226586Sdim/// the difference between them.
643226586Sdim///
644226586Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
645226586Sdim                           const char *Label,
646226586Sdim                           DirectiveList &Left,
647226586Sdim                           const_diag_iterator d2_begin,
648226586Sdim                           const_diag_iterator d2_end) {
649226586Sdim  DirectiveList LeftOnly;
650226586Sdim  DiagList Right(d2_begin, d2_end);
651226586Sdim
652226586Sdim  for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
653226586Sdim    Directive& D = **I;
654239462Sdim    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
655226586Sdim
656239462Sdim    for (unsigned i = 0; i < D.Max; ++i) {
657226586Sdim      DiagList::iterator II, IE;
658226586Sdim      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
659226586Sdim        unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
660226586Sdim        if (LineNo1 != LineNo2)
661226586Sdim          continue;
662226586Sdim
663251662Sdim        if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
664251662Sdim          continue;
665251662Sdim
666226586Sdim        const std::string &RightText = II->second;
667239462Sdim        if (D.match(RightText))
668226586Sdim          break;
669226586Sdim      }
670226586Sdim      if (II == IE) {
671226586Sdim        // Not found.
672239462Sdim        if (i >= D.Min) break;
673226586Sdim        LeftOnly.push_back(*I);
674226586Sdim      } else {
675226586Sdim        // Found. The same cannot be found twice.
676226586Sdim        Right.erase(II);
677226586Sdim      }
678226586Sdim    }
679226586Sdim  }
680226586Sdim  // Now all that's left in Right are those that were not matched.
681239462Sdim  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
682239462Sdim  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
683234353Sdim  return num;
684226586Sdim}
685226586Sdim
686226586Sdim/// CheckResults - This compares the expected results to those that
687226586Sdim/// were actually reported. It emits any discrepencies. Return "true" if there
688226586Sdim/// were problems. Return "false" otherwise.
689226586Sdim///
690226586Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
691226586Sdim                             const TextDiagnosticBuffer &Buffer,
692226586Sdim                             ExpectedData &ED) {
693226586Sdim  // We want to capture the delta between what was expected and what was
694226586Sdim  // seen.
695226586Sdim  //
696226586Sdim  //   Expected \ Seen - set expected but not seen
697226586Sdim  //   Seen \ Expected - set seen but not expected
698226586Sdim  unsigned NumProblems = 0;
699226586Sdim
700226586Sdim  // See if there are error mismatches.
701226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
702226586Sdim                            Buffer.err_begin(), Buffer.err_end());
703226586Sdim
704226586Sdim  // See if there are warning mismatches.
705226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
706226586Sdim                            Buffer.warn_begin(), Buffer.warn_end());
707226586Sdim
708226586Sdim  // See if there are note mismatches.
709226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
710226586Sdim                            Buffer.note_begin(), Buffer.note_end());
711226586Sdim
712226586Sdim  return NumProblems;
713226586Sdim}
714226586Sdim
715243830Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
716243830Sdim                                                      FileID FID,
717243830Sdim                                                      ParsedStatus PS) {
718243830Sdim  // Check SourceManager hasn't changed.
719243830Sdim  setSourceManager(SM);
720243830Sdim
721243830Sdim#ifndef NDEBUG
722243830Sdim  if (FID.isInvalid())
723243830Sdim    return;
724243830Sdim
725243830Sdim  const FileEntry *FE = SM.getFileEntryForID(FID);
726243830Sdim
727243830Sdim  if (PS == IsParsed) {
728243830Sdim    // Move the FileID from the unparsed set to the parsed set.
729243830Sdim    UnparsedFiles.erase(FID);
730243830Sdim    ParsedFiles.insert(std::make_pair(FID, FE));
731243830Sdim  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
732243830Sdim    // Add the FileID to the unparsed set if we haven't seen it before.
733243830Sdim
734243830Sdim    // Check for directives.
735243830Sdim    bool FoundDirectives;
736243830Sdim    if (PS == IsUnparsedNoDirectives)
737243830Sdim      FoundDirectives = false;
738243830Sdim    else
739243830Sdim      FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
740243830Sdim
741243830Sdim    // Add the FileID to the unparsed set.
742243830Sdim    UnparsedFiles.insert(std::make_pair(FID,
743243830Sdim                                      UnparsedFileStatus(FE, FoundDirectives)));
744243830Sdim  }
745243830Sdim#endif
746243830Sdim}
747243830Sdim
748226586Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() {
749226586Sdim  // Ensure any diagnostics go to the primary client.
750226586Sdim  bool OwnsCurClient = Diags.ownsClient();
751226586Sdim  DiagnosticConsumer *CurClient = Diags.takeClient();
752226586Sdim  Diags.setClient(PrimaryClient, false);
753226586Sdim
754243830Sdim#ifndef NDEBUG
755243830Sdim  // In a debug build, scan through any files that may have been missed
756243830Sdim  // during parsing and issue a fatal error if directives are contained
757243830Sdim  // within these files.  If a fatal error occurs, this suggests that
758243830Sdim  // this file is being parsed separately from the main file, in which
759243830Sdim  // case consider moving the directives to the correct place, if this
760243830Sdim  // is applicable.
761243830Sdim  if (UnparsedFiles.size() > 0) {
762243830Sdim    // Generate a cache of parsed FileEntry pointers for alias lookups.
763243830Sdim    llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
764243830Sdim    for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
765243830Sdim                                End = ParsedFiles.end(); I != End; ++I) {
766243830Sdim      if (const FileEntry *FE = I->second)
767243830Sdim        ParsedFileCache.insert(FE);
768243830Sdim    }
769239462Sdim
770243830Sdim    // Iterate through list of unparsed files.
771243830Sdim    for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
772243830Sdim                                  End = UnparsedFiles.end(); I != End; ++I) {
773243830Sdim      const UnparsedFileStatus &Status = I->second;
774243830Sdim      const FileEntry *FE = Status.getFile();
775243830Sdim
776243830Sdim      // Skip files that have been parsed via an alias.
777243830Sdim      if (FE && ParsedFileCache.count(FE))
778239462Sdim        continue;
779239462Sdim
780243830Sdim      // Report a fatal error if this file contained directives.
781243830Sdim      if (Status.foundDirectives()) {
782239462Sdim        llvm::report_fatal_error(Twine("-verify directives found after rather"
783239462Sdim                                       " than during normal parsing of ",
784243830Sdim                                 StringRef(FE ? FE->getName() : "(unknown)")));
785243830Sdim      }
786226586Sdim    }
787226586Sdim
788243830Sdim    // UnparsedFiles has been processed now, so clear it.
789243830Sdim    UnparsedFiles.clear();
790243830Sdim  }
791243830Sdim#endif // !NDEBUG
792243830Sdim
793243830Sdim  if (SrcManager) {
794243830Sdim    // Produce an error if no expected-* directives could be found in the
795243830Sdim    // source file(s) processed.
796243830Sdim    if (Status == HasNoDirectives) {
797243830Sdim      Diags.Report(diag::err_verify_no_directives).setForceEmit();
798243830Sdim      ++NumErrors;
799243830Sdim      Status = HasNoDirectivesReported;
800243830Sdim    }
801243830Sdim
802226586Sdim    // Check that the expected diagnostics occurred.
803243830Sdim    NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
804226586Sdim  } else {
805239462Sdim    NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
806239462Sdim                                  Buffer->err_end(), "error") +
807239462Sdim                  PrintUnexpected(Diags, 0, Buffer->warn_begin(),
808239462Sdim                                  Buffer->warn_end(), "warn") +
809239462Sdim                  PrintUnexpected(Diags, 0, Buffer->note_begin(),
810239462Sdim                                  Buffer->note_end(), "note"));
811226586Sdim  }
812226586Sdim
813226586Sdim  Diags.takeClient();
814226586Sdim  Diags.setClient(CurClient, OwnsCurClient);
815226586Sdim
816226586Sdim  // Reset the buffer, we have processed all the diagnostics in it.
817226586Sdim  Buffer.reset(new TextDiagnosticBuffer());
818239462Sdim  ED.Errors.clear();
819239462Sdim  ED.Warnings.clear();
820239462Sdim  ED.Notes.clear();
821226586Sdim}
822226586Sdim
823239462SdimDirective *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
824239462Sdim                             SourceLocation DiagnosticLoc, StringRef Text,
825239462Sdim                             unsigned Min, unsigned Max) {
826226586Sdim  if (RegexKind)
827239462Sdim    return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
828239462Sdim  return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
829226586Sdim}
830