clang  6.0.0svn
VerifyDiagnosticConsumer.cpp
Go to the documentation of this file.
1 //===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This is a concrete diagnostic client, which buffers the diagnostic messages.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "clang/Basic/CharInfo.h"
19 #include "clang/Lex/HeaderSearch.h"
20 #include "clang/Lex/Preprocessor.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/Support/Regex.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace clang;
29 
31  : Diags(Diags_),
32  PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()),
33  Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr),
34  LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0),
35  Status(HasNoDirectives)
36 {
37  if (Diags.hasSourceManager())
38  setSourceManager(Diags.getSourceManager());
39 }
40 
42  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
43  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
44  SrcManager = nullptr;
45  CheckDiagnostics();
46  assert(!Diags.ownsClient() &&
47  "The VerifyDiagnosticConsumer takes over ownership of the client!");
48 }
49 
50 #ifndef NDEBUG
51 namespace {
52 class VerifyFileTracker : public PPCallbacks {
55 
56 public:
57  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
58  : Verify(Verify), SM(SM) { }
59 
60  /// \brief Hook into the preprocessor and update the list of parsed
61  /// files when the preprocessor indicates a new file is entered.
62  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
64  FileID PrevFID) override {
65  Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
67  }
68 };
69 } // End anonymous namespace.
70 #endif
71 
72 // DiagnosticConsumer interface.
73 
75  const Preprocessor *PP) {
76  // Attach comment handler on first invocation.
77  if (++ActiveSourceFiles == 1) {
78  if (PP) {
79  CurrentPreprocessor = PP;
80  this->LangOpts = &LangOpts;
81  setSourceManager(PP->getSourceManager());
82  const_cast<Preprocessor*>(PP)->addCommentHandler(this);
83 #ifndef NDEBUG
84  // Debug build tracks parsed files.
85  const_cast<Preprocessor*>(PP)->addPPCallbacks(
86  llvm::make_unique<VerifyFileTracker>(*this, *SrcManager));
87 #endif
88  }
89  }
90 
91  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
92  PrimaryClient->BeginSourceFile(LangOpts, PP);
93 }
94 
96  assert(ActiveSourceFiles && "No active source files!");
97  PrimaryClient->EndSourceFile();
98 
99  // Detach comment handler once last active source file completed.
100  if (--ActiveSourceFiles == 0) {
101  if (CurrentPreprocessor)
102  const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
103 
104  // Check diagnostics once last file completed.
105  CheckDiagnostics();
106  CurrentPreprocessor = nullptr;
107  LangOpts = nullptr;
108  }
109 }
110 
112  DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
113  if (Info.hasSourceManager()) {
114  // If this diagnostic is for a different source manager, ignore it.
115  if (SrcManager && &Info.getSourceManager() != SrcManager)
116  return;
117 
118  setSourceManager(Info.getSourceManager());
119  }
120 
121 #ifndef NDEBUG
122  // Debug build tracks unparsed files for possible
123  // unparsed expected-* directives.
124  if (SrcManager) {
125  SourceLocation Loc = Info.getLocation();
126  if (Loc.isValid()) {
128 
129  Loc = SrcManager->getExpansionLoc(Loc);
130  FileID FID = SrcManager->getFileID(Loc);
131 
132  const FileEntry *FE = SrcManager->getFileEntryForID(FID);
133  if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
134  // If the file is a modules header file it shall not be parsed
135  // for expected-* directives.
136  HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
137  if (HS.findModuleForHeader(FE))
139  }
140 
141  UpdateParsedFileStatus(*SrcManager, FID, PS);
142  }
143  }
144 #endif
145 
146  // Send the diagnostic to the buffer, we will check it once we reach the end
147  // of the source file (or are destructed).
148  Buffer->HandleDiagnostic(DiagLevel, Info);
149 }
150 
151 //===----------------------------------------------------------------------===//
152 // Checking diagnostics implementation.
153 //===----------------------------------------------------------------------===//
154 
157 
158 namespace {
159 
160 /// StandardDirective - Directive with string matching.
161 ///
162 class StandardDirective : public Directive {
163 public:
164  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
165  bool MatchAnyLine, StringRef Text, unsigned Min,
166  unsigned Max)
167  : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
168 
169  bool isValid(std::string &Error) override {
170  // all strings are considered valid; even empty ones
171  return true;
172  }
173 
174  bool match(StringRef S) override {
175  return S.find(Text) != StringRef::npos;
176  }
177 };
178 
179 /// RegexDirective - Directive with regular-expression matching.
180 ///
181 class RegexDirective : public Directive {
182 public:
183  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
184  bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
185  StringRef RegexStr)
186  : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
187  Regex(RegexStr) { }
188 
189  bool isValid(std::string &Error) override {
190  return Regex.isValid(Error);
191  }
192 
193  bool match(StringRef S) override {
194  return Regex.match(S);
195  }
196 
197 private:
198  llvm::Regex Regex;
199 };
200 
201 class ParseHelper
202 {
203 public:
204  ParseHelper(StringRef S)
205  : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {}
206 
207  // Return true if string literal is next.
208  bool Next(StringRef S) {
209  P = C;
210  PEnd = C + S.size();
211  if (PEnd > End)
212  return false;
213  return !memcmp(P, S.data(), S.size());
214  }
215 
216  // Return true if number is next.
217  // Output N only if number is next.
218  bool Next(unsigned &N) {
219  unsigned TMP = 0;
220  P = C;
221  for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
222  TMP *= 10;
223  TMP += P[0] - '0';
224  }
225  if (P == C)
226  return false;
227  PEnd = P;
228  N = TMP;
229  return true;
230  }
231 
232  // Return true if string literal is found.
233  // When true, P marks begin-position of S in content.
234  bool Search(StringRef S, bool EnsureStartOfWord = false) {
235  do {
236  P = std::search(C, End, S.begin(), S.end());
237  PEnd = P + S.size();
238  if (P == End)
239  break;
240  if (!EnsureStartOfWord
241  // Check if string literal starts a new word.
242  || P == Begin || isWhitespace(P[-1])
243  // Or it could be preceded by the start of a comment.
244  || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
245  && P[-2] == '/'))
246  return true;
247  // Otherwise, skip and search again.
248  } while (Advance());
249  return false;
250  }
251 
252  // Return true if a CloseBrace that closes the OpenBrace at the current nest
253  // level is found. When true, P marks begin-position of CloseBrace.
254  bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
255  unsigned Depth = 1;
256  P = C;
257  while (P < End) {
258  StringRef S(P, End - P);
259  if (S.startswith(OpenBrace)) {
260  ++Depth;
261  P += OpenBrace.size();
262  } else if (S.startswith(CloseBrace)) {
263  --Depth;
264  if (Depth == 0) {
265  PEnd = P + CloseBrace.size();
266  return true;
267  }
268  P += CloseBrace.size();
269  } else {
270  ++P;
271  }
272  }
273  return false;
274  }
275 
276  // Advance 1-past previous next/search.
277  // Behavior is undefined if previous next/search failed.
278  bool Advance() {
279  C = PEnd;
280  return C < End;
281  }
282 
283  // Skip zero or more whitespace.
284  void SkipWhitespace() {
285  for (; C < End && isWhitespace(*C); ++C)
286  ;
287  }
288 
289  // Return true if EOF reached.
290  bool Done() {
291  return !(C < End);
292  }
293 
294  const char * const Begin; // beginning of expected content
295  const char * const End; // end of expected content (1-past)
296  const char *C; // position of next char in content
297  const char *P;
298 
299 private:
300  const char *PEnd; // previous next/search subject end (1-past)
301 };
302 
303 } // namespace anonymous
304 
305 /// ParseDirective - Go through the comment and see if it indicates expected
306 /// diagnostics. If so, then put them in the appropriate directive list.
307 ///
308 /// Returns true if any valid directives were found.
309 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
310  Preprocessor *PP, SourceLocation Pos,
312  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
313 
314  // A single comment may contain multiple directives.
315  bool FoundDirective = false;
316  for (ParseHelper PH(S); !PH.Done();) {
317  // Search for token: expected
318  if (!PH.Search("expected", true))
319  break;
320  PH.Advance();
321 
322  // Next token: -
323  if (!PH.Next("-"))
324  continue;
325  PH.Advance();
326 
327  // Next token: { error | warning | note }
328  DirectiveList *DL = nullptr;
329  if (PH.Next("error"))
330  DL = ED ? &ED->Errors : nullptr;
331  else if (PH.Next("warning"))
332  DL = ED ? &ED->Warnings : nullptr;
333  else if (PH.Next("remark"))
334  DL = ED ? &ED->Remarks : nullptr;
335  else if (PH.Next("note"))
336  DL = ED ? &ED->Notes : nullptr;
337  else if (PH.Next("no-diagnostics")) {
339  Diags.Report(Pos, diag::err_verify_invalid_no_diags)
340  << /*IsExpectedNoDiagnostics=*/true;
341  else
343  continue;
344  } else
345  continue;
346  PH.Advance();
347 
349  Diags.Report(Pos, diag::err_verify_invalid_no_diags)
350  << /*IsExpectedNoDiagnostics=*/false;
351  continue;
352  }
354 
355  // If a directive has been found but we're not interested
356  // in storing the directive information, return now.
357  if (!DL)
358  return true;
359 
360  // Default directive kind.
361  bool RegexKind = false;
362  const char* KindStr = "string";
363 
364  // Next optional token: -
365  if (PH.Next("-re")) {
366  PH.Advance();
367  RegexKind = true;
368  KindStr = "regex";
369  }
370 
371  // Next optional token: @
372  SourceLocation ExpectedLoc;
373  bool MatchAnyLine = false;
374  if (!PH.Next("@")) {
375  ExpectedLoc = Pos;
376  } else {
377  PH.Advance();
378  unsigned Line = 0;
379  bool FoundPlus = PH.Next("+");
380  if (FoundPlus || PH.Next("-")) {
381  // Relative to current line.
382  PH.Advance();
383  bool Invalid = false;
384  unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
385  if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
386  if (FoundPlus) ExpectedLine += Line;
387  else ExpectedLine -= Line;
388  ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
389  }
390  } else if (PH.Next(Line)) {
391  // Absolute line number.
392  if (Line > 0)
393  ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
394  } else if (PP && PH.Search(":")) {
395  // Specific source file.
396  StringRef Filename(PH.C, PH.P-PH.C);
397  PH.Advance();
398 
399  // Lookup file via Preprocessor, like a #include.
400  const DirectoryLookup *CurDir;
401  const FileEntry *FE =
402  PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
403  nullptr, nullptr, nullptr, nullptr);
404  if (!FE) {
405  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
406  diag::err_verify_missing_file) << Filename << KindStr;
407  continue;
408  }
409 
410  if (SM.translateFile(FE).isInvalid())
411  SM.createFileID(FE, Pos, SrcMgr::C_User);
412 
413  if (PH.Next(Line) && Line > 0)
414  ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
415  else if (PH.Next("*")) {
416  MatchAnyLine = true;
417  ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
418  }
419  }
420 
421  if (ExpectedLoc.isInvalid()) {
422  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
423  diag::err_verify_missing_line) << KindStr;
424  continue;
425  }
426  PH.Advance();
427  }
428 
429  // Skip optional whitespace.
430  PH.SkipWhitespace();
431 
432  // Next optional token: positive integer or a '+'.
433  unsigned Min = 1;
434  unsigned Max = 1;
435  if (PH.Next(Min)) {
436  PH.Advance();
437  // A positive integer can be followed by a '+' meaning min
438  // or more, or by a '-' meaning a range from min to max.
439  if (PH.Next("+")) {
440  Max = Directive::MaxCount;
441  PH.Advance();
442  } else if (PH.Next("-")) {
443  PH.Advance();
444  if (!PH.Next(Max) || Max < Min) {
445  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
446  diag::err_verify_invalid_range) << KindStr;
447  continue;
448  }
449  PH.Advance();
450  } else {
451  Max = Min;
452  }
453  } else if (PH.Next("+")) {
454  // '+' on its own means "1 or more".
455  Max = Directive::MaxCount;
456  PH.Advance();
457  }
458 
459  // Skip optional whitespace.
460  PH.SkipWhitespace();
461 
462  // Next token: {{
463  if (!PH.Next("{{")) {
464  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
465  diag::err_verify_missing_start) << KindStr;
466  continue;
467  }
468  PH.Advance();
469  const char* const ContentBegin = PH.C; // mark content begin
470 
471  // Search for token: }}
472  if (!PH.SearchClosingBrace("{{", "}}")) {
473  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
474  diag::err_verify_missing_end) << KindStr;
475  continue;
476  }
477  const char* const ContentEnd = PH.P; // mark content end
478  PH.Advance();
479 
480  // Build directive text; convert \n to newlines.
481  std::string Text;
482  StringRef NewlineStr = "\\n";
483  StringRef Content(ContentBegin, ContentEnd-ContentBegin);
484  size_t CPos = 0;
485  size_t FPos;
486  while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
487  Text += Content.substr(CPos, FPos-CPos);
488  Text += '\n';
489  CPos = FPos + NewlineStr.size();
490  }
491  if (Text.empty())
492  Text.assign(ContentBegin, ContentEnd);
493 
494  // Check that regex directives contain at least one regex.
495  if (RegexKind && Text.find("{{") == StringRef::npos) {
496  Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
497  diag::err_verify_missing_regex) << Text;
498  return false;
499  }
500 
501  // Construct new directive.
502  std::unique_ptr<Directive> D = Directive::create(
503  RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
504 
505  std::string Error;
506  if (D->isValid(Error)) {
507  DL->push_back(std::move(D));
508  FoundDirective = true;
509  } else {
510  Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
511  diag::err_verify_invalid_content)
512  << KindStr << Error;
513  }
514  }
515 
516  return FoundDirective;
517 }
518 
519 /// HandleComment - Hook into the preprocessor and extract comments containing
520 /// expected errors and warnings.
522  SourceRange Comment) {
524 
525  // If this comment is for a different source manager, ignore it.
526  if (SrcManager && &SM != SrcManager)
527  return false;
528 
529  SourceLocation CommentBegin = Comment.getBegin();
530 
531  const char *CommentRaw = SM.getCharacterData(CommentBegin);
532  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
533 
534  if (C.empty())
535  return false;
536 
537  // Fold any "<EOL>" sequences
538  size_t loc = C.find('\\');
539  if (loc == StringRef::npos) {
540  ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
541  return false;
542  }
543 
544  std::string C2;
545  C2.reserve(C.size());
546 
547  for (size_t last = 0;; loc = C.find('\\', last)) {
548  if (loc == StringRef::npos || loc == C.size()) {
549  C2 += C.substr(last);
550  break;
551  }
552  C2 += C.substr(last, loc-last);
553  last = loc + 1;
554 
555  if (C[last] == '\n' || C[last] == '\r') {
556  ++last;
557 
558  // Escape \r\n or \n\r, but not \n\n.
559  if (last < C.size())
560  if (C[last] == '\n' || C[last] == '\r')
561  if (C[last] != C[last-1])
562  ++last;
563  } else {
564  // This was just a normal backslash.
565  C2 += '\\';
566  }
567  }
568 
569  if (!C2.empty())
570  ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
571  return false;
572 }
573 
574 #ifndef NDEBUG
575 /// \brief Lex the specified source file to determine whether it contains
576 /// any expected-* directives. As a Lexer is used rather than a full-blown
577 /// Preprocessor, directives inside skipped #if blocks will still be found.
578 ///
579 /// \return true if any directives were found.
581  const LangOptions &LangOpts) {
582  // Create a raw lexer to pull all the comments out of FID.
583  if (FID.isInvalid())
584  return false;
585 
586  // Create a lexer to lex all the tokens of the main file in raw mode.
587  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
588  Lexer RawLex(FID, FromFile, SM, LangOpts);
589 
590  // Return comments as tokens, this is how we find expected diagnostics.
591  RawLex.SetCommentRetentionState(true);
592 
593  Token Tok;
594  Tok.setKind(tok::comment);
597  while (Tok.isNot(tok::eof)) {
598  RawLex.LexFromRawLexer(Tok);
599  if (!Tok.is(tok::comment)) continue;
600 
601  std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
602  if (Comment.empty()) continue;
603 
604  // Find first directive.
605  if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
606  Status))
607  return true;
608  }
609  return false;
610 }
611 #endif // !NDEBUG
612 
613 /// \brief Takes a list of diagnostics that have been generated but not matched
614 /// by an expected-* directive and produces a diagnostic to the user from this.
615 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
616  const_diag_iterator diag_begin,
617  const_diag_iterator diag_end,
618  const char *Kind) {
619  if (diag_begin == diag_end) return 0;
620 
621  SmallString<256> Fmt;
622  llvm::raw_svector_ostream OS(Fmt);
623  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
624  if (I->first.isInvalid() || !SourceMgr)
625  OS << "\n (frontend)";
626  else {
627  OS << "\n ";
628  if (const FileEntry *File = SourceMgr->getFileEntryForID(
629  SourceMgr->getFileID(I->first)))
630  OS << " File " << File->getName();
631  OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
632  }
633  OS << ": " << I->second;
634  }
635 
636  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
637  << Kind << /*Unexpected=*/true << OS.str();
638  return std::distance(diag_begin, diag_end);
639 }
640 
641 /// \brief Takes a list of diagnostics that were expected to have been generated
642 /// but were not and produces a diagnostic to the user from this.
643 static unsigned PrintExpected(DiagnosticsEngine &Diags,
644  SourceManager &SourceMgr,
645  std::vector<Directive *> &DL, const char *Kind) {
646  if (DL.empty())
647  return 0;
648 
649  SmallString<256> Fmt;
650  llvm::raw_svector_ostream OS(Fmt);
651  for (auto *DirPtr : DL) {
652  Directive &D = *DirPtr;
653  OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc);
654  if (D.MatchAnyLine)
655  OS << " Line *";
656  else
657  OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
658  if (D.DirectiveLoc != D.DiagnosticLoc)
659  OS << " (directive at "
660  << SourceMgr.getFilename(D.DirectiveLoc) << ':'
661  << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
662  OS << ": " << D.Text;
663  }
664 
665  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
666  << Kind << /*Unexpected=*/false << OS.str();
667  return DL.size();
668 }
669 
670 /// \brief Determine whether two source locations come from the same file.
671 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
672  SourceLocation DiagnosticLoc) {
673  while (DiagnosticLoc.isMacroID())
674  DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
675 
676  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
677  return true;
678 
679  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
680  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
681  return true;
682 
683  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
684 }
685 
686 /// CheckLists - Compare expected to seen diagnostic lists and return the
687 /// the difference between them.
688 ///
689 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
690  const char *Label,
691  DirectiveList &Left,
692  const_diag_iterator d2_begin,
693  const_diag_iterator d2_end,
694  bool IgnoreUnexpected) {
695  std::vector<Directive *> LeftOnly;
696  DiagList Right(d2_begin, d2_end);
697 
698  for (auto &Owner : Left) {
699  Directive &D = *Owner;
700  unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
701 
702  for (unsigned i = 0; i < D.Max; ++i) {
703  DiagList::iterator II, IE;
704  for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
705  if (!D.MatchAnyLine) {
706  unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
707  if (LineNo1 != LineNo2)
708  continue;
709  }
710 
711  if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
712  continue;
713 
714  const std::string &RightText = II->second;
715  if (D.match(RightText))
716  break;
717  }
718  if (II == IE) {
719  // Not found.
720  if (i >= D.Min) break;
721  LeftOnly.push_back(&D);
722  } else {
723  // Found. The same cannot be found twice.
724  Right.erase(II);
725  }
726  }
727  }
728  // Now all that's left in Right are those that were not matched.
729  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
730  if (!IgnoreUnexpected)
731  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
732  return num;
733 }
734 
735 /// CheckResults - This compares the expected results to those that
736 /// were actually reported. It emits any discrepencies. Return "true" if there
737 /// were problems. Return "false" otherwise.
738 ///
739 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
740  const TextDiagnosticBuffer &Buffer,
741  ExpectedData &ED) {
742  // We want to capture the delta between what was expected and what was
743  // seen.
744  //
745  // Expected \ Seen - set expected but not seen
746  // Seen \ Expected - set seen but not expected
747  unsigned NumProblems = 0;
748 
749  const DiagnosticLevelMask DiagMask =
750  Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
751 
752  // See if there are error mismatches.
753  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
754  Buffer.err_begin(), Buffer.err_end(),
755  bool(DiagnosticLevelMask::Error & DiagMask));
756 
757  // See if there are warning mismatches.
758  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
759  Buffer.warn_begin(), Buffer.warn_end(),
760  bool(DiagnosticLevelMask::Warning & DiagMask));
761 
762  // See if there are remark mismatches.
763  NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
764  Buffer.remark_begin(), Buffer.remark_end(),
765  bool(DiagnosticLevelMask::Remark & DiagMask));
766 
767  // See if there are note mismatches.
768  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
769  Buffer.note_begin(), Buffer.note_end(),
770  bool(DiagnosticLevelMask::Note & DiagMask));
771 
772  return NumProblems;
773 }
774 
776  FileID FID,
777  ParsedStatus PS) {
778  // Check SourceManager hasn't changed.
779  setSourceManager(SM);
780 
781 #ifndef NDEBUG
782  if (FID.isInvalid())
783  return;
784 
785  const FileEntry *FE = SM.getFileEntryForID(FID);
786 
787  if (PS == IsParsed) {
788  // Move the FileID from the unparsed set to the parsed set.
789  UnparsedFiles.erase(FID);
790  ParsedFiles.insert(std::make_pair(FID, FE));
791  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
792  // Add the FileID to the unparsed set if we haven't seen it before.
793 
794  // Check for directives.
795  bool FoundDirectives;
796  if (PS == IsUnparsedNoDirectives)
797  FoundDirectives = false;
798  else
799  FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
800 
801  // Add the FileID to the unparsed set.
802  UnparsedFiles.insert(std::make_pair(FID,
803  UnparsedFileStatus(FE, FoundDirectives)));
804  }
805 #endif
806 }
807 
808 void VerifyDiagnosticConsumer::CheckDiagnostics() {
809  // Ensure any diagnostics go to the primary client.
810  DiagnosticConsumer *CurClient = Diags.getClient();
811  std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
812  Diags.setClient(PrimaryClient, false);
813 
814 #ifndef NDEBUG
815  // In a debug build, scan through any files that may have been missed
816  // during parsing and issue a fatal error if directives are contained
817  // within these files. If a fatal error occurs, this suggests that
818  // this file is being parsed separately from the main file, in which
819  // case consider moving the directives to the correct place, if this
820  // is applicable.
821  if (UnparsedFiles.size() > 0) {
822  // Generate a cache of parsed FileEntry pointers for alias lookups.
823  llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
824  for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
825  End = ParsedFiles.end(); I != End; ++I) {
826  if (const FileEntry *FE = I->second)
827  ParsedFileCache.insert(FE);
828  }
829 
830  // Iterate through list of unparsed files.
831  for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
832  End = UnparsedFiles.end(); I != End; ++I) {
833  const UnparsedFileStatus &Status = I->second;
834  const FileEntry *FE = Status.getFile();
835 
836  // Skip files that have been parsed via an alias.
837  if (FE && ParsedFileCache.count(FE))
838  continue;
839 
840  // Report a fatal error if this file contained directives.
841  if (Status.foundDirectives()) {
842  llvm::report_fatal_error(Twine("-verify directives found after rather"
843  " than during normal parsing of ",
844  StringRef(FE ? FE->getName() : "(unknown)")));
845  }
846  }
847 
848  // UnparsedFiles has been processed now, so clear it.
849  UnparsedFiles.clear();
850  }
851 #endif // !NDEBUG
852 
853  if (SrcManager) {
854  // Produce an error if no expected-* directives could be found in the
855  // source file(s) processed.
856  if (Status == HasNoDirectives) {
857  Diags.Report(diag::err_verify_no_directives).setForceEmit();
858  ++NumErrors;
859  Status = HasNoDirectivesReported;
860  }
861 
862  // Check that the expected diagnostics occurred.
863  NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
864  } else {
865  const DiagnosticLevelMask DiagMask =
866  ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
867  if (bool(DiagnosticLevelMask::Error & DiagMask))
868  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
869  Buffer->err_end(), "error");
870  if (bool(DiagnosticLevelMask::Warning & DiagMask))
871  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
872  Buffer->warn_end(), "warn");
873  if (bool(DiagnosticLevelMask::Remark & DiagMask))
874  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
875  Buffer->remark_end(), "remark");
876  if (bool(DiagnosticLevelMask::Note & DiagMask))
877  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
878  Buffer->note_end(), "note");
879  }
880 
881  Diags.setClient(CurClient, Owner.release() != nullptr);
882 
883  // Reset the buffer, we have processed all the diagnostics in it.
884  Buffer.reset(new TextDiagnosticBuffer());
885  ED.Reset();
886 }
887 
888 std::unique_ptr<Directive> Directive::create(bool RegexKind,
889  SourceLocation DirectiveLoc,
890  SourceLocation DiagnosticLoc,
891  bool MatchAnyLine, StringRef Text,
892  unsigned Min, unsigned Max) {
893  if (!RegexKind)
894  return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
895  MatchAnyLine, Text, Min, Max);
896 
897  // Parse the directive into a regular expression.
898  std::string RegexStr;
899  StringRef S = Text;
900  while (!S.empty()) {
901  if (S.startswith("{{")) {
902  S = S.drop_front(2);
903  size_t RegexMatchLength = S.find("}}");
904  assert(RegexMatchLength != StringRef::npos);
905  // Append the regex, enclosed in parentheses.
906  RegexStr += "(";
907  RegexStr.append(S.data(), RegexMatchLength);
908  RegexStr += ")";
909  S = S.drop_front(RegexMatchLength + 2);
910  } else {
911  size_t VerbatimMatchLength = S.find("{{");
912  if (VerbatimMatchLength == StringRef::npos)
913  VerbatimMatchLength = S.size();
914  // Escape and append the fixed string.
915  RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
916  S = S.drop_front(VerbatimMatchLength);
917  }
918  }
919 
920  return llvm::make_unique<RegexDirective>(
921  DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr);
922 }
static unsigned getSpelling(const Token &Tok, const char *&Buffer, const SourceManager &SourceMgr, const LangOptions &LangOpts, bool *Invalid=nullptr)
getSpelling - This method is used to get the spelling of a token into a preallocated buffer...
Definition: Lexer.cpp:370
bool isWrittenInSameFile(SourceLocation Loc1, SourceLocation Loc2) const
Returns true if the spelling locations for both SourceLocations are part of the same file buffer...
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens...
Definition: Lexer.h:63
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T -> getSizeExpr()))
VerifyDiagnosticConsumer - Create a diagnostic client which will use markers in the input source to c...
Defines the clang::FileManager interface and associated types.
SourceManager & getSourceManager() const
Definition: Diagnostic.h:438
static LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: &#39; &#39;, &#39;\t&#39;, &#39;\f&#39;, &#39;\v&#39;, &#39;\n&#39;, &#39;\r&#39;.
Definition: CharInfo.h:88
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition: Lexer.h:171
unsigned NumErrors
Number of errors reported.
Definition: Diagnostic.h:1398
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {...
Definition: Token.h:95
void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS)
Update lists of parsed and unparsed files.
const_iterator remark_end() const
static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const TextDiagnosticBuffer &Buffer, ExpectedData &ED)
CheckResults - This compares the expected results to those that were actually reported.
VerifyDiagnosticConsumer(DiagnosticsEngine &Diags)
Create a new verifying diagnostic client, which will issue errors to the currently-attached diagnosti...
StringRef P
const char * getCharacterData(SourceLocation SL, bool *Invalid=nullptr) const
Return a pointer to the start of the specified location in the appropriate spelling MemoryBuffer...
File has been processed via HandleComment.
VerifyDiagnosticConsumer::DirectiveList DirectiveList
const DiagnosticBuilder & setForceEmit() const
Forces the diagnostic to be emitted.
Definition: Diagnostic.h:1045
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1205
float __ovld __cnfn distance(float p0, float p1)
Returns the distance between p0 and p1.
SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const
Gets the location of the immediate macro caller, one level up the stack toward the initial macro type...
const FileEntry * LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled, const DirectoryLookup *FromDir, const FileEntry *FromFile, const DirectoryLookup *&CurDir, SmallVectorImpl< char > *SearchPath, SmallVectorImpl< char > *RelativePath, ModuleMap::KnownHeader *SuggestedModule, bool *IsMapped, bool SkipCache=false)
Given a "foo" or <foo> reference, look up the indicated file.
static std::unique_ptr< Directive > create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1395
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:82
bool hasSourceManager() const
Definition: Diagnostic.h:1237
This interface provides a way to observe the actions of the preprocessor as it does its thing...
Definition: PPCallbacks.h:36
TextDiagnosticBuffer::DiagList DiagList
VerifyDiagnosticConsumer::ExpectedData ExpectedData
TextDiagnosticBuffer::const_iterator const_diag_iterator
void EndSourceFile() override
Callback to inform the diagnostic client that processing of a source file has ended.
ExpectedData - owns directive objects and deletes on destructor.
SourceLocation translateFileLineCol(const FileEntry *SourceFile, unsigned Line, unsigned Col) const
Get the source location for the given file:line:col triplet.
Token - This structure provides full information about a lexed token.
Definition: Token.h:35
void setKind(tok::TokenKind K)
Definition: Token.h:91
const_iterator err_end() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
const_iterator note_begin() const
VerifyDiagnosticConsumer::Directive Directive
const_iterator note_end() const
bool isInvalid() const
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
const SourceLocation & getLocation() const
Definition: Diagnostic.h:1236
const FormatToken & Tok
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:147
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
DiagnosticsEngine & getDiagnostics() const
const_iterator err_begin() const
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
Encapsulates the information needed to find the file referenced by a #include or #include_next, (sub-)framework lookup, etc.
Definition: HeaderSearch.h:137
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
File has diagnostics but guaranteed no directives.
StringRef Filename
Definition: Format.cpp:1316
SourceLocation End
std::string Label
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
const AnnotatedLine * Line
FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID=0, unsigned LoadedOffset=0)
Create a new FileID that represents the specified file being #included from the specified IncludePosi...
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file. ...
Definition: Token.h:124
Defines the clang::Preprocessor interface.
static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, const_diag_iterator diag_begin, const_diag_iterator diag_end, const char *Kind)
Takes a list of diagnostics that have been generated but not matched by an expected-* directive and p...
#define bool
Definition: stdbool.h:31
bool isWrittenInMainFile(SourceLocation Loc) const
Returns true if the spelling location for the given location is in the main file buffer.
SourceLocation Begin
int Depth
Definition: ASTDiff.cpp:191
SourceLocation getEnd() const
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
const SourceManager & SM
Definition: Format.cpp:1308
DirectoryLookup - This class represents one entry in the search list that specifies the search order ...
bool HandleComment(Preprocessor &PP, SourceRange Comment) override
HandleComment - Hook into the preprocessor and extract comments containing expected errors and warnin...
SourceManager & getSourceManager() const
Definition: Preprocessor.h:732
ModuleMap::KnownHeader findModuleForHeader(const FileEntry *File, bool AllowTextual=false) const
Retrieve the module that corresponds to the given file, if any.
StringRef getFilename(SourceLocation SpellingLoc) const
Return the filename of the file containing a SourceLocation.
std::vector< std::pair< SourceLocation, std::string > > DiagList
static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc)
Determine whether two source locations come from the same file.
Kind
llvm::MemoryBuffer * getBuffer(FileID FID, SourceLocation Loc, bool *Invalid=nullptr) const
Return the buffer for the specified FileID.
Encodes a location in the source.
StringRef getName() const
Definition: FileManager.h:84
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:59
DiagList::const_iterator const_iterator
SourceManager & getSourceManager() const
Definition: Diagnostic.h:1238
virtual bool match(StringRef S)=0
File has diagnostics and may have directives.
std::vector< std::unique_ptr< Directive > > DirectiveList
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isNot(tok::TokenKind K) const
Definition: Token.h:96
virtual bool isValid(std::string &Error)=0
const_iterator warn_end() const
Dataflow Directional Tag Classes.
static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, std::vector< Directive *> &DL, const char *Kind)
Takes a list of diagnostics that were expected to have been generated but were not and produces a dia...
bool isValid() const
Return true if this is a valid SourceLocation object.
static bool findDirectives(SourceManager &SM, FileID FID, const LangOptions &LangOpts)
Lex the specified source file to determine whether it contains any expected-* directives.
static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, Preprocessor *PP, SourceLocation Pos, VerifyDiagnosticConsumer::DirectiveStatus &Status)
ParseDirective - Go through the comment and see if it indicates expected diagnostics.
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:417
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
bool isMacroID() const
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
const_iterator warn_begin() const
static const unsigned MaxCount
Constant representing n or more matches.
DiagnosticsEngine & getDiagnostics() const
Definition: Preprocessor.h:725
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:150
const_iterator remark_begin() const
bool hasSourceManager() const
Definition: Diagnostic.h:437
static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const char *Label, DirectiveList &Left, const_diag_iterator d2_begin, const_diag_iterator d2_end, bool IgnoreUnexpected)
CheckLists - Compare expected to seen diagnostic lists and return the the difference between them...
void SetCommentRetentionState(bool Mode)
SetCommentRetentionMode - Change the comment retention mode of the lexer to the specified mode...
Definition: Lexer.h:204
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
Definition: Diagnostic.h:1225
DiagnosticLevelMask
A bitmask representing the diagnostic levels used by VerifyDiagnosticConsumer.
StringRef Text
Definition: Format.cpp:1317
A trivial tuple used to represent a source range.
Directive - Abstract class representing a parsed verify directive.
SourceLocation getBegin() const
This class handles loading and caching of source files into memory.
void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP) override
Callback to inform the diagnostic client that processing of a source file is beginning.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:98
bool ownsClient() const
Determine whether this DiagnosticsEngine object own its client.
Definition: Diagnostic.h:431