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
30280031SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
31280031Sdim  : Diags(Diags_),
32280031Sdim    PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()),
33276479Sdim    Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr),
34276479Sdim    LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0),
35276479Sdim    Status(HasNoDirectives)
36226586Sdim{
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!");
44276479Sdim  SrcManager = nullptr;
45280031Sdim  CheckDiagnostics();
46280031Sdim  Diags.takeClient().release();
47226586Sdim}
48226586Sdim
49239462Sdim#ifndef NDEBUG
50239462Sdimnamespace {
51239462Sdimclass VerifyFileTracker : public PPCallbacks {
52243830Sdim  VerifyDiagnosticConsumer &Verify;
53239462Sdim  SourceManager &SM;
54239462Sdim
55239462Sdimpublic:
56243830Sdim  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
57243830Sdim    : Verify(Verify), SM(SM) { }
58239462Sdim
59239462Sdim  /// \brief Hook into the preprocessor and update the list of parsed
60239462Sdim  /// files when the preprocessor indicates a new file is entered.
61288943Sdim  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
62288943Sdim                   SrcMgr::CharacteristicKind FileType,
63288943Sdim                   FileID PrevFID) override {
64243830Sdim    Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
65243830Sdim                                  VerifyDiagnosticConsumer::IsParsed);
66239462Sdim  }
67239462Sdim};
68239462Sdim} // End anonymous namespace.
69239462Sdim#endif
70239462Sdim
71226586Sdim// DiagnosticConsumer interface.
72226586Sdim
73226586Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
74234353Sdim                                               const Preprocessor *PP) {
75239462Sdim  // Attach comment handler on first invocation.
76239462Sdim  if (++ActiveSourceFiles == 1) {
77239462Sdim    if (PP) {
78239462Sdim      CurrentPreprocessor = PP;
79243830Sdim      this->LangOpts = &LangOpts;
80243830Sdim      setSourceManager(PP->getSourceManager());
81239462Sdim      const_cast<Preprocessor*>(PP)->addCommentHandler(this);
82239462Sdim#ifndef NDEBUG
83243830Sdim      // Debug build tracks parsed files.
84280031Sdim      const_cast<Preprocessor*>(PP)->addPPCallbacks(
85280031Sdim                      llvm::make_unique<VerifyFileTracker>(*this, *SrcManager));
86239462Sdim#endif
87239462Sdim    }
88239462Sdim  }
89226586Sdim
90239462Sdim  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
91226586Sdim  PrimaryClient->BeginSourceFile(LangOpts, PP);
92226586Sdim}
93226586Sdim
94226586Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() {
95239462Sdim  assert(ActiveSourceFiles && "No active source files!");
96226586Sdim  PrimaryClient->EndSourceFile();
97226586Sdim
98239462Sdim  // Detach comment handler once last active source file completed.
99239462Sdim  if (--ActiveSourceFiles == 0) {
100239462Sdim    if (CurrentPreprocessor)
101239462Sdim      const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
102239462Sdim
103239462Sdim    // Check diagnostics once last file completed.
104239462Sdim    CheckDiagnostics();
105276479Sdim    CurrentPreprocessor = nullptr;
106276479Sdim    LangOpts = nullptr;
107239462Sdim  }
108226586Sdim}
109226586Sdim
110226586Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic(
111226586Sdim      DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
112251662Sdim  if (Info.hasSourceManager()) {
113251662Sdim    // If this diagnostic is for a different source manager, ignore it.
114251662Sdim    if (SrcManager && &Info.getSourceManager() != SrcManager)
115251662Sdim      return;
116251662Sdim
117243830Sdim    setSourceManager(Info.getSourceManager());
118251662Sdim  }
119243830Sdim
120239462Sdim#ifndef NDEBUG
121243830Sdim  // Debug build tracks unparsed files for possible
122243830Sdim  // unparsed expected-* directives.
123243830Sdim  if (SrcManager) {
124243830Sdim    SourceLocation Loc = Info.getLocation();
125243830Sdim    if (Loc.isValid()) {
126243830Sdim      ParsedStatus PS = IsUnparsed;
127243830Sdim
128243830Sdim      Loc = SrcManager->getExpansionLoc(Loc);
129243830Sdim      FileID FID = SrcManager->getFileID(Loc);
130243830Sdim
131243830Sdim      const FileEntry *FE = SrcManager->getFileEntryForID(FID);
132243830Sdim      if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
133243830Sdim        // If the file is a modules header file it shall not be parsed
134243830Sdim        // for expected-* directives.
135243830Sdim        HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
136243830Sdim        if (HS.findModuleForHeader(FE))
137243830Sdim          PS = IsUnparsedNoDirectives;
138243830Sdim      }
139243830Sdim
140243830Sdim      UpdateParsedFileStatus(*SrcManager, FID, PS);
141243830Sdim    }
142226586Sdim  }
143239462Sdim#endif
144243830Sdim
145226586Sdim  // Send the diagnostic to the buffer, we will check it once we reach the end
146226586Sdim  // of the source file (or are destructed).
147226586Sdim  Buffer->HandleDiagnostic(DiagLevel, Info);
148226586Sdim}
149226586Sdim
150226586Sdim//===----------------------------------------------------------------------===//
151226586Sdim// Checking diagnostics implementation.
152226586Sdim//===----------------------------------------------------------------------===//
153226586Sdim
154226586Sdimtypedef TextDiagnosticBuffer::DiagList DiagList;
155226586Sdimtypedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
156226586Sdim
157226586Sdimnamespace {
158226586Sdim
159226586Sdim/// StandardDirective - Directive with string matching.
160226586Sdim///
161226586Sdimclass StandardDirective : public Directive {
162226586Sdimpublic:
163239462Sdim  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
164276479Sdim                    bool MatchAnyLine, StringRef Text, unsigned Min,
165276479Sdim                    unsigned Max)
166276479Sdim    : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
167226586Sdim
168276479Sdim  bool isValid(std::string &Error) override {
169226586Sdim    // all strings are considered valid; even empty ones
170226586Sdim    return true;
171226586Sdim  }
172226586Sdim
173276479Sdim  bool match(StringRef S) override {
174239462Sdim    return S.find(Text) != StringRef::npos;
175226586Sdim  }
176226586Sdim};
177226586Sdim
178226586Sdim/// RegexDirective - Directive with regular-expression matching.
179226586Sdim///
180226586Sdimclass RegexDirective : public Directive {
181226586Sdimpublic:
182239462Sdim  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
183276479Sdim                 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
184276479Sdim                 StringRef RegexStr)
185276479Sdim    : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
186276479Sdim      Regex(RegexStr) { }
187226586Sdim
188276479Sdim  bool isValid(std::string &Error) override {
189296417Sdim    return Regex.isValid(Error);
190226586Sdim  }
191226586Sdim
192276479Sdim  bool match(StringRef S) override {
193226586Sdim    return Regex.match(S);
194226586Sdim  }
195226586Sdim
196226586Sdimprivate:
197226586Sdim  llvm::Regex Regex;
198226586Sdim};
199226586Sdim
200226586Sdimclass ParseHelper
201226586Sdim{
202226586Sdimpublic:
203239462Sdim  ParseHelper(StringRef S)
204276479Sdim    : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {}
205226586Sdim
206226586Sdim  // Return true if string literal is next.
207226586Sdim  bool Next(StringRef S) {
208226586Sdim    P = C;
209226586Sdim    PEnd = C + S.size();
210226586Sdim    if (PEnd > End)
211226586Sdim      return false;
212226586Sdim    return !memcmp(P, S.data(), S.size());
213226586Sdim  }
214226586Sdim
215226586Sdim  // Return true if number is next.
216226586Sdim  // Output N only if number is next.
217226586Sdim  bool Next(unsigned &N) {
218226586Sdim    unsigned TMP = 0;
219226586Sdim    P = C;
220226586Sdim    for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
221226586Sdim      TMP *= 10;
222226586Sdim      TMP += P[0] - '0';
223226586Sdim    }
224226586Sdim    if (P == C)
225226586Sdim      return false;
226226586Sdim    PEnd = P;
227226586Sdim    N = TMP;
228226586Sdim    return true;
229226586Sdim  }
230226586Sdim
231226586Sdim  // Return true if string literal is found.
232226586Sdim  // When true, P marks begin-position of S in content.
233243830Sdim  bool Search(StringRef S, bool EnsureStartOfWord = false) {
234243830Sdim    do {
235243830Sdim      P = std::search(C, End, S.begin(), S.end());
236243830Sdim      PEnd = P + S.size();
237243830Sdim      if (P == End)
238243830Sdim        break;
239243830Sdim      if (!EnsureStartOfWord
240243830Sdim            // Check if string literal starts a new word.
241249423Sdim            || P == Begin || isWhitespace(P[-1])
242276479Sdim            // Or it could be preceded by the start of a comment.
243243830Sdim            || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
244243830Sdim                                &&  P[-2] == '/'))
245243830Sdim        return true;
246243830Sdim      // Otherwise, skip and search again.
247243830Sdim    } while (Advance());
248243830Sdim    return false;
249226586Sdim  }
250226586Sdim
251276479Sdim  // Return true if a CloseBrace that closes the OpenBrace at the current nest
252276479Sdim  // level is found. When true, P marks begin-position of CloseBrace.
253276479Sdim  bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
254276479Sdim    unsigned Depth = 1;
255276479Sdim    P = C;
256276479Sdim    while (P < End) {
257276479Sdim      StringRef S(P, End - P);
258276479Sdim      if (S.startswith(OpenBrace)) {
259276479Sdim        ++Depth;
260276479Sdim        P += OpenBrace.size();
261276479Sdim      } else if (S.startswith(CloseBrace)) {
262276479Sdim        --Depth;
263276479Sdim        if (Depth == 0) {
264276479Sdim          PEnd = P + CloseBrace.size();
265276479Sdim          return true;
266276479Sdim        }
267276479Sdim        P += CloseBrace.size();
268276479Sdim      } else {
269276479Sdim        ++P;
270276479Sdim      }
271276479Sdim    }
272276479Sdim    return false;
273276479Sdim  }
274276479Sdim
275226586Sdim  // Advance 1-past previous next/search.
276226586Sdim  // Behavior is undefined if previous next/search failed.
277226586Sdim  bool Advance() {
278226586Sdim    C = PEnd;
279226586Sdim    return C < End;
280226586Sdim  }
281226586Sdim
282226586Sdim  // Skip zero or more whitespace.
283226586Sdim  void SkipWhitespace() {
284249423Sdim    for (; C < End && isWhitespace(*C); ++C)
285226586Sdim      ;
286226586Sdim  }
287226586Sdim
288226586Sdim  // Return true if EOF reached.
289226586Sdim  bool Done() {
290226586Sdim    return !(C < End);
291226586Sdim  }
292226586Sdim
293226586Sdim  const char * const Begin; // beginning of expected content
294226586Sdim  const char * const End;   // end of expected content (1-past)
295226586Sdim  const char *C;            // position of next char in content
296226586Sdim  const char *P;
297226586Sdim
298226586Sdimprivate:
299226586Sdim  const char *PEnd; // previous next/search subject end (1-past)
300226586Sdim};
301226586Sdim
302226586Sdim} // namespace anonymous
303226586Sdim
304226586Sdim/// ParseDirective - Go through the comment and see if it indicates expected
305226586Sdim/// diagnostics. If so, then put them in the appropriate directive list.
306226586Sdim///
307239462Sdim/// Returns true if any valid directives were found.
308239462Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
309251662Sdim                           Preprocessor *PP, SourceLocation Pos,
310243830Sdim                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
311251662Sdim  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
312251662Sdim
313226586Sdim  // A single comment may contain multiple directives.
314239462Sdim  bool FoundDirective = false;
315239462Sdim  for (ParseHelper PH(S); !PH.Done();) {
316239462Sdim    // Search for token: expected
317243830Sdim    if (!PH.Search("expected", true))
318226586Sdim      break;
319226586Sdim    PH.Advance();
320226586Sdim
321239462Sdim    // Next token: -
322226586Sdim    if (!PH.Next("-"))
323226586Sdim      continue;
324226586Sdim    PH.Advance();
325226586Sdim
326239462Sdim    // Next token: { error | warning | note }
327276479Sdim    DirectiveList *DL = nullptr;
328226586Sdim    if (PH.Next("error"))
329276479Sdim      DL = ED ? &ED->Errors : nullptr;
330226586Sdim    else if (PH.Next("warning"))
331276479Sdim      DL = ED ? &ED->Warnings : nullptr;
332276479Sdim    else if (PH.Next("remark"))
333276479Sdim      DL = ED ? &ED->Remarks : nullptr;
334226586Sdim    else if (PH.Next("note"))
335276479Sdim      DL = ED ? &ED->Notes : nullptr;
336243830Sdim    else if (PH.Next("no-diagnostics")) {
337243830Sdim      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
338243830Sdim        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
339243830Sdim          << /*IsExpectedNoDiagnostics=*/true;
340243830Sdim      else
341243830Sdim        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
342226586Sdim      continue;
343243830Sdim    } else
344243830Sdim      continue;
345226586Sdim    PH.Advance();
346226586Sdim
347243830Sdim    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
348243830Sdim      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
349243830Sdim        << /*IsExpectedNoDiagnostics=*/false;
350243830Sdim      continue;
351243830Sdim    }
352243830Sdim    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
353243830Sdim
354239462Sdim    // If a directive has been found but we're not interested
355239462Sdim    // in storing the directive information, return now.
356239462Sdim    if (!DL)
357239462Sdim      return true;
358239462Sdim
359239462Sdim    // Default directive kind.
360226586Sdim    bool RegexKind = false;
361226586Sdim    const char* KindStr = "string";
362226586Sdim
363239462Sdim    // Next optional token: -
364226586Sdim    if (PH.Next("-re")) {
365226586Sdim      PH.Advance();
366226586Sdim      RegexKind = true;
367226586Sdim      KindStr = "regex";
368226586Sdim    }
369226586Sdim
370239462Sdim    // Next optional token: @
371239462Sdim    SourceLocation ExpectedLoc;
372276479Sdim    bool MatchAnyLine = false;
373239462Sdim    if (!PH.Next("@")) {
374239462Sdim      ExpectedLoc = Pos;
375239462Sdim    } else {
376239462Sdim      PH.Advance();
377239462Sdim      unsigned Line = 0;
378239462Sdim      bool FoundPlus = PH.Next("+");
379239462Sdim      if (FoundPlus || PH.Next("-")) {
380239462Sdim        // Relative to current line.
381239462Sdim        PH.Advance();
382239462Sdim        bool Invalid = false;
383239462Sdim        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
384239462Sdim        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
385239462Sdim          if (FoundPlus) ExpectedLine += Line;
386239462Sdim          else ExpectedLine -= Line;
387239462Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
388239462Sdim        }
389251662Sdim      } else if (PH.Next(Line)) {
390239462Sdim        // Absolute line number.
391251662Sdim        if (Line > 0)
392251662Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
393251662Sdim      } else if (PP && PH.Search(":")) {
394251662Sdim        // Specific source file.
395251662Sdim        StringRef Filename(PH.C, PH.P-PH.C);
396251662Sdim        PH.Advance();
397251662Sdim
398251662Sdim        // Lookup file via Preprocessor, like a #include.
399251662Sdim        const DirectoryLookup *CurDir;
400280031Sdim        const FileEntry *FE =
401280031Sdim            PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
402280031Sdim                           nullptr, nullptr, nullptr);
403251662Sdim        if (!FE) {
404251662Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
405251662Sdim                       diag::err_verify_missing_file) << Filename << KindStr;
406251662Sdim          continue;
407251662Sdim        }
408251662Sdim
409251662Sdim        if (SM.translateFile(FE).isInvalid())
410251662Sdim          SM.createFileID(FE, Pos, SrcMgr::C_User);
411251662Sdim
412239462Sdim        if (PH.Next(Line) && Line > 0)
413251662Sdim          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
414276479Sdim        else if (PH.Next("*")) {
415276479Sdim          MatchAnyLine = true;
416276479Sdim          ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
417276479Sdim        }
418239462Sdim      }
419239462Sdim
420239462Sdim      if (ExpectedLoc.isInvalid()) {
421239462Sdim        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
422239462Sdim                     diag::err_verify_missing_line) << KindStr;
423239462Sdim        continue;
424239462Sdim      }
425239462Sdim      PH.Advance();
426239462Sdim    }
427239462Sdim
428239462Sdim    // Skip optional whitespace.
429226586Sdim    PH.SkipWhitespace();
430226586Sdim
431239462Sdim    // Next optional token: positive integer or a '+'.
432239462Sdim    unsigned Min = 1;
433239462Sdim    unsigned Max = 1;
434239462Sdim    if (PH.Next(Min)) {
435226586Sdim      PH.Advance();
436239462Sdim      // A positive integer can be followed by a '+' meaning min
437239462Sdim      // or more, or by a '-' meaning a range from min to max.
438239462Sdim      if (PH.Next("+")) {
439239462Sdim        Max = Directive::MaxCount;
440239462Sdim        PH.Advance();
441239462Sdim      } else if (PH.Next("-")) {
442239462Sdim        PH.Advance();
443239462Sdim        if (!PH.Next(Max) || Max < Min) {
444239462Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
445239462Sdim                       diag::err_verify_invalid_range) << KindStr;
446239462Sdim          continue;
447239462Sdim        }
448239462Sdim        PH.Advance();
449239462Sdim      } else {
450239462Sdim        Max = Min;
451239462Sdim      }
452239462Sdim    } else if (PH.Next("+")) {
453239462Sdim      // '+' on its own means "1 or more".
454239462Sdim      Max = Directive::MaxCount;
455234353Sdim      PH.Advance();
456234353Sdim    }
457226586Sdim
458239462Sdim    // Skip optional whitespace.
459226586Sdim    PH.SkipWhitespace();
460226586Sdim
461239462Sdim    // Next token: {{
462226586Sdim    if (!PH.Next("{{")) {
463239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
464239462Sdim                   diag::err_verify_missing_start) << KindStr;
465226586Sdim      continue;
466226586Sdim    }
467226586Sdim    PH.Advance();
468226586Sdim    const char* const ContentBegin = PH.C; // mark content begin
469226586Sdim
470239462Sdim    // Search for token: }}
471276479Sdim    if (!PH.SearchClosingBrace("{{", "}}")) {
472239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
473239462Sdim                   diag::err_verify_missing_end) << KindStr;
474226586Sdim      continue;
475226586Sdim    }
476226586Sdim    const char* const ContentEnd = PH.P; // mark content end
477226586Sdim    PH.Advance();
478226586Sdim
479239462Sdim    // Build directive text; convert \n to newlines.
480226586Sdim    std::string Text;
481226586Sdim    StringRef NewlineStr = "\\n";
482226586Sdim    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
483226586Sdim    size_t CPos = 0;
484226586Sdim    size_t FPos;
485226586Sdim    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
486226586Sdim      Text += Content.substr(CPos, FPos-CPos);
487226586Sdim      Text += '\n';
488226586Sdim      CPos = FPos + NewlineStr.size();
489226586Sdim    }
490226586Sdim    if (Text.empty())
491226586Sdim      Text.assign(ContentBegin, ContentEnd);
492226586Sdim
493276479Sdim    // Check that regex directives contain at least one regex.
494276479Sdim    if (RegexKind && Text.find("{{") == StringRef::npos) {
495276479Sdim      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
496276479Sdim                   diag::err_verify_missing_regex) << Text;
497276479Sdim      return false;
498276479Sdim    }
499276479Sdim
500239462Sdim    // Construct new directive.
501280031Sdim    std::unique_ptr<Directive> D = Directive::create(
502280031Sdim        RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
503276479Sdim
504226586Sdim    std::string Error;
505239462Sdim    if (D->isValid(Error)) {
506280031Sdim      DL->push_back(std::move(D));
507239462Sdim      FoundDirective = true;
508239462Sdim    } else {
509239462Sdim      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
510239462Sdim                   diag::err_verify_invalid_content)
511226586Sdim        << KindStr << Error;
512226586Sdim    }
513226586Sdim  }
514239462Sdim
515239462Sdim  return FoundDirective;
516226586Sdim}
517226586Sdim
518239462Sdim/// HandleComment - Hook into the preprocessor and extract comments containing
519239462Sdim///  expected errors and warnings.
520239462Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
521239462Sdim                                             SourceRange Comment) {
522239462Sdim  SourceManager &SM = PP.getSourceManager();
523251662Sdim
524251662Sdim  // If this comment is for a different source manager, ignore it.
525251662Sdim  if (SrcManager && &SM != SrcManager)
526251662Sdim    return false;
527251662Sdim
528239462Sdim  SourceLocation CommentBegin = Comment.getBegin();
529239462Sdim
530239462Sdim  const char *CommentRaw = SM.getCharacterData(CommentBegin);
531239462Sdim  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
532239462Sdim
533239462Sdim  if (C.empty())
534239462Sdim    return false;
535239462Sdim
536239462Sdim  // Fold any "\<EOL>" sequences
537239462Sdim  size_t loc = C.find('\\');
538239462Sdim  if (loc == StringRef::npos) {
539251662Sdim    ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
540239462Sdim    return false;
541239462Sdim  }
542239462Sdim
543239462Sdim  std::string C2;
544239462Sdim  C2.reserve(C.size());
545239462Sdim
546239462Sdim  for (size_t last = 0;; loc = C.find('\\', last)) {
547239462Sdim    if (loc == StringRef::npos || loc == C.size()) {
548239462Sdim      C2 += C.substr(last);
549239462Sdim      break;
550239462Sdim    }
551239462Sdim    C2 += C.substr(last, loc-last);
552239462Sdim    last = loc + 1;
553239462Sdim
554239462Sdim    if (C[last] == '\n' || C[last] == '\r') {
555239462Sdim      ++last;
556239462Sdim
557239462Sdim      // Escape \r\n  or \n\r, but not \n\n.
558239462Sdim      if (last < C.size())
559239462Sdim        if (C[last] == '\n' || C[last] == '\r')
560239462Sdim          if (C[last] != C[last-1])
561239462Sdim            ++last;
562239462Sdim    } else {
563239462Sdim      // This was just a normal backslash.
564239462Sdim      C2 += '\\';
565239462Sdim    }
566239462Sdim  }
567239462Sdim
568239462Sdim  if (!C2.empty())
569251662Sdim    ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
570239462Sdim  return false;
571239462Sdim}
572239462Sdim
573239462Sdim#ifndef NDEBUG
574239462Sdim/// \brief Lex the specified source file to determine whether it contains
575239462Sdim/// any expected-* directives.  As a Lexer is used rather than a full-blown
576239462Sdim/// Preprocessor, directives inside skipped #if blocks will still be found.
577239462Sdim///
578239462Sdim/// \return true if any directives were found.
579243830Sdimstatic bool findDirectives(SourceManager &SM, FileID FID,
580243830Sdim                           const LangOptions &LangOpts) {
581226586Sdim  // Create a raw lexer to pull all the comments out of FID.
582226586Sdim  if (FID.isInvalid())
583239462Sdim    return false;
584226586Sdim
585226586Sdim  // Create a lexer to lex all the tokens of the main file in raw mode.
586226586Sdim  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
587243830Sdim  Lexer RawLex(FID, FromFile, SM, LangOpts);
588226586Sdim
589226586Sdim  // Return comments as tokens, this is how we find expected diagnostics.
590226586Sdim  RawLex.SetCommentRetentionState(true);
591226586Sdim
592226586Sdim  Token Tok;
593226586Sdim  Tok.setKind(tok::comment);
594243830Sdim  VerifyDiagnosticConsumer::DirectiveStatus Status =
595243830Sdim    VerifyDiagnosticConsumer::HasNoDirectives;
596226586Sdim  while (Tok.isNot(tok::eof)) {
597261991Sdim    RawLex.LexFromRawLexer(Tok);
598226586Sdim    if (!Tok.is(tok::comment)) continue;
599226586Sdim
600243830Sdim    std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
601226586Sdim    if (Comment.empty()) continue;
602226586Sdim
603243830Sdim    // Find first directive.
604276479Sdim    if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
605276479Sdim                       Status))
606243830Sdim      return true;
607239462Sdim  }
608243830Sdim  return false;
609226586Sdim}
610239462Sdim#endif // !NDEBUG
611226586Sdim
612239462Sdim/// \brief Takes a list of diagnostics that have been generated but not matched
613239462Sdim/// by an expected-* directive and produces a diagnostic to the user from this.
614239462Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
615239462Sdim                                const_diag_iterator diag_begin,
616239462Sdim                                const_diag_iterator diag_end,
617239462Sdim                                const char *Kind) {
618226586Sdim  if (diag_begin == diag_end) return 0;
619226586Sdim
620234353Sdim  SmallString<256> Fmt;
621226586Sdim  llvm::raw_svector_ostream OS(Fmt);
622226586Sdim  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
623226586Sdim    if (I->first.isInvalid() || !SourceMgr)
624226586Sdim      OS << "\n  (frontend)";
625251662Sdim    else {
626251662Sdim      OS << "\n ";
627251662Sdim      if (const FileEntry *File = SourceMgr->getFileEntryForID(
628251662Sdim                                                SourceMgr->getFileID(I->first)))
629251662Sdim        OS << " File " << File->getName();
630251662Sdim      OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
631251662Sdim    }
632226586Sdim    OS << ": " << I->second;
633226586Sdim  }
634226586Sdim
635239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
636239462Sdim    << Kind << /*Unexpected=*/true << OS.str();
637226586Sdim  return std::distance(diag_begin, diag_end);
638226586Sdim}
639226586Sdim
640239462Sdim/// \brief Takes a list of diagnostics that were expected to have been generated
641239462Sdim/// but were not and produces a diagnostic to the user from this.
642280031Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags,
643280031Sdim                              SourceManager &SourceMgr,
644280031Sdim                              std::vector<Directive *> &DL, const char *Kind) {
645226586Sdim  if (DL.empty())
646226586Sdim    return 0;
647226586Sdim
648234353Sdim  SmallString<256> Fmt;
649226586Sdim  llvm::raw_svector_ostream OS(Fmt);
650280031Sdim  for (auto *DirPtr : DL) {
651280031Sdim    Directive &D = *DirPtr;
652276479Sdim    OS << "\n  File " << SourceMgr.getFilename(D.DiagnosticLoc);
653276479Sdim    if (D.MatchAnyLine)
654276479Sdim      OS << " Line *";
655276479Sdim    else
656276479Sdim      OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
657239462Sdim    if (D.DirectiveLoc != D.DiagnosticLoc)
658239462Sdim      OS << " (directive at "
659251662Sdim         << SourceMgr.getFilename(D.DirectiveLoc) << ':'
660251662Sdim         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
661226586Sdim    OS << ": " << D.Text;
662226586Sdim  }
663226586Sdim
664239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
665239462Sdim    << Kind << /*Unexpected=*/false << OS.str();
666226586Sdim  return DL.size();
667226586Sdim}
668226586Sdim
669251662Sdim/// \brief Determine whether two source locations come from the same file.
670251662Sdimstatic bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
671251662Sdim                           SourceLocation DiagnosticLoc) {
672251662Sdim  while (DiagnosticLoc.isMacroID())
673251662Sdim    DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
674251662Sdim
675261991Sdim  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
676251662Sdim    return true;
677251662Sdim
678251662Sdim  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
679261991Sdim  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
680251662Sdim    return true;
681251662Sdim
682251662Sdim  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
683251662Sdim}
684251662Sdim
685226586Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the
686226586Sdim/// the difference between them.
687226586Sdim///
688226586Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
689226586Sdim                           const char *Label,
690226586Sdim                           DirectiveList &Left,
691226586Sdim                           const_diag_iterator d2_begin,
692288943Sdim                           const_diag_iterator d2_end,
693288943Sdim                           bool IgnoreUnexpected) {
694280031Sdim  std::vector<Directive *> LeftOnly;
695226586Sdim  DiagList Right(d2_begin, d2_end);
696226586Sdim
697280031Sdim  for (auto &Owner : Left) {
698280031Sdim    Directive &D = *Owner;
699239462Sdim    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
700226586Sdim
701239462Sdim    for (unsigned i = 0; i < D.Max; ++i) {
702226586Sdim      DiagList::iterator II, IE;
703226586Sdim      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
704276479Sdim        if (!D.MatchAnyLine) {
705276479Sdim          unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
706276479Sdim          if (LineNo1 != LineNo2)
707276479Sdim            continue;
708276479Sdim        }
709226586Sdim
710251662Sdim        if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
711251662Sdim          continue;
712251662Sdim
713226586Sdim        const std::string &RightText = II->second;
714239462Sdim        if (D.match(RightText))
715226586Sdim          break;
716226586Sdim      }
717226586Sdim      if (II == IE) {
718226586Sdim        // Not found.
719239462Sdim        if (i >= D.Min) break;
720280031Sdim        LeftOnly.push_back(&D);
721226586Sdim      } else {
722226586Sdim        // Found. The same cannot be found twice.
723226586Sdim        Right.erase(II);
724226586Sdim      }
725226586Sdim    }
726226586Sdim  }
727226586Sdim  // Now all that's left in Right are those that were not matched.
728239462Sdim  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
729288943Sdim  if (!IgnoreUnexpected)
730288943Sdim    num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
731234353Sdim  return num;
732226586Sdim}
733226586Sdim
734226586Sdim/// CheckResults - This compares the expected results to those that
735226586Sdim/// were actually reported. It emits any discrepencies. Return "true" if there
736226586Sdim/// were problems. Return "false" otherwise.
737226586Sdim///
738226586Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
739226586Sdim                             const TextDiagnosticBuffer &Buffer,
740226586Sdim                             ExpectedData &ED) {
741226586Sdim  // We want to capture the delta between what was expected and what was
742226586Sdim  // seen.
743226586Sdim  //
744226586Sdim  //   Expected \ Seen - set expected but not seen
745226586Sdim  //   Seen \ Expected - set seen but not expected
746226586Sdim  unsigned NumProblems = 0;
747226586Sdim
748288943Sdim  const DiagnosticLevelMask DiagMask =
749288943Sdim    Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
750288943Sdim
751226586Sdim  // See if there are error mismatches.
752226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
753288943Sdim                            Buffer.err_begin(), Buffer.err_end(),
754288943Sdim                            bool(DiagnosticLevelMask::Error & DiagMask));
755226586Sdim
756226586Sdim  // See if there are warning mismatches.
757226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
758288943Sdim                            Buffer.warn_begin(), Buffer.warn_end(),
759288943Sdim                            bool(DiagnosticLevelMask::Warning & DiagMask));
760226586Sdim
761276479Sdim  // See if there are remark mismatches.
762276479Sdim  NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
763288943Sdim                            Buffer.remark_begin(), Buffer.remark_end(),
764288943Sdim                            bool(DiagnosticLevelMask::Remark & DiagMask));
765276479Sdim
766226586Sdim  // See if there are note mismatches.
767226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
768288943Sdim                            Buffer.note_begin(), Buffer.note_end(),
769288943Sdim                            bool(DiagnosticLevelMask::Note & DiagMask));
770226586Sdim
771226586Sdim  return NumProblems;
772226586Sdim}
773226586Sdim
774243830Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
775243830Sdim                                                      FileID FID,
776243830Sdim                                                      ParsedStatus PS) {
777243830Sdim  // Check SourceManager hasn't changed.
778243830Sdim  setSourceManager(SM);
779243830Sdim
780243830Sdim#ifndef NDEBUG
781243830Sdim  if (FID.isInvalid())
782243830Sdim    return;
783243830Sdim
784243830Sdim  const FileEntry *FE = SM.getFileEntryForID(FID);
785243830Sdim
786243830Sdim  if (PS == IsParsed) {
787243830Sdim    // Move the FileID from the unparsed set to the parsed set.
788243830Sdim    UnparsedFiles.erase(FID);
789243830Sdim    ParsedFiles.insert(std::make_pair(FID, FE));
790243830Sdim  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
791243830Sdim    // Add the FileID to the unparsed set if we haven't seen it before.
792243830Sdim
793243830Sdim    // Check for directives.
794243830Sdim    bool FoundDirectives;
795243830Sdim    if (PS == IsUnparsedNoDirectives)
796243830Sdim      FoundDirectives = false;
797243830Sdim    else
798243830Sdim      FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
799243830Sdim
800243830Sdim    // Add the FileID to the unparsed set.
801243830Sdim    UnparsedFiles.insert(std::make_pair(FID,
802243830Sdim                                      UnparsedFileStatus(FE, FoundDirectives)));
803243830Sdim  }
804243830Sdim#endif
805243830Sdim}
806243830Sdim
807226586Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() {
808226586Sdim  // Ensure any diagnostics go to the primary client.
809280031Sdim  DiagnosticConsumer *CurClient = Diags.getClient();
810280031Sdim  std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
811226586Sdim  Diags.setClient(PrimaryClient, false);
812226586Sdim
813243830Sdim#ifndef NDEBUG
814243830Sdim  // In a debug build, scan through any files that may have been missed
815243830Sdim  // during parsing and issue a fatal error if directives are contained
816243830Sdim  // within these files.  If a fatal error occurs, this suggests that
817243830Sdim  // this file is being parsed separately from the main file, in which
818243830Sdim  // case consider moving the directives to the correct place, if this
819243830Sdim  // is applicable.
820243830Sdim  if (UnparsedFiles.size() > 0) {
821243830Sdim    // Generate a cache of parsed FileEntry pointers for alias lookups.
822243830Sdim    llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
823243830Sdim    for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
824243830Sdim                                End = ParsedFiles.end(); I != End; ++I) {
825243830Sdim      if (const FileEntry *FE = I->second)
826243830Sdim        ParsedFileCache.insert(FE);
827243830Sdim    }
828239462Sdim
829243830Sdim    // Iterate through list of unparsed files.
830243830Sdim    for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
831243830Sdim                                  End = UnparsedFiles.end(); I != End; ++I) {
832243830Sdim      const UnparsedFileStatus &Status = I->second;
833243830Sdim      const FileEntry *FE = Status.getFile();
834243830Sdim
835243830Sdim      // Skip files that have been parsed via an alias.
836243830Sdim      if (FE && ParsedFileCache.count(FE))
837239462Sdim        continue;
838239462Sdim
839243830Sdim      // Report a fatal error if this file contained directives.
840243830Sdim      if (Status.foundDirectives()) {
841239462Sdim        llvm::report_fatal_error(Twine("-verify directives found after rather"
842239462Sdim                                       " than during normal parsing of ",
843243830Sdim                                 StringRef(FE ? FE->getName() : "(unknown)")));
844243830Sdim      }
845226586Sdim    }
846226586Sdim
847243830Sdim    // UnparsedFiles has been processed now, so clear it.
848243830Sdim    UnparsedFiles.clear();
849243830Sdim  }
850243830Sdim#endif // !NDEBUG
851243830Sdim
852243830Sdim  if (SrcManager) {
853243830Sdim    // Produce an error if no expected-* directives could be found in the
854243830Sdim    // source file(s) processed.
855243830Sdim    if (Status == HasNoDirectives) {
856243830Sdim      Diags.Report(diag::err_verify_no_directives).setForceEmit();
857243830Sdim      ++NumErrors;
858243830Sdim      Status = HasNoDirectivesReported;
859243830Sdim    }
860243830Sdim
861226586Sdim    // Check that the expected diagnostics occurred.
862243830Sdim    NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
863226586Sdim  } else {
864288943Sdim    const DiagnosticLevelMask DiagMask =
865288943Sdim        ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
866288943Sdim    if (bool(DiagnosticLevelMask::Error & DiagMask))
867288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
868288943Sdim                                   Buffer->err_end(), "error");
869288943Sdim    if (bool(DiagnosticLevelMask::Warning & DiagMask))
870288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
871288943Sdim                                   Buffer->warn_end(), "warn");
872288943Sdim    if (bool(DiagnosticLevelMask::Remark & DiagMask))
873288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
874288943Sdim                                   Buffer->remark_end(), "remark");
875288943Sdim    if (bool(DiagnosticLevelMask::Note & DiagMask))
876288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
877288943Sdim                                   Buffer->note_end(), "note");
878226586Sdim  }
879226586Sdim
880280031Sdim  Diags.setClient(CurClient, Owner.release() != nullptr);
881226586Sdim
882226586Sdim  // Reset the buffer, we have processed all the diagnostics in it.
883226586Sdim  Buffer.reset(new TextDiagnosticBuffer());
884276479Sdim  ED.Reset();
885226586Sdim}
886226586Sdim
887280031Sdimstd::unique_ptr<Directive> Directive::create(bool RegexKind,
888280031Sdim                                             SourceLocation DirectiveLoc,
889280031Sdim                                             SourceLocation DiagnosticLoc,
890280031Sdim                                             bool MatchAnyLine, StringRef Text,
891280031Sdim                                             unsigned Min, unsigned Max) {
892276479Sdim  if (!RegexKind)
893280031Sdim    return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
894280031Sdim                                                MatchAnyLine, Text, Min, Max);
895276479Sdim
896276479Sdim  // Parse the directive into a regular expression.
897276479Sdim  std::string RegexStr;
898276479Sdim  StringRef S = Text;
899276479Sdim  while (!S.empty()) {
900276479Sdim    if (S.startswith("{{")) {
901276479Sdim      S = S.drop_front(2);
902276479Sdim      size_t RegexMatchLength = S.find("}}");
903276479Sdim      assert(RegexMatchLength != StringRef::npos);
904276479Sdim      // Append the regex, enclosed in parentheses.
905276479Sdim      RegexStr += "(";
906276479Sdim      RegexStr.append(S.data(), RegexMatchLength);
907276479Sdim      RegexStr += ")";
908276479Sdim      S = S.drop_front(RegexMatchLength + 2);
909276479Sdim    } else {
910276479Sdim      size_t VerbatimMatchLength = S.find("{{");
911276479Sdim      if (VerbatimMatchLength == StringRef::npos)
912276479Sdim        VerbatimMatchLength = S.size();
913276479Sdim      // Escape and append the fixed string.
914276479Sdim      RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
915276479Sdim      S = S.drop_front(VerbatimMatchLength);
916276479Sdim    }
917276479Sdim  }
918276479Sdim
919280031Sdim  return llvm::make_unique<RegexDirective>(
920280031Sdim      DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr);
921226586Sdim}
922