VerifyDiagnosticConsumer.cpp revision 243830
1243789Sdim//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
2243789Sdim//
3243789Sdim//                     The LLVM Compiler Infrastructure
4243789Sdim//
5243789Sdim// This file is distributed under the University of Illinois Open Source
6243789Sdim// License. See LICENSE.TXT for details.
7243789Sdim//
8243789Sdim//===----------------------------------------------------------------------===//
9243789Sdim//
10243789Sdim// This is a concrete diagnostic client, which buffers the diagnostic messages.
11243789Sdim//
12249423Sdim//===----------------------------------------------------------------------===//
13243789Sdim
14243789Sdim#include "clang/Basic/FileManager.h"
15249423Sdim#include "clang/Frontend/VerifyDiagnosticConsumer.h"
16243789Sdim#include "clang/Frontend/FrontendDiagnostic.h"
17243789Sdim#include "clang/Frontend/TextDiagnosticBuffer.h"
18243789Sdim#include "clang/Lex/HeaderSearch.h"
19243789Sdim#include "clang/Lex/Preprocessor.h"
20243789Sdim#include "llvm/ADT/SmallString.h"
21243789Sdim#include "llvm/Support/Regex.h"
22243789Sdim#include "llvm/Support/raw_ostream.h"
23249423Sdim#include <cctype>
24243789Sdim
25243789Sdimusing namespace clang;
26243789Sdimtypedef VerifyDiagnosticConsumer::Directive Directive;
27243789Sdimtypedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
28249423Sdimtypedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
29243789Sdim
30243789SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
31243789Sdim  : Diags(_Diags),
32243789Sdim    PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
33243789Sdim    Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
34243789Sdim    LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives)
35249423Sdim{
36243789Sdim  Diags.takeClient();
37243789Sdim  if (Diags.hasSourceManager())
38243789Sdim    setSourceManager(Diags.getSourceManager());
39243789Sdim}
40249423Sdim
41249423SdimVerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
42249423Sdim  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
43243789Sdim  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
44249423Sdim  SrcManager = 0;
45243789Sdim  CheckDiagnostics();
46243789Sdim  Diags.takeClient();
47249423Sdim  if (OwnsPrimaryClient)
48249423Sdim    delete PrimaryClient;
49249423Sdim}
50249423Sdim
51249423Sdim#ifndef NDEBUG
52249423Sdimnamespace {
53243789Sdimclass VerifyFileTracker : public PPCallbacks {
54243789Sdim  VerifyDiagnosticConsumer &Verify;
55243789Sdim  SourceManager &SM;
56243789Sdim
57249423Sdimpublic:
58249423Sdim  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
59249423Sdim    : Verify(Verify), SM(SM) { }
60249423Sdim
61243789Sdim  /// \brief Hook into the preprocessor and update the list of parsed
62249423Sdim  /// files when the preprocessor indicates a new file is entered.
63243789Sdim  virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
64249423Sdim                           SrcMgr::CharacteristicKind FileType,
65243789Sdim                           FileID PrevFID) {
66249423Sdim    Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
67249423Sdim                                  VerifyDiagnosticConsumer::IsParsed);
68249423Sdim  }
69249423Sdim};
70249423Sdim} // End anonymous namespace.
71249423Sdim#endif
72249423Sdim
73249423Sdim// DiagnosticConsumer interface.
74249423Sdim
75249423Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
76249423Sdim                                               const Preprocessor *PP) {
77249423Sdim  // Attach comment handler on first invocation.
78249423Sdim  if (++ActiveSourceFiles == 1) {
79243789Sdim    if (PP) {
80243789Sdim      CurrentPreprocessor = PP;
81243789Sdim      this->LangOpts = &LangOpts;
82249423Sdim      setSourceManager(PP->getSourceManager());
83249423Sdim      const_cast<Preprocessor*>(PP)->addCommentHandler(this);
84249423Sdim#ifndef NDEBUG
85243789Sdim      // Debug build tracks parsed files.
86243789Sdim      VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager);
87249423Sdim      const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
88249423Sdim#endif
89243789Sdim    }
90243789Sdim  }
91243789Sdim
92243789Sdim  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
93249423Sdim  PrimaryClient->BeginSourceFile(LangOpts, PP);
94243789Sdim}
95249423Sdim
96249423Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() {
97249423Sdim  assert(ActiveSourceFiles && "No active source files!");
98249423Sdim  PrimaryClient->EndSourceFile();
99249423Sdim
100249423Sdim  // Detach comment handler once last active source file completed.
101249423Sdim  if (--ActiveSourceFiles == 0) {
102249423Sdim    if (CurrentPreprocessor)
103249423Sdim      const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
104243789Sdim
105249423Sdim    // Check diagnostics once last file completed.
106249423Sdim    CheckDiagnostics();
107249423Sdim    CurrentPreprocessor = 0;
108249423Sdim    LangOpts = 0;
109249423Sdim  }
110249423Sdim}
111243789Sdim
112249423Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic(
113249423Sdim      DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
114249423Sdim  if (Info.hasSourceManager())
115243789Sdim    setSourceManager(Info.getSourceManager());
116243789Sdim
117249423Sdim#ifndef NDEBUG
118243789Sdim  // Debug build tracks unparsed files for possible
119249423Sdim  // unparsed expected-* directives.
120249423Sdim  if (SrcManager) {
121249423Sdim    SourceLocation Loc = Info.getLocation();
122249423Sdim    if (Loc.isValid()) {
123249423Sdim      ParsedStatus PS = IsUnparsed;
124249423Sdim
125243789Sdim      Loc = SrcManager->getExpansionLoc(Loc);
126243789Sdim      FileID FID = SrcManager->getFileID(Loc);
127243789Sdim
128243789Sdim      const FileEntry *FE = SrcManager->getFileEntryForID(FID);
129243789Sdim      if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
130243789Sdim        // If the file is a modules header file it shall not be parsed
131249423Sdim        // for expected-* directives.
132243789Sdim        HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
133243789Sdim        if (HS.findModuleForHeader(FE))
134243789Sdim          PS = IsUnparsedNoDirectives;
135243789Sdim      }
136243789Sdim
137243789Sdim      UpdateParsedFileStatus(*SrcManager, FID, PS);
138243789Sdim    }
139243789Sdim  }
140249423Sdim#endif
141243789Sdim
142249423Sdim  // Send the diagnostic to the buffer, we will check it once we reach the end
143243789Sdim  // of the source file (or are destructed).
144243789Sdim  Buffer->HandleDiagnostic(DiagLevel, Info);
145249423Sdim}
146243789Sdim
147249423Sdim//===----------------------------------------------------------------------===//
148249423Sdim// Checking diagnostics implementation.
149249423Sdim//===----------------------------------------------------------------------===//
150249423Sdim
151249423Sdimtypedef TextDiagnosticBuffer::DiagList DiagList;
152249423Sdimtypedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
153249423Sdim
154243789Sdimnamespace {
155243789Sdim
156243789Sdim/// StandardDirective - Directive with string matching.
157249423Sdim///
158243789Sdimclass StandardDirective : public Directive {
159249423Sdimpublic:
160249423Sdim  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
161243789Sdim                    StringRef Text, unsigned Min, unsigned Max)
162243789Sdim    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
163243789Sdim
164249423Sdim  virtual bool isValid(std::string &Error) {
165243789Sdim    // all strings are considered valid; even empty ones
166243789Sdim    return true;
167249423Sdim  }
168249423Sdim
169249423Sdim  virtual bool match(StringRef S) {
170249423Sdim    return S.find(Text) != StringRef::npos;
171249423Sdim  }
172249423Sdim};
173249423Sdim
174249423Sdim/// RegexDirective - Directive with regular-expression matching.
175249423Sdim///
176249423Sdimclass RegexDirective : public Directive {
177249423Sdimpublic:
178243789Sdim  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
179243789Sdim                 StringRef Text, unsigned Min, unsigned Max)
180243789Sdim    : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
181243789Sdim
182249423Sdim  virtual bool isValid(std::string &Error) {
183249423Sdim    if (Regex.isValid(Error))
184249423Sdim      return true;
185249423Sdim    return false;
186249423Sdim  }
187249423Sdim
188249423Sdim  virtual bool match(StringRef S) {
189249423Sdim    return Regex.match(S);
190249423Sdim  }
191249423Sdim
192249423Sdimprivate:
193249423Sdim  llvm::Regex Regex;
194249423Sdim};
195243789Sdim
196243789Sdimclass ParseHelper
197243789Sdim{
198243789Sdimpublic:
199243789Sdim  ParseHelper(StringRef S)
200243789Sdim    : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
201243789Sdim
202243789Sdim  // Return true if string literal is next.
203249423Sdim  bool Next(StringRef S) {
204249423Sdim    P = C;
205249423Sdim    PEnd = C + S.size();
206249423Sdim    if (PEnd > End)
207243789Sdim      return false;
208243789Sdim    return !memcmp(P, S.data(), S.size());
209243789Sdim  }
210243789Sdim
211243789Sdim  // Return true if number is next.
212243789Sdim  // Output N only if number is next.
213243789Sdim  bool Next(unsigned &N) {
214249423Sdim    unsigned TMP = 0;
215249423Sdim    P = C;
216249423Sdim    for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
217249423Sdim      TMP *= 10;
218243789Sdim      TMP += P[0] - '0';
219243789Sdim    }
220243789Sdim    if (P == C)
221243789Sdim      return false;
222243789Sdim    PEnd = P;
223243789Sdim    N = TMP;
224249423Sdim    return true;
225243789Sdim  }
226249423Sdim
227249423Sdim  // Return true if string literal is found.
228243789Sdim  // When true, P marks begin-position of S in content.
229249423Sdim  bool Search(StringRef S, bool EnsureStartOfWord = false) {
230249423Sdim    do {
231249423Sdim      P = std::search(C, End, S.begin(), S.end());
232249423Sdim      PEnd = P + S.size();
233249423Sdim      if (P == End)
234249423Sdim        break;
235249423Sdim      if (!EnsureStartOfWord
236249423Sdim            // Check if string literal starts a new word.
237243789Sdim            || P == Begin || isspace(P[-1])
238249423Sdim            // Or it could be preceeded by the start of a comment.
239249423Sdim            || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
240249423Sdim                                &&  P[-2] == '/'))
241249423Sdim        return true;
242249423Sdim      // Otherwise, skip and search again.
243249423Sdim    } while (Advance());
244249423Sdim    return false;
245249423Sdim  }
246249423Sdim
247249423Sdim  // Advance 1-past previous next/search.
248249423Sdim  // Behavior is undefined if previous next/search failed.
249249423Sdim  bool Advance() {
250249423Sdim    C = PEnd;
251249423Sdim    return C < End;
252249423Sdim  }
253249423Sdim
254249423Sdim  // Skip zero or more whitespace.
255249423Sdim  void SkipWhitespace() {
256249423Sdim    for (; C < End && isspace(*C); ++C)
257249423Sdim      ;
258249423Sdim  }
259249423Sdim
260249423Sdim  // Return true if EOF reached.
261249423Sdim  bool Done() {
262249423Sdim    return !(C < End);
263249423Sdim  }
264249423Sdim
265249423Sdim  const char * const Begin; // beginning of expected content
266249423Sdim  const char * const End;   // end of expected content (1-past)
267249423Sdim  const char *C;            // position of next char in content
268249423Sdim  const char *P;
269243789Sdim
270243789Sdimprivate:
271249423Sdim  const char *PEnd; // previous next/search subject end (1-past)
272243789Sdim};
273249423Sdim
274243789Sdim} // namespace anonymous
275249423Sdim
276243789Sdim/// ParseDirective - Go through the comment and see if it indicates expected
277249423Sdim/// diagnostics. If so, then put them in the appropriate directive list.
278249423Sdim///
279249423Sdim/// Returns true if any valid directives were found.
280249423Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
281249423Sdim                           SourceLocation Pos, DiagnosticsEngine &Diags,
282249423Sdim                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
283249423Sdim  // A single comment may contain multiple directives.
284243789Sdim  bool FoundDirective = false;
285249423Sdim  for (ParseHelper PH(S); !PH.Done();) {
286249423Sdim    // Search for token: expected
287249423Sdim    if (!PH.Search("expected", true))
288243789Sdim      break;
289249423Sdim    PH.Advance();
290243789Sdim
291243789Sdim    // Next token: -
292243789Sdim    if (!PH.Next("-"))
293243789Sdim      continue;
294243789Sdim    PH.Advance();
295243789Sdim
296243789Sdim    // Next token: { error | warning | note }
297243789Sdim    DirectiveList* DL = NULL;
298243789Sdim    if (PH.Next("error"))
299243789Sdim      DL = ED ? &ED->Errors : NULL;
300243789Sdim    else if (PH.Next("warning"))
301243789Sdim      DL = ED ? &ED->Warnings : NULL;
302243789Sdim    else if (PH.Next("note"))
303243789Sdim      DL = ED ? &ED->Notes : NULL;
304243789Sdim    else if (PH.Next("no-diagnostics")) {
305243789Sdim      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
306249423Sdim        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
307249423Sdim          << /*IsExpectedNoDiagnostics=*/true;
308243789Sdim      else
309243789Sdim        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
310243789Sdim      continue;
311243789Sdim    } else
312243789Sdim      continue;
313249423Sdim    PH.Advance();
314243789Sdim
315243789Sdim    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
316243789Sdim      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
317243789Sdim        << /*IsExpectedNoDiagnostics=*/false;
318243789Sdim      continue;
319243789Sdim    }
320243789Sdim    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
321243789Sdim
322243789Sdim    // If a directive has been found but we're not interested
323243789Sdim    // in storing the directive information, return now.
324249423Sdim    if (!DL)
325249423Sdim      return true;
326249423Sdim
327249423Sdim    // Default directive kind.
328249423Sdim    bool RegexKind = false;
329243789Sdim    const char* KindStr = "string";
330243789Sdim
331243789Sdim    // Next optional token: -
332249423Sdim    if (PH.Next("-re")) {
333249423Sdim      PH.Advance();
334249423Sdim      RegexKind = true;
335249423Sdim      KindStr = "regex";
336249423Sdim    }
337243789Sdim
338249423Sdim    // Next optional token: @
339243789Sdim    SourceLocation ExpectedLoc;
340249423Sdim    if (!PH.Next("@")) {
341249423Sdim      ExpectedLoc = Pos;
342249423Sdim    } else {
343249423Sdim      PH.Advance();
344249423Sdim      unsigned Line = 0;
345249423Sdim      bool FoundPlus = PH.Next("+");
346249423Sdim      if (FoundPlus || PH.Next("-")) {
347249423Sdim        // Relative to current line.
348243789Sdim        PH.Advance();
349243789Sdim        bool Invalid = false;
350249423Sdim        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
351249423Sdim        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
352249423Sdim          if (FoundPlus) ExpectedLine += Line;
353249423Sdim          else ExpectedLine -= Line;
354249423Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
355249423Sdim        }
356249423Sdim      } else {
357249423Sdim        // Absolute line number.
358249423Sdim        if (PH.Next(Line) && Line > 0)
359243789Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
360243789Sdim      }
361249423Sdim
362249423Sdim      if (ExpectedLoc.isInvalid()) {
363243789Sdim        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
364249423Sdim                     diag::err_verify_missing_line) << KindStr;
365249423Sdim        continue;
366243789Sdim      }
367243789Sdim      PH.Advance();
368243789Sdim    }
369243789Sdim
370243789Sdim    // Skip optional whitespace.
371243789Sdim    PH.SkipWhitespace();
372243789Sdim
373243789Sdim    // Next optional token: positive integer or a '+'.
374243789Sdim    unsigned Min = 1;
375243789Sdim    unsigned Max = 1;
376243789Sdim    if (PH.Next(Min)) {
377243789Sdim      PH.Advance();
378243789Sdim      // A positive integer can be followed by a '+' meaning min
379249423Sdim      // or more, or by a '-' meaning a range from min to max.
380249423Sdim      if (PH.Next("+")) {
381249423Sdim        Max = Directive::MaxCount;
382249423Sdim        PH.Advance();
383249423Sdim      } else if (PH.Next("-")) {
384249423Sdim        PH.Advance();
385249423Sdim        if (!PH.Next(Max) || Max < Min) {
386249423Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
387249423Sdim                       diag::err_verify_invalid_range) << KindStr;
388249423Sdim          continue;
389249423Sdim        }
390249423Sdim        PH.Advance();
391249423Sdim      } else {
392243789Sdim        Max = Min;
393243789Sdim      }
394243789Sdim    } else if (PH.Next("+")) {
395243789Sdim      // '+' on its own means "1 or more".
396249423Sdim      Max = Directive::MaxCount;
397249423Sdim      PH.Advance();
398249423Sdim    }
399249423Sdim
400243789Sdim    // Skip optional whitespace.
401243789Sdim    PH.SkipWhitespace();
402249423Sdim
403249423Sdim    // Next token: {{
404249423Sdim    if (!PH.Next("{{")) {
405249423Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
406249423Sdim                   diag::err_verify_missing_start) << KindStr;
407249423Sdim      continue;
408249423Sdim    }
409249423Sdim    PH.Advance();
410249423Sdim    const char* const ContentBegin = PH.C; // mark content begin
411249423Sdim
412243789Sdim    // Search for token: }}
413243789Sdim    if (!PH.Search("}}")) {
414243789Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
415243789Sdim                   diag::err_verify_missing_end) << KindStr;
416249423Sdim      continue;
417249423Sdim    }
418249423Sdim    const char* const ContentEnd = PH.P; // mark content end
419249423Sdim    PH.Advance();
420249423Sdim
421249423Sdim    // Build directive text; convert \n to newlines.
422249423Sdim    std::string Text;
423249423Sdim    StringRef NewlineStr = "\\n";
424249423Sdim    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
425243789Sdim    size_t CPos = 0;
426243789Sdim    size_t FPos;
427243789Sdim    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
428243789Sdim      Text += Content.substr(CPos, FPos-CPos);
429243789Sdim      Text += '\n';
430243789Sdim      CPos = FPos + NewlineStr.size();
431249423Sdim    }
432243789Sdim    if (Text.empty())
433243789Sdim      Text.assign(ContentBegin, ContentEnd);
434243789Sdim
435243789Sdim    // Construct new directive.
436249423Sdim    Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
437249423Sdim                                     Min, Max);
438249423Sdim    std::string Error;
439249423Sdim    if (D->isValid(Error)) {
440249423Sdim      DL->push_back(D);
441249423Sdim      FoundDirective = true;
442249423Sdim    } else {
443249423Sdim      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
444249423Sdim                   diag::err_verify_invalid_content)
445249423Sdim        << KindStr << Error;
446249423Sdim    }
447249423Sdim  }
448249423Sdim
449243789Sdim  return FoundDirective;
450249423Sdim}
451249423Sdim
452249423Sdim/// HandleComment - Hook into the preprocessor and extract comments containing
453249423Sdim///  expected errors and warnings.
454249423Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
455243789Sdim                                             SourceRange Comment) {
456243789Sdim  SourceManager &SM = PP.getSourceManager();
457243789Sdim  SourceLocation CommentBegin = Comment.getBegin();
458243789Sdim
459243789Sdim  const char *CommentRaw = SM.getCharacterData(CommentBegin);
460249423Sdim  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
461243789Sdim
462243789Sdim  if (C.empty())
463249423Sdim    return false;
464243789Sdim
465243789Sdim  // Fold any "\<EOL>" sequences
466243789Sdim  size_t loc = C.find('\\');
467243789Sdim  if (loc == StringRef::npos) {
468249423Sdim    ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics(), Status);
469243789Sdim    return false;
470243789Sdim  }
471243789Sdim
472243789Sdim  std::string C2;
473249423Sdim  C2.reserve(C.size());
474249423Sdim
475243789Sdim  for (size_t last = 0;; loc = C.find('\\', last)) {
476249423Sdim    if (loc == StringRef::npos || loc == C.size()) {
477249423Sdim      C2 += C.substr(last);
478249423Sdim      break;
479249423Sdim    }
480249423Sdim    C2 += C.substr(last, loc-last);
481249423Sdim    last = loc + 1;
482249423Sdim
483249423Sdim    if (C[last] == '\n' || C[last] == '\r') {
484249423Sdim      ++last;
485249423Sdim
486249423Sdim      // Escape \r\n  or \n\r, but not \n\n.
487243789Sdim      if (last < C.size())
488243789Sdim        if (C[last] == '\n' || C[last] == '\r')
489243789Sdim          if (C[last] != C[last-1])
490243789Sdim            ++last;
491243789Sdim    } else {
492243789Sdim      // This was just a normal backslash.
493249423Sdim      C2 += '\\';
494249423Sdim    }
495249423Sdim  }
496243789Sdim
497243789Sdim  if (!C2.empty())
498249423Sdim    ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics(), Status);
499249423Sdim  return false;
500249423Sdim}
501249423Sdim
502249423Sdim#ifndef NDEBUG
503249423Sdim/// \brief Lex the specified source file to determine whether it contains
504249423Sdim/// any expected-* directives.  As a Lexer is used rather than a full-blown
505243789Sdim/// Preprocessor, directives inside skipped #if blocks will still be found.
506249423Sdim///
507243789Sdim/// \return true if any directives were found.
508243789Sdimstatic bool findDirectives(SourceManager &SM, FileID FID,
509243789Sdim                           const LangOptions &LangOpts) {
510243789Sdim  // Create a raw lexer to pull all the comments out of FID.
511243789Sdim  if (FID.isInvalid())
512243789Sdim    return false;
513249423Sdim
514249423Sdim  // Create a lexer to lex all the tokens of the main file in raw mode.
515249423Sdim  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
516249423Sdim  Lexer RawLex(FID, FromFile, SM, LangOpts);
517249423Sdim
518249423Sdim  // Return comments as tokens, this is how we find expected diagnostics.
519249423Sdim  RawLex.SetCommentRetentionState(true);
520249423Sdim
521243789Sdim  Token Tok;
522243789Sdim  Tok.setKind(tok::comment);
523243789Sdim  VerifyDiagnosticConsumer::DirectiveStatus Status =
524249423Sdim    VerifyDiagnosticConsumer::HasNoDirectives;
525249423Sdim  while (Tok.isNot(tok::eof)) {
526243789Sdim    RawLex.Lex(Tok);
527243789Sdim    if (!Tok.is(tok::comment)) continue;
528243789Sdim
529249423Sdim    std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
530249423Sdim    if (Comment.empty()) continue;
531249423Sdim
532249423Sdim    // Find first directive.
533249423Sdim    if (ParseDirective(Comment, 0, SM, Tok.getLocation(),
534243789Sdim                       SM.getDiagnostics(), Status))
535243789Sdim      return true;
536243789Sdim  }
537243789Sdim  return false;
538243789Sdim}
539243789Sdim#endif // !NDEBUG
540243789Sdim
541243789Sdim/// \brief Takes a list of diagnostics that have been generated but not matched
542243789Sdim/// by an expected-* directive and produces a diagnostic to the user from this.
543243789Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
544243789Sdim                                const_diag_iterator diag_begin,
545243789Sdim                                const_diag_iterator diag_end,
546243789Sdim                                const char *Kind) {
547249423Sdim  if (diag_begin == diag_end) return 0;
548249423Sdim
549249423Sdim  SmallString<256> Fmt;
550249423Sdim  llvm::raw_svector_ostream OS(Fmt);
551249423Sdim  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
552243789Sdim    if (I->first.isInvalid() || !SourceMgr)
553243789Sdim      OS << "\n  (frontend)";
554249423Sdim    else
555249423Sdim      OS << "\n  Line " << SourceMgr->getPresumedLineNumber(I->first);
556249423Sdim    OS << ": " << I->second;
557249423Sdim  }
558249423Sdim
559243789Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
560249423Sdim    << Kind << /*Unexpected=*/true << OS.str();
561249423Sdim  return std::distance(diag_begin, diag_end);
562249423Sdim}
563249423Sdim
564249423Sdim/// \brief Takes a list of diagnostics that were expected to have been generated
565249423Sdim/// but were not and produces a diagnostic to the user from this.
566249423Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
567249423Sdim                              DirectiveList &DL, const char *Kind) {
568249423Sdim  if (DL.empty())
569249423Sdim    return 0;
570249423Sdim
571243789Sdim  SmallString<256> Fmt;
572249423Sdim  llvm::raw_svector_ostream OS(Fmt);
573249423Sdim  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
574249423Sdim    Directive &D = **I;
575249423Sdim    OS << "\n  Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
576249423Sdim    if (D.DirectiveLoc != D.DiagnosticLoc)
577249423Sdim      OS << " (directive at "
578249423Sdim         << SourceMgr.getFilename(D.DirectiveLoc) << ":"
579249423Sdim         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
580249423Sdim    OS << ": " << D.Text;
581249423Sdim  }
582249423Sdim
583249423Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
584249423Sdim    << Kind << /*Unexpected=*/false << OS.str();
585249423Sdim  return DL.size();
586249423Sdim}
587249423Sdim
588249423Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the
589249423Sdim/// the difference between them.
590249423Sdim///
591249423Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
592249423Sdim                           const char *Label,
593249423Sdim                           DirectiveList &Left,
594249423Sdim                           const_diag_iterator d2_begin,
595249423Sdim                           const_diag_iterator d2_end) {
596249423Sdim  DirectiveList LeftOnly;
597249423Sdim  DiagList Right(d2_begin, d2_end);
598249423Sdim
599243789Sdim  for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
600243789Sdim    Directive& D = **I;
601243789Sdim    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
602243789Sdim
603243789Sdim    for (unsigned i = 0; i < D.Max; ++i) {
604243789Sdim      DiagList::iterator II, IE;
605243789Sdim      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
606243789Sdim        unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
607243789Sdim        if (LineNo1 != LineNo2)
608243789Sdim          continue;
609243789Sdim
610243789Sdim        const std::string &RightText = II->second;
611243789Sdim        if (D.match(RightText))
612243789Sdim          break;
613243789Sdim      }
614243789Sdim      if (II == IE) {
615249423Sdim        // Not found.
616249423Sdim        if (i >= D.Min) break;
617249423Sdim        LeftOnly.push_back(*I);
618249423Sdim      } else {
619243789Sdim        // Found. The same cannot be found twice.
620243789Sdim        Right.erase(II);
621243789Sdim      }
622243789Sdim    }
623249423Sdim  }
624249423Sdim  // Now all that's left in Right are those that were not matched.
625243789Sdim  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
626243789Sdim  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
627243789Sdim  return num;
628249423Sdim}
629249423Sdim
630249423Sdim/// CheckResults - This compares the expected results to those that
631249423Sdim/// were actually reported. It emits any discrepencies. Return "true" if there
632249423Sdim/// were problems. Return "false" otherwise.
633243789Sdim///
634243789Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
635249423Sdim                             const TextDiagnosticBuffer &Buffer,
636243789Sdim                             ExpectedData &ED) {
637249423Sdim  // We want to capture the delta between what was expected and what was
638249423Sdim  // seen.
639243789Sdim  //
640249423Sdim  //   Expected \ Seen - set expected but not seen
641243789Sdim  //   Seen \ Expected - set seen but not expected
642243789Sdim  unsigned NumProblems = 0;
643243789Sdim
644243789Sdim  // See if there are error mismatches.
645243789Sdim  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
646243789Sdim                            Buffer.err_begin(), Buffer.err_end());
647243789Sdim
648243789Sdim  // See if there are warning mismatches.
649249423Sdim  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
650249423Sdim                            Buffer.warn_begin(), Buffer.warn_end());
651243789Sdim
652243789Sdim  // See if there are note mismatches.
653243789Sdim  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
654243789Sdim                            Buffer.note_begin(), Buffer.note_end());
655243789Sdim
656243789Sdim  return NumProblems;
657243789Sdim}
658243789Sdim
659243789Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
660249423Sdim                                                      FileID FID,
661243789Sdim                                                      ParsedStatus PS) {
662249423Sdim  // Check SourceManager hasn't changed.
663249423Sdim  setSourceManager(SM);
664243789Sdim
665243789Sdim#ifndef NDEBUG
666243789Sdim  if (FID.isInvalid())
667243789Sdim    return;
668243789Sdim
669249423Sdim  const FileEntry *FE = SM.getFileEntryForID(FID);
670243789Sdim
671243789Sdim  if (PS == IsParsed) {
672243789Sdim    // Move the FileID from the unparsed set to the parsed set.
673243789Sdim    UnparsedFiles.erase(FID);
674243789Sdim    ParsedFiles.insert(std::make_pair(FID, FE));
675249423Sdim  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
676249423Sdim    // Add the FileID to the unparsed set if we haven't seen it before.
677243789Sdim
678249423Sdim    // Check for directives.
679249423Sdim    bool FoundDirectives;
680249423Sdim    if (PS == IsUnparsedNoDirectives)
681249423Sdim      FoundDirectives = false;
682249423Sdim    else
683249423Sdim      FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
684249423Sdim
685249423Sdim    // Add the FileID to the unparsed set.
686243789Sdim    UnparsedFiles.insert(std::make_pair(FID,
687249423Sdim                                      UnparsedFileStatus(FE, FoundDirectives)));
688249423Sdim  }
689249423Sdim#endif
690249423Sdim}
691243789Sdim
692243789Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() {
693249423Sdim  // Ensure any diagnostics go to the primary client.
694249423Sdim  bool OwnsCurClient = Diags.ownsClient();
695249423Sdim  DiagnosticConsumer *CurClient = Diags.takeClient();
696249423Sdim  Diags.setClient(PrimaryClient, false);
697249423Sdim
698249423Sdim#ifndef NDEBUG
699243789Sdim  // In a debug build, scan through any files that may have been missed
700249423Sdim  // during parsing and issue a fatal error if directives are contained
701249423Sdim  // within these files.  If a fatal error occurs, this suggests that
702249423Sdim  // this file is being parsed separately from the main file, in which
703249423Sdim  // case consider moving the directives to the correct place, if this
704249423Sdim  // is applicable.
705249423Sdim  if (UnparsedFiles.size() > 0) {
706249423Sdim    // Generate a cache of parsed FileEntry pointers for alias lookups.
707249423Sdim    llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
708249423Sdim    for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
709249423Sdim                                End = ParsedFiles.end(); I != End; ++I) {
710249423Sdim      if (const FileEntry *FE = I->second)
711243789Sdim        ParsedFileCache.insert(FE);
712243789Sdim    }
713243789Sdim
714243789Sdim    // Iterate through list of unparsed files.
715243789Sdim    for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
716243789Sdim                                  End = UnparsedFiles.end(); I != End; ++I) {
717243789Sdim      const UnparsedFileStatus &Status = I->second;
718243789Sdim      const FileEntry *FE = Status.getFile();
719243789Sdim
720243789Sdim      // Skip files that have been parsed via an alias.
721249423Sdim      if (FE && ParsedFileCache.count(FE))
722243789Sdim        continue;
723243789Sdim
724249423Sdim      // Report a fatal error if this file contained directives.
725243789Sdim      if (Status.foundDirectives()) {
726243789Sdim        llvm::report_fatal_error(Twine("-verify directives found after rather"
727243789Sdim                                       " than during normal parsing of ",
728243789Sdim                                 StringRef(FE ? FE->getName() : "(unknown)")));
729243789Sdim      }
730243789Sdim    }
731249423Sdim
732249423Sdim    // UnparsedFiles has been processed now, so clear it.
733249423Sdim    UnparsedFiles.clear();
734249423Sdim  }
735249423Sdim#endif // !NDEBUG
736249423Sdim
737249423Sdim  if (SrcManager) {
738249423Sdim    // Produce an error if no expected-* directives could be found in the
739249423Sdim    // source file(s) processed.
740249423Sdim    if (Status == HasNoDirectives) {
741249423Sdim      Diags.Report(diag::err_verify_no_directives).setForceEmit();
742249423Sdim      ++NumErrors;
743249423Sdim      Status = HasNoDirectivesReported;
744249423Sdim    }
745249423Sdim
746249423Sdim    // Check that the expected diagnostics occurred.
747249423Sdim    NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
748249423Sdim  } else {
749249423Sdim    NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
750249423Sdim                                  Buffer->err_end(), "error") +
751249423Sdim                  PrintUnexpected(Diags, 0, Buffer->warn_begin(),
752249423Sdim                                  Buffer->warn_end(), "warn") +
753249423Sdim                  PrintUnexpected(Diags, 0, Buffer->note_begin(),
754249423Sdim                                  Buffer->note_end(), "note"));
755249423Sdim  }
756249423Sdim
757249423Sdim  Diags.takeClient();
758249423Sdim  Diags.setClient(CurClient, OwnsCurClient);
759249423Sdim
760249423Sdim  // Reset the buffer, we have processed all the diagnostics in it.
761249423Sdim  Buffer.reset(new TextDiagnosticBuffer());
762249423Sdim  ED.Errors.clear();
763249423Sdim  ED.Warnings.clear();
764243789Sdim  ED.Notes.clear();
765249423Sdim}
766249423Sdim
767249423SdimDiagnosticConsumer *
768249423SdimVerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
769249423Sdim  if (!Diags.getClient())
770249423Sdim    Diags.setClient(PrimaryClient->clone(Diags));
771243789Sdim
772243789Sdim  return new VerifyDiagnosticConsumer(Diags);
773243789Sdim}
774249423Sdim
775249423SdimDirective *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
776243789Sdim                             SourceLocation DiagnosticLoc, StringRef Text,
777243789Sdim                             unsigned Min, unsigned Max) {
778243789Sdim  if (RegexKind)
779243789Sdim    return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
780243789Sdim  return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
781243789Sdim}
782249423Sdim