clang  5.0.0
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.
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,
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 ///
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 ///
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 }
SourceManager & getSourceManager() const
Definition: Preprocessor.h:729
const_iterator warn_end() const
ModuleMap::KnownHeader findModuleForHeader(const FileEntry *File, bool AllowTextual=false) const
Retrieve the module that corresponds to the given file, if any.
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
SourceLocation getEnd() const
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens...
Definition: Lexer.h:46
DiagnosticConsumer * getClient()
Definition: Diagnostic.h:427
bool isMacroID() const
VerifyDiagnosticConsumer - Create a diagnostic client which will use markers in the input source to c...
bool isLoadedFileID(FileID FID) const
Returns true if FID came from a PCH/Module.
Defines the clang::FileManager interface and associated types.
const_iterator note_begin() const
static LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t', '\f', '\v', '\n', '\r'.
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:154
unsigned NumErrors
Number of errors reported.
Definition: Diagnostic.h:1398
void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS)
Update lists of parsed and unparsed files.
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) 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...
const char * getCharacterData(SourceLocation SL, bool *Invalid=nullptr) const
Return a pointer to the start of the specified location in the appropriate spelling MemoryBuffer...
const_iterator err_end() const
StringRef P
llvm::MemoryBuffer * getBuffer(FileID FID, SourceLocation Loc, bool *Invalid=nullptr) const
Return the buffer for the specified FileID.
File has been processed via HandleComment.
VerifyDiagnosticConsumer::DirectiveList DirectiveList
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
std::unique_ptr< llvm::MemoryBuffer > Buffer
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1205
std::unique_ptr< DiagnosticConsumer > takeClient()
Return the current diagnostic client along with ownership of that client.
Definition: Diagnostic.h:435
float __ovld __cnfn distance(float p0, float p1)
Returns the distance between p0 and p1.
virtual void EndSourceFile()
Callback to inform the diagnostic client that processing of a source file has ended.
Definition: Diagnostic.h:1428
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
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.
void setClient(DiagnosticConsumer *client, bool ShouldOwnClient=true)
Set the diagnostic client associated with this diagnostic object.
Definition: Diagnostic.cpp:90
ExpectedData - owns directive objects and deletes on destructor.
Token - This structure provides full information about a lexed token.
Definition: Token.h:35
void setKind(tok::TokenKind K)
Definition: Token.h:91
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
SourceManager & getSourceManager() const
Definition: Diagnostic.h:438
VerifyDiagnosticConsumer::Directive Directive
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
const SourceLocation & getLocation() const
Definition: Diagnostic.h:1236
SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const
Gets the location of the immediate macro caller, one level up the stack toward the initial macro type...
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:147
HeaderSearch & getHeaderSearchInfo() const
Definition: Preprocessor.h:731
StringRef getName() const
Definition: FileManager.h:84
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...
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
SourceLocation translateFileLineCol(const FileEntry *SourceFile, unsigned Line, unsigned Col) const
Get the source location for the given file:line:col triplet.
detail::InMemoryDirectory::const_iterator I
bool isInvalid() const
Encapsulates the information needed to find the file referenced by a #include or #include_next, (sub-)framework lookup, etc.
Definition: HeaderSearch.h:137
const_iterator remark_end() const
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
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.
const_iterator remark_begin() const
StringRef getFilename(SourceLocation SpellingLoc) const
Return the filename of the file containing a SourceLocation.
StringRef Filename
Definition: Format.cpp:1301
const SmallVectorImpl< AnnotatedLine * >::const_iterator End
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
int * Depth
bool isWrittenInSameFile(SourceLocation Loc1, SourceLocation Loc2) const
Returns true if the spelling locations for both SourceLocations are part of the same file buffer...
std::string Label
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...
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
SourceLocation Begin
bool isWrittenInMainFile(SourceLocation Loc) const
Returns true if the spelling location for the given location is in the main file buffer.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file. ...
Definition: Token.h:124
const_iterator note_end() const
bool isNot(tok::TokenKind K) const
Definition: Token.h:96
const SourceManager & SM
Definition: Format.cpp:1293
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...
const_iterator warn_begin() const
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
SmallVectorImpl< AnnotatedLine * >::const_iterator Next
Encodes a location in the source.
AnnotatedLine & Line
bool isValid() const
Return true if this is a valid SourceLocation object.
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:59
DiagList::const_iterator const_iterator
DiagnosticsEngine & getDiagnostics() const
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:417
SourceLocation getBegin() const
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
virtual bool match(StringRef S)=0
File has diagnostics and may have directives.
DiagnosticsEngine & getDiagnostics() const
Definition: Preprocessor.h:722
const DiagnosticBuilder & setForceEmit() const
Forces the diagnostic to be emitted.
Definition: Diagnostic.h:1045
std::vector< std::unique_ptr< Directive > > DirectiveList
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T-> getSizeExpr()))
virtual bool isValid(std::string &Error)=0
bool hasSourceManager() const
Definition: Diagnostic.h:1237
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.
detail::InMemoryDirectory::const_iterator E
SourceMgr(SourceMgr)
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
bool hasSourceManager() const
Definition: Diagnostic.h:437
SourceManager & getSourceManager() const
Definition: Diagnostic.h:1238
bool isInvalid() const
static const unsigned MaxCount
Constant representing n or more matches.
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:150
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:187
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.
FormattingAttemptStatus * Status
Definition: Format.cpp:1073
StringRef Text
Definition: Format.cpp:1302
bool ownsClient() const
Determine whether this DiagnosticsEngine object own its client.
Definition: Diagnostic.h:431
A trivial tuple used to represent a source range.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
Directive - Abstract class representing a parsed verify directive.
virtual void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP=nullptr)
Callback to inform the diagnostic client that processing of a source file is beginning.
Definition: Diagnostic.h:1420
This class handles loading and caching of source files into memory.
const_iterator err_begin() const
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