clang  9.0.0svn
VerifyDiagnosticConsumer.cpp
Go to the documentation of this file.
1 //===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This is a concrete diagnostic client, which buffers the diagnostic messages.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "clang/Basic/CharInfo.h"
15 #include "clang/Basic/Diagnostic.h"
18 #include "clang/Basic/LLVM.h"
21 #include "clang/Basic/TokenKinds.h"
24 #include "clang/Lex/HeaderSearch.h"
25 #include "clang/Lex/Lexer.h"
26 #include "clang/Lex/PPCallbacks.h"
27 #include "clang/Lex/Preprocessor.h"
28 #include "clang/Lex/Token.h"
29 #include "llvm/ADT/STLExtras.h"
30 #include "llvm/ADT/SmallPtrSet.h"
31 #include "llvm/ADT/SmallString.h"
32 #include "llvm/ADT/StringRef.h"
33 #include "llvm/ADT/Twine.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include "llvm/Support/Regex.h"
36 #include "llvm/Support/raw_ostream.h"
37 #include <algorithm>
38 #include <cassert>
39 #include <cstddef>
40 #include <cstring>
41 #include <iterator>
42 #include <memory>
43 #include <string>
44 #include <utility>
45 #include <vector>
46 
47 using namespace clang;
48 
52 
54  : Diags(Diags_), PrimaryClient(Diags.getClient()),
55  PrimaryClientOwner(Diags.takeClient()),
56  Buffer(new TextDiagnosticBuffer()), Status(HasNoDirectives) {
57  if (Diags.hasSourceManager())
58  setSourceManager(Diags.getSourceManager());
59 }
60 
62  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
63  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
64  SrcManager = nullptr;
65  CheckDiagnostics();
66  assert(!Diags.ownsClient() &&
67  "The VerifyDiagnosticConsumer takes over ownership of the client!");
68 }
69 
70 #ifndef NDEBUG
71 
72 namespace {
73 
74 class VerifyFileTracker : public PPCallbacks {
77 
78 public:
79  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
80  : Verify(Verify), SM(SM) {}
81 
82  /// Hook into the preprocessor and update the list of parsed
83  /// files when the preprocessor indicates a new file is entered.
84  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
86  FileID PrevFID) override {
87  Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
89  }
90 };
91 
92 } // namespace
93 
94 #endif
95 
96 // DiagnosticConsumer interface.
97 
99  const Preprocessor *PP) {
100  // Attach comment handler on first invocation.
101  if (++ActiveSourceFiles == 1) {
102  if (PP) {
103  CurrentPreprocessor = PP;
104  this->LangOpts = &LangOpts;
105  setSourceManager(PP->getSourceManager());
106  const_cast<Preprocessor *>(PP)->addCommentHandler(this);
107 #ifndef NDEBUG
108  // Debug build tracks parsed files.
109  const_cast<Preprocessor *>(PP)->addPPCallbacks(
110  llvm::make_unique<VerifyFileTracker>(*this, *SrcManager));
111 #endif
112  }
113  }
114 
115  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
116  PrimaryClient->BeginSourceFile(LangOpts, PP);
117 }
118 
120  assert(ActiveSourceFiles && "No active source files!");
121  PrimaryClient->EndSourceFile();
122 
123  // Detach comment handler once last active source file completed.
124  if (--ActiveSourceFiles == 0) {
125  if (CurrentPreprocessor)
126  const_cast<Preprocessor *>(CurrentPreprocessor)->
127  removeCommentHandler(this);
128 
129  // Check diagnostics once last file completed.
130  CheckDiagnostics();
131  CurrentPreprocessor = nullptr;
132  LangOpts = nullptr;
133  }
134 }
135 
137  DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
138  if (Info.hasSourceManager()) {
139  // If this diagnostic is for a different source manager, ignore it.
140  if (SrcManager && &Info.getSourceManager() != SrcManager)
141  return;
142 
143  setSourceManager(Info.getSourceManager());
144  }
145 
146 #ifndef NDEBUG
147  // Debug build tracks unparsed files for possible
148  // unparsed expected-* directives.
149  if (SrcManager) {
150  SourceLocation Loc = Info.getLocation();
151  if (Loc.isValid()) {
153 
154  Loc = SrcManager->getExpansionLoc(Loc);
155  FileID FID = SrcManager->getFileID(Loc);
156 
157  const FileEntry *FE = SrcManager->getFileEntryForID(FID);
158  if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
159  // If the file is a modules header file it shall not be parsed
160  // for expected-* directives.
161  HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
162  if (HS.findModuleForHeader(FE))
164  }
165 
166  UpdateParsedFileStatus(*SrcManager, FID, PS);
167  }
168  }
169 #endif
170 
171  // Send the diagnostic to the buffer, we will check it once we reach the end
172  // of the source file (or are destructed).
173  Buffer->HandleDiagnostic(DiagLevel, Info);
174 }
175 
176 //===----------------------------------------------------------------------===//
177 // Checking diagnostics implementation.
178 //===----------------------------------------------------------------------===//
179 
182 
183 namespace {
184 
185 /// StandardDirective - Directive with string matching.
186 class StandardDirective : public Directive {
187 public:
188  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
189  bool MatchAnyLine, StringRef Text, unsigned Min,
190  unsigned Max)
191  : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {}
192 
193  bool isValid(std::string &Error) override {
194  // all strings are considered valid; even empty ones
195  return true;
196  }
197 
198  bool match(StringRef S) override {
199  return S.find(Text) != StringRef::npos;
200  }
201 };
202 
203 /// RegexDirective - Directive with regular-expression matching.
204 class RegexDirective : public Directive {
205 public:
206  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
207  bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
208  StringRef RegexStr)
209  : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
210  Regex(RegexStr) {}
211 
212  bool isValid(std::string &Error) override {
213  return Regex.isValid(Error);
214  }
215 
216  bool match(StringRef S) override {
217  return Regex.match(S);
218  }
219 
220 private:
221  llvm::Regex Regex;
222 };
223 
224 class ParseHelper
225 {
226 public:
227  ParseHelper(StringRef S)
228  : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
229 
230  // Return true if string literal is next.
231  bool Next(StringRef S) {
232  P = C;
233  PEnd = C + S.size();
234  if (PEnd > End)
235  return false;
236  return memcmp(P, S.data(), S.size()) == 0;
237  }
238 
239  // Return true if number is next.
240  // Output N only if number is next.
241  bool Next(unsigned &N) {
242  unsigned TMP = 0;
243  P = C;
244  for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
245  TMP *= 10;
246  TMP += P[0] - '0';
247  }
248  if (P == C)
249  return false;
250  PEnd = P;
251  N = TMP;
252  return true;
253  }
254 
255  // Return true if string literal S is matched in content.
256  // When true, P marks begin-position of the match, and calling Advance sets C
257  // to end-position of the match.
258  // If S is the empty string, then search for any letter instead (makes sense
259  // with FinishDirectiveToken=true).
260  // If EnsureStartOfWord, then skip matches that don't start a new word.
261  // If FinishDirectiveToken, then assume the match is the start of a comment
262  // directive for -verify, and extend the match to include the entire first
263  // token of that directive.
264  bool Search(StringRef S, bool EnsureStartOfWord = false,
265  bool FinishDirectiveToken = false) {
266  do {
267  if (!S.empty()) {
268  P = std::search(C, End, S.begin(), S.end());
269  PEnd = P + S.size();
270  }
271  else {
272  P = C;
273  while (P != End && !isLetter(*P))
274  ++P;
275  PEnd = P + 1;
276  }
277  if (P == End)
278  break;
279  // If not start of word but required, skip and search again.
280  if (EnsureStartOfWord
281  // Check if string literal starts a new word.
282  && !(P == Begin || isWhitespace(P[-1])
283  // Or it could be preceded by the start of a comment.
284  || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
285  && P[-2] == '/')))
286  continue;
287  if (FinishDirectiveToken) {
288  while (PEnd != End && (isAlphanumeric(*PEnd)
289  || *PEnd == '-' || *PEnd == '_'))
290  ++PEnd;
291  // Put back trailing digits and hyphens to be parsed later as a count
292  // or count range. Because -verify prefixes must start with letters,
293  // we know the actual directive we found starts with a letter, so
294  // we won't put back the entire directive word and thus record an empty
295  // string.
296  assert(isLetter(*P) && "-verify prefix must start with a letter");
297  while (isDigit(PEnd[-1]) || PEnd[-1] == '-')
298  --PEnd;
299  }
300  return true;
301  } while (Advance());
302  return false;
303  }
304 
305  // Return true if a CloseBrace that closes the OpenBrace at the current nest
306  // level is found. When true, P marks begin-position of CloseBrace.
307  bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
308  unsigned Depth = 1;
309  P = C;
310  while (P < End) {
311  StringRef S(P, End - P);
312  if (S.startswith(OpenBrace)) {
313  ++Depth;
314  P += OpenBrace.size();
315  } else if (S.startswith(CloseBrace)) {
316  --Depth;
317  if (Depth == 0) {
318  PEnd = P + CloseBrace.size();
319  return true;
320  }
321  P += CloseBrace.size();
322  } else {
323  ++P;
324  }
325  }
326  return false;
327  }
328 
329  // Advance 1-past previous next/search.
330  // Behavior is undefined if previous next/search failed.
331  bool Advance() {
332  C = PEnd;
333  return C < End;
334  }
335 
336  // Skip zero or more whitespace.
337  void SkipWhitespace() {
338  for (; C < End && isWhitespace(*C); ++C)
339  ;
340  }
341 
342  // Return true if EOF reached.
343  bool Done() {
344  return !(C < End);
345  }
346 
347  // Beginning of expected content.
348  const char * const Begin;
349 
350  // End of expected content (1-past).
351  const char * const End;
352 
353  // Position of next char in content.
354  const char *C;
355 
356  const char *P;
357 
358 private:
359  // Previous next/search subject end (1-past).
360  const char *PEnd = nullptr;
361 };
362 
363 } // anonymous
364 
365 /// ParseDirective - Go through the comment and see if it indicates expected
366 /// diagnostics. If so, then put them in the appropriate directive list.
367 ///
368 /// Returns true if any valid directives were found.
369 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
370  Preprocessor *PP, SourceLocation Pos,
372  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
373 
374  // A single comment may contain multiple directives.
375  bool FoundDirective = false;
376  for (ParseHelper PH(S); !PH.Done();) {
377  // Search for the initial directive token.
378  // If one prefix, save time by searching only for its directives.
379  // Otherwise, search for any potential directive token and check it later.
380  const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes;
381  if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true)
382  : PH.Search("", true, true)))
383  break;
384  PH.Advance();
385 
386  // Default directive kind.
387  bool RegexKind = false;
388  const char* KindStr = "string";
389 
390  // Parse the initial directive token in reverse so we can easily determine
391  // its exact actual prefix. If we were to parse it from the front instead,
392  // it would be harder to determine where the prefix ends because there
393  // might be multiple matching -verify prefixes because some might prefix
394  // others.
395  StringRef DToken(PH.P, PH.C - PH.P);
396 
397  // Regex in initial directive token: -re
398  if (DToken.endswith("-re")) {
399  RegexKind = true;
400  KindStr = "regex";
401  DToken = DToken.substr(0, DToken.size()-3);
402  }
403 
404  // Type in initial directive token: -{error|warning|note|no-diagnostics}
405  DirectiveList *DL = nullptr;
406  bool NoDiag = false;
407  StringRef DType;
408  if (DToken.endswith(DType="-error"))
409  DL = ED ? &ED->Errors : nullptr;
410  else if (DToken.endswith(DType="-warning"))
411  DL = ED ? &ED->Warnings : nullptr;
412  else if (DToken.endswith(DType="-remark"))
413  DL = ED ? &ED->Remarks : nullptr;
414  else if (DToken.endswith(DType="-note"))
415  DL = ED ? &ED->Notes : nullptr;
416  else if (DToken.endswith(DType="-no-diagnostics")) {
417  NoDiag = true;
418  if (RegexKind)
419  continue;
420  }
421  else
422  continue;
423  DToken = DToken.substr(0, DToken.size()-DType.size());
424 
425  // What's left in DToken is the actual prefix. That might not be a -verify
426  // prefix even if there is only one -verify prefix (for example, the full
427  // DToken is foo-bar-warning, but foo is the only -verify prefix).
428  if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
429  continue;
430 
431  if (NoDiag) {
433  Diags.Report(Pos, diag::err_verify_invalid_no_diags)
434  << /*IsExpectedNoDiagnostics=*/true;
435  else
437  continue;
438  }
440  Diags.Report(Pos, diag::err_verify_invalid_no_diags)
441  << /*IsExpectedNoDiagnostics=*/false;
442  continue;
443  }
445 
446  // If a directive has been found but we're not interested
447  // in storing the directive information, return now.
448  if (!DL)
449  return true;
450 
451  // Next optional token: @
452  SourceLocation ExpectedLoc;
453  bool MatchAnyLine = false;
454  if (!PH.Next("@")) {
455  ExpectedLoc = Pos;
456  } else {
457  PH.Advance();
458  unsigned Line = 0;
459  bool FoundPlus = PH.Next("+");
460  if (FoundPlus || PH.Next("-")) {
461  // Relative to current line.
462  PH.Advance();
463  bool Invalid = false;
464  unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
465  if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
466  if (FoundPlus) ExpectedLine += Line;
467  else ExpectedLine -= Line;
468  ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
469  }
470  } else if (PH.Next(Line)) {
471  // Absolute line number.
472  if (Line > 0)
473  ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
474  } else if (PP && PH.Search(":")) {
475  // Specific source file.
476  StringRef Filename(PH.C, PH.P-PH.C);
477  PH.Advance();
478 
479  // Lookup file via Preprocessor, like a #include.
480  const DirectoryLookup *CurDir;
481  const FileEntry *FE =
482  PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
483  nullptr, nullptr, nullptr, nullptr, nullptr);
484  if (!FE) {
485  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
486  diag::err_verify_missing_file) << Filename << KindStr;
487  continue;
488  }
489 
490  if (SM.translateFile(FE).isInvalid())
491  SM.createFileID(FE, Pos, SrcMgr::C_User);
492 
493  if (PH.Next(Line) && Line > 0)
494  ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
495  else if (PH.Next("*")) {
496  MatchAnyLine = true;
497  ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
498  }
499  } else if (PH.Next("*")) {
500  MatchAnyLine = true;
501  ExpectedLoc = SourceLocation();
502  }
503 
504  if (ExpectedLoc.isInvalid() && !MatchAnyLine) {
505  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
506  diag::err_verify_missing_line) << KindStr;
507  continue;
508  }
509  PH.Advance();
510  }
511 
512  // Skip optional whitespace.
513  PH.SkipWhitespace();
514 
515  // Next optional token: positive integer or a '+'.
516  unsigned Min = 1;
517  unsigned Max = 1;
518  if (PH.Next(Min)) {
519  PH.Advance();
520  // A positive integer can be followed by a '+' meaning min
521  // or more, or by a '-' meaning a range from min to max.
522  if (PH.Next("+")) {
523  Max = Directive::MaxCount;
524  PH.Advance();
525  } else if (PH.Next("-")) {
526  PH.Advance();
527  if (!PH.Next(Max) || Max < Min) {
528  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
529  diag::err_verify_invalid_range) << KindStr;
530  continue;
531  }
532  PH.Advance();
533  } else {
534  Max = Min;
535  }
536  } else if (PH.Next("+")) {
537  // '+' on its own means "1 or more".
538  Max = Directive::MaxCount;
539  PH.Advance();
540  }
541 
542  // Skip optional whitespace.
543  PH.SkipWhitespace();
544 
545  // Next token: {{
546  if (!PH.Next("{{")) {
547  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
548  diag::err_verify_missing_start) << KindStr;
549  continue;
550  }
551  PH.Advance();
552  const char* const ContentBegin = PH.C; // mark content begin
553 
554  // Search for token: }}
555  if (!PH.SearchClosingBrace("{{", "}}")) {
556  Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
557  diag::err_verify_missing_end) << KindStr;
558  continue;
559  }
560  const char* const ContentEnd = PH.P; // mark content end
561  PH.Advance();
562 
563  // Build directive text; convert \n to newlines.
564  std::string Text;
565  StringRef NewlineStr = "\\n";
566  StringRef Content(ContentBegin, ContentEnd-ContentBegin);
567  size_t CPos = 0;
568  size_t FPos;
569  while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
570  Text += Content.substr(CPos, FPos-CPos);
571  Text += '\n';
572  CPos = FPos + NewlineStr.size();
573  }
574  if (Text.empty())
575  Text.assign(ContentBegin, ContentEnd);
576 
577  // Check that regex directives contain at least one regex.
578  if (RegexKind && Text.find("{{") == StringRef::npos) {
579  Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
580  diag::err_verify_missing_regex) << Text;
581  return false;
582  }
583 
584  // Construct new directive.
585  std::unique_ptr<Directive> D = Directive::create(
586  RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
587 
588  std::string Error;
589  if (D->isValid(Error)) {
590  DL->push_back(std::move(D));
591  FoundDirective = true;
592  } else {
593  Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
594  diag::err_verify_invalid_content)
595  << KindStr << Error;
596  }
597  }
598 
599  return FoundDirective;
600 }
601 
602 /// HandleComment - Hook into the preprocessor and extract comments containing
603 /// expected errors and warnings.
605  SourceRange Comment) {
607 
608  // If this comment is for a different source manager, ignore it.
609  if (SrcManager && &SM != SrcManager)
610  return false;
611 
612  SourceLocation CommentBegin = Comment.getBegin();
613 
614  const char *CommentRaw = SM.getCharacterData(CommentBegin);
615  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
616 
617  if (C.empty())
618  return false;
619 
620  // Fold any "<EOL>" sequences
621  size_t loc = C.find('\\');
622  if (loc == StringRef::npos) {
623  ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
624  return false;
625  }
626 
627  std::string C2;
628  C2.reserve(C.size());
629 
630  for (size_t last = 0;; loc = C.find('\\', last)) {
631  if (loc == StringRef::npos || loc == C.size()) {
632  C2 += C.substr(last);
633  break;
634  }
635  C2 += C.substr(last, loc-last);
636  last = loc + 1;
637 
638  if (C[last] == '\n' || C[last] == '\r') {
639  ++last;
640 
641  // Escape \r\n or \n\r, but not \n\n.
642  if (last < C.size())
643  if (C[last] == '\n' || C[last] == '\r')
644  if (C[last] != C[last-1])
645  ++last;
646  } else {
647  // This was just a normal backslash.
648  C2 += '\\';
649  }
650  }
651 
652  if (!C2.empty())
653  ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
654  return false;
655 }
656 
657 #ifndef NDEBUG
658 /// Lex the specified source file to determine whether it contains
659 /// any expected-* directives. As a Lexer is used rather than a full-blown
660 /// Preprocessor, directives inside skipped #if blocks will still be found.
661 ///
662 /// \return true if any directives were found.
664  const LangOptions &LangOpts) {
665  // Create a raw lexer to pull all the comments out of FID.
666  if (FID.isInvalid())
667  return false;
668 
669  // Create a lexer to lex all the tokens of the main file in raw mode.
670  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
671  Lexer RawLex(FID, FromFile, SM, LangOpts);
672 
673  // Return comments as tokens, this is how we find expected diagnostics.
674  RawLex.SetCommentRetentionState(true);
675 
676  Token Tok;
677  Tok.setKind(tok::comment);
680  while (Tok.isNot(tok::eof)) {
681  RawLex.LexFromRawLexer(Tok);
682  if (!Tok.is(tok::comment)) continue;
683 
684  std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
685  if (Comment.empty()) continue;
686 
687  // Find first directive.
688  if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
689  Status))
690  return true;
691  }
692  return false;
693 }
694 #endif // !NDEBUG
695 
696 /// Takes a list of diagnostics that have been generated but not matched
697 /// by an expected-* directive and produces a diagnostic to the user from this.
698 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
699  const_diag_iterator diag_begin,
700  const_diag_iterator diag_end,
701  const char *Kind) {
702  if (diag_begin == diag_end) return 0;
703 
704  SmallString<256> Fmt;
705  llvm::raw_svector_ostream OS(Fmt);
706  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
707  if (I->first.isInvalid() || !SourceMgr)
708  OS << "\n (frontend)";
709  else {
710  OS << "\n ";
711  if (const FileEntry *File = SourceMgr->getFileEntryForID(
712  SourceMgr->getFileID(I->first)))
713  OS << " File " << File->getName();
714  OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
715  }
716  OS << ": " << I->second;
717  }
718 
719  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
720  << Kind << /*Unexpected=*/true << OS.str();
721  return std::distance(diag_begin, diag_end);
722 }
723 
724 /// Takes a list of diagnostics that were expected to have been generated
725 /// but were not and produces a diagnostic to the user from this.
726 static unsigned PrintExpected(DiagnosticsEngine &Diags,
727  SourceManager &SourceMgr,
728  std::vector<Directive *> &DL, const char *Kind) {
729  if (DL.empty())
730  return 0;
731 
732  SmallString<256> Fmt;
733  llvm::raw_svector_ostream OS(Fmt);
734  for (const auto *D : DL) {
735  if (D->DiagnosticLoc.isInvalid())
736  OS << "\n File *";
737  else
738  OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc);
739  if (D->MatchAnyLine)
740  OS << " Line *";
741  else
742  OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
743  if (D->DirectiveLoc != D->DiagnosticLoc)
744  OS << " (directive at "
745  << SourceMgr.getFilename(D->DirectiveLoc) << ':'
746  << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
747  OS << ": " << D->Text;
748  }
749 
750  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
751  << Kind << /*Unexpected=*/false << OS.str();
752  return DL.size();
753 }
754 
755 /// Determine whether two source locations come from the same file.
756 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
757  SourceLocation DiagnosticLoc) {
758  while (DiagnosticLoc.isMacroID())
759  DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
760 
761  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
762  return true;
763 
764  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
765  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
766  return true;
767 
768  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
769 }
770 
771 /// CheckLists - Compare expected to seen diagnostic lists and return the
772 /// the difference between them.
773 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
774  const char *Label,
775  DirectiveList &Left,
776  const_diag_iterator d2_begin,
777  const_diag_iterator d2_end,
778  bool IgnoreUnexpected) {
779  std::vector<Directive *> LeftOnly;
780  DiagList Right(d2_begin, d2_end);
781 
782  for (auto &Owner : Left) {
783  Directive &D = *Owner;
784  unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
785 
786  for (unsigned i = 0; i < D.Max; ++i) {
787  DiagList::iterator II, IE;
788  for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
789  if (!D.MatchAnyLine) {
790  unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
791  if (LineNo1 != LineNo2)
792  continue;
793  }
794 
795  if (!D.DiagnosticLoc.isInvalid() &&
796  !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
797  continue;
798 
799  const std::string &RightText = II->second;
800  if (D.match(RightText))
801  break;
802  }
803  if (II == IE) {
804  // Not found.
805  if (i >= D.Min) break;
806  LeftOnly.push_back(&D);
807  } else {
808  // Found. The same cannot be found twice.
809  Right.erase(II);
810  }
811  }
812  }
813  // Now all that's left in Right are those that were not matched.
814  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
815  if (!IgnoreUnexpected)
816  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
817  return num;
818 }
819 
820 /// CheckResults - This compares the expected results to those that
821 /// were actually reported. It emits any discrepencies. Return "true" if there
822 /// were problems. Return "false" otherwise.
823 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
824  const TextDiagnosticBuffer &Buffer,
825  ExpectedData &ED) {
826  // We want to capture the delta between what was expected and what was
827  // seen.
828  //
829  // Expected \ Seen - set expected but not seen
830  // Seen \ Expected - set seen but not expected
831  unsigned NumProblems = 0;
832 
833  const DiagnosticLevelMask DiagMask =
834  Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
835 
836  // See if there are error mismatches.
837  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
838  Buffer.err_begin(), Buffer.err_end(),
839  bool(DiagnosticLevelMask::Error & DiagMask));
840 
841  // See if there are warning mismatches.
842  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
843  Buffer.warn_begin(), Buffer.warn_end(),
844  bool(DiagnosticLevelMask::Warning & DiagMask));
845 
846  // See if there are remark mismatches.
847  NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
848  Buffer.remark_begin(), Buffer.remark_end(),
849  bool(DiagnosticLevelMask::Remark & DiagMask));
850 
851  // See if there are note mismatches.
852  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
853  Buffer.note_begin(), Buffer.note_end(),
854  bool(DiagnosticLevelMask::Note & DiagMask));
855 
856  return NumProblems;
857 }
858 
860  FileID FID,
861  ParsedStatus PS) {
862  // Check SourceManager hasn't changed.
863  setSourceManager(SM);
864 
865 #ifndef NDEBUG
866  if (FID.isInvalid())
867  return;
868 
869  const FileEntry *FE = SM.getFileEntryForID(FID);
870 
871  if (PS == IsParsed) {
872  // Move the FileID from the unparsed set to the parsed set.
873  UnparsedFiles.erase(FID);
874  ParsedFiles.insert(std::make_pair(FID, FE));
875  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
876  // Add the FileID to the unparsed set if we haven't seen it before.
877 
878  // Check for directives.
879  bool FoundDirectives;
880  if (PS == IsUnparsedNoDirectives)
881  FoundDirectives = false;
882  else
883  FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
884 
885  // Add the FileID to the unparsed set.
886  UnparsedFiles.insert(std::make_pair(FID,
887  UnparsedFileStatus(FE, FoundDirectives)));
888  }
889 #endif
890 }
891 
892 void VerifyDiagnosticConsumer::CheckDiagnostics() {
893  // Ensure any diagnostics go to the primary client.
894  DiagnosticConsumer *CurClient = Diags.getClient();
895  std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
896  Diags.setClient(PrimaryClient, false);
897 
898 #ifndef NDEBUG
899  // In a debug build, scan through any files that may have been missed
900  // during parsing and issue a fatal error if directives are contained
901  // within these files. If a fatal error occurs, this suggests that
902  // this file is being parsed separately from the main file, in which
903  // case consider moving the directives to the correct place, if this
904  // is applicable.
905  if (!UnparsedFiles.empty()) {
906  // Generate a cache of parsed FileEntry pointers for alias lookups.
907  llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
908  for (const auto &I : ParsedFiles)
909  if (const FileEntry *FE = I.second)
910  ParsedFileCache.insert(FE);
911 
912  // Iterate through list of unparsed files.
913  for (const auto &I : UnparsedFiles) {
914  const UnparsedFileStatus &Status = I.second;
915  const FileEntry *FE = Status.getFile();
916 
917  // Skip files that have been parsed via an alias.
918  if (FE && ParsedFileCache.count(FE))
919  continue;
920 
921  // Report a fatal error if this file contained directives.
922  if (Status.foundDirectives()) {
923  llvm::report_fatal_error(Twine("-verify directives found after rather"
924  " than during normal parsing of ",
925  StringRef(FE ? FE->getName() : "(unknown)")));
926  }
927  }
928 
929  // UnparsedFiles has been processed now, so clear it.
930  UnparsedFiles.clear();
931  }
932 #endif // !NDEBUG
933 
934  if (SrcManager) {
935  // Produce an error if no expected-* directives could be found in the
936  // source file(s) processed.
937  if (Status == HasNoDirectives) {
938  Diags.Report(diag::err_verify_no_directives).setForceEmit();
939  ++NumErrors;
940  Status = HasNoDirectivesReported;
941  }
942 
943  // Check that the expected diagnostics occurred.
944  NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
945  } else {
946  const DiagnosticLevelMask DiagMask =
947  ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
948  if (bool(DiagnosticLevelMask::Error & DiagMask))
949  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
950  Buffer->err_end(), "error");
951  if (bool(DiagnosticLevelMask::Warning & DiagMask))
952  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
953  Buffer->warn_end(), "warn");
954  if (bool(DiagnosticLevelMask::Remark & DiagMask))
955  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
956  Buffer->remark_end(), "remark");
957  if (bool(DiagnosticLevelMask::Note & DiagMask))
958  NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
959  Buffer->note_end(), "note");
960  }
961 
962  Diags.setClient(CurClient, Owner.release() != nullptr);
963 
964  // Reset the buffer, we have processed all the diagnostics in it.
965  Buffer.reset(new TextDiagnosticBuffer());
966  ED.Reset();
967 }
968 
969 std::unique_ptr<Directive> Directive::create(bool RegexKind,
970  SourceLocation DirectiveLoc,
971  SourceLocation DiagnosticLoc,
972  bool MatchAnyLine, StringRef Text,
973  unsigned Min, unsigned Max) {
974  if (!RegexKind)
975  return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
976  MatchAnyLine, Text, Min, Max);
977 
978  // Parse the directive into a regular expression.
979  std::string RegexStr;
980  StringRef S = Text;
981  while (!S.empty()) {
982  if (S.startswith("{{")) {
983  S = S.drop_front(2);
984  size_t RegexMatchLength = S.find("}}");
985  assert(RegexMatchLength != StringRef::npos);
986  // Append the regex, enclosed in parentheses.
987  RegexStr += "(";
988  RegexStr.append(S.data(), RegexMatchLength);
989  RegexStr += ")";
990  S = S.drop_front(RegexMatchLength + 2);
991  } else {
992  size_t VerbatimMatchLength = S.find("{{");
993  if (VerbatimMatchLength == StringRef::npos)
994  VerbatimMatchLength = S.size();
995  // Escape and append the fixed string.
996  RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
997  S = S.drop_front(VerbatimMatchLength);
998  }
999  }
1000 
1001  return llvm::make_unique<RegexDirective>(
1002  DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr);
1003 }
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:388
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:76
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T -> getSizeExpr()))
std::vector< std::unique_ptr< Directive > > DirectiveList
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:521
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition: Lexer.h:194
unsigned NumErrors
Number of errors reported.
Definition: Diagnostic.h:1490
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:94
Defines the SourceManager interface.
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.
const DiagnosticBuilder & setForceEmit() const
Forces the diagnostic to be emitted.
Definition: Diagnostic.h:1133
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1294
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...
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:1487
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:77
bool hasSourceManager() const
Definition: Diagnostic.h:1327
This interface provides a way to observe the actions of the preprocessor as it does its thing...
Definition: PPCallbacks.h:35
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.
LLVM_READONLY bool isLetter(unsigned char c)
Return true if this character is an ASCII letter: [a-zA-Z].
Definition: CharInfo.h:111
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:34
void setKind(tok::TokenKind K)
Definition: Token.h:90
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:49
const_iterator note_begin() const
const_iterator note_end() const
bool isInvalid() const
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
TextDiagnosticBuffer::DiagList DiagList
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:87
const SourceLocation & getLocation() const
Definition: Diagnostic.h:1326
const FormatToken & Tok
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:148
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
Defines the Diagnostic-related interfaces.
DiagnosticsEngine & getDiagnostics() const
const_iterator err_begin() const
DiagList::const_iterator const_iterator
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:158
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:1628
std::vector< std::pair< SourceLocation, std::string > > DiagList
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:123
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
LLVM_READONLY bool isAlphanumeric(unsigned char c)
Return true if this character is an ASCII letter or digit: [a-zA-Z0-9].
Definition: CharInfo.h:117
int Depth
Definition: ASTDiff.cpp:190
SourceLocation getEnd() const
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
const SourceManager & SM
Definition: Format.cpp:1489
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:821
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.
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.
static ICEDiag NoDiag()
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
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 *IsFrameworkFound, bool SkipCache=false)
Given a "foo" or <foo> reference, look up the indicated file.
SourceManager & getSourceManager() const
Definition: Diagnostic.h:1328
virtual bool match(StringRef S)=0
File has diagnostics and may have directives.
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:95
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.
std::vector< std::string > VerifyPrefixes
The prefixes for comment directives sought by -verify ("expected" by default).
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.
VerifyDiagnosticConsumer::DirectiveList DirectiveList
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:499
VerifyDiagnosticConsumer::ExpectedData ExpectedData
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
bool isMacroID() const
TextDiagnosticBuffer::const_iterator const_diag_iterator
LLVM_READONLY bool isDigit(unsigned char c)
Return true if this character is an ASCII digit: [0-9].
Definition: CharInfo.h:93
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
Defines the PPCallbacks interface.
Defines the clang::TokenKind enum and support functions.
const_iterator warn_begin() const
Defines the clang::SourceLocation class and associated facilities.
static const unsigned MaxCount
Constant representing n or more matches.
DiagnosticsEngine & getDiagnostics() const
Definition: Preprocessor.h:814
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:151
const_iterator remark_begin() const
bool hasSourceManager() const
Definition: Diagnostic.h:519
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:227
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
Definition: Diagnostic.h:1315
DiagnosticLevelMask
A bitmask representing the diagnostic levels used by VerifyDiagnosticConsumer.
StringRef Text
Definition: Format.cpp:1629
A trivial tuple used to represent a source range.
Directive - Abstract class representing a parsed verify directive.
SourceLocation getBegin() const
VerifyDiagnosticConsumer::Directive Directive
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:124
bool ownsClient() const
Determine whether this DiagnosticsEngine object own its client.
Definition: Diagnostic.h:513