30 #include "llvm/ADT/STLExtras.h" 31 #include "llvm/ADT/SmallPtrSet.h" 32 #include "llvm/ADT/SmallString.h" 33 #include "llvm/ADT/StringRef.h" 34 #include "llvm/ADT/Twine.h" 35 #include "llvm/Support/ErrorHandling.h" 36 #include "llvm/Support/Regex.h" 37 #include "llvm/Support/raw_ostream.h" 48 using namespace clang;
55 : Diags(Diags_), PrimaryClient(Diags.getClient()),
56 PrimaryClientOwner(Diags.takeClient()),
63 assert(!ActiveSourceFiles &&
"Incomplete parsing of source files!");
64 assert(!CurrentPreprocessor &&
"CurrentPreprocessor should be invalid!");
68 "The VerifyDiagnosticConsumer takes over ownership of the client!");
81 : Verify(Verify),
SM(SM) {}
102 if (++ActiveSourceFiles == 1) {
104 CurrentPreprocessor = PP;
105 this->LangOpts = &LangOpts;
107 const_cast<Preprocessor *
>(PP)->addCommentHandler(
this);
111 llvm::make_unique<VerifyFileTracker>(*
this, *SrcManager));
116 assert((!PP || CurrentPreprocessor == PP) &&
"Preprocessor changed!");
117 PrimaryClient->BeginSourceFile(LangOpts, PP);
121 assert(ActiveSourceFiles &&
"No active source files!");
122 PrimaryClient->EndSourceFile();
125 if (--ActiveSourceFiles == 0) {
126 if (CurrentPreprocessor)
128 removeCommentHandler(
this);
132 CurrentPreprocessor =
nullptr;
155 Loc = SrcManager->getExpansionLoc(Loc);
156 FileID FID = SrcManager->getFileID(Loc);
158 const FileEntry *FE = SrcManager->getFileEntryForID(FID);
159 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
162 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
174 Buffer->HandleDiagnostic(DiagLevel, Info);
187 class StandardDirective :
public Directive {
190 bool MatchAnyLine, StringRef
Text,
unsigned Min,
192 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {}
194 bool isValid(std::string &
Error)
override {
199 bool match(StringRef S)
override {
200 return S.find(Text) != StringRef::npos;
205 class RegexDirective :
public Directive {
208 bool MatchAnyLine, StringRef
Text,
unsigned Min,
unsigned Max,
210 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
213 bool isValid(std::string &
Error)
override {
217 bool match(StringRef S)
override {
218 return Regex.match(S);
228 ParseHelper(StringRef S)
232 bool Next(StringRef S) {
237 return memcmp(
P, S.data(), S.size()) == 0;
242 bool Next(
unsigned &N) {
245 for (; P < End && P[0] >=
'0' &&
P[0] <=
'9'; ++
P) {
265 bool Search(StringRef S,
bool EnsureStartOfWord =
false,
266 bool FinishDirectiveToken =
false) {
269 P = std::search(
C,
End, S.begin(), S.end());
281 if (EnsureStartOfWord
285 || (
P > (
Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
288 if (FinishDirectiveToken) {
290 || *PEnd ==
'-' || *PEnd ==
'_'))
297 assert(
isLetter(*
P) &&
"-verify prefix must start with a letter");
298 while (
isDigit(PEnd[-1]) || PEnd[-1] ==
'-')
308 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
312 StringRef S(
P,
End -
P);
313 if (S.startswith(OpenBrace)) {
315 P += OpenBrace.size();
316 }
else if (S.startswith(CloseBrace)) {
319 PEnd =
P + CloseBrace.size();
322 P += CloseBrace.size();
338 void SkipWhitespace() {
349 const char *
const Begin;
352 const char *
const End;
361 const char *PEnd =
nullptr;
376 bool FoundDirective =
false;
377 for (ParseHelper PH(S); !PH.Done();) {
382 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(),
true,
true)
383 : PH.Search(
"",
true,
true)))
388 bool RegexKind =
false;
389 const char* KindStr =
"string";
396 StringRef DToken(PH.P, PH.C - PH.P);
399 if (DToken.endswith(
"-re")) {
402 DToken = DToken.substr(0, DToken.size()-3);
409 if (DToken.endswith(DType=
"-error"))
410 DL = ED ? &ED->
Errors :
nullptr;
411 else if (DToken.endswith(DType=
"-warning"))
413 else if (DToken.endswith(DType=
"-remark"))
414 DL = ED ? &ED->
Remarks :
nullptr;
415 else if (DToken.endswith(DType=
"-note"))
416 DL = ED ? &ED->
Notes :
nullptr;
417 else if (DToken.endswith(DType=
"-no-diagnostics")) {
424 DToken = DToken.substr(0, DToken.size()-DType.size());
429 if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
434 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
441 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
454 bool MatchAnyLine =
false;
460 bool FoundPlus = PH.Next(
"+");
461 if (FoundPlus || PH.Next(
"-")) {
464 bool Invalid =
false;
466 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
467 if (FoundPlus) ExpectedLine +=
Line;
468 else ExpectedLine -=
Line;
471 }
else if (PH.Next(Line)) {
475 }
else if (PP && PH.Search(
":")) {
477 StringRef
Filename(PH.C, PH.P-PH.C);
483 PP->
LookupFile(Pos, Filename,
false,
nullptr,
nullptr, CurDir,
484 nullptr,
nullptr,
nullptr,
nullptr);
487 diag::err_verify_missing_file) << Filename << KindStr;
494 if (PH.Next(Line) && Line > 0)
496 else if (PH.Next(
"*")) {
500 }
else if (PH.Next(
"*")) {
505 if (ExpectedLoc.
isInvalid() && !MatchAnyLine) {
507 diag::err_verify_missing_line) << KindStr;
526 }
else if (PH.Next(
"-")) {
528 if (!PH.Next(Max) || Max < Min) {
530 diag::err_verify_invalid_range) << KindStr;
537 }
else if (PH.Next(
"+")) {
547 if (!PH.Next(
"{{")) {
549 diag::err_verify_missing_start) << KindStr;
553 const char*
const ContentBegin = PH.C;
556 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
558 diag::err_verify_missing_end) << KindStr;
561 const char*
const ContentEnd = PH.P;
566 StringRef NewlineStr =
"\\n";
567 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
570 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
571 Text += Content.substr(CPos, FPos-CPos);
573 CPos = FPos + NewlineStr.size();
576 Text.assign(ContentBegin, ContentEnd);
579 if (RegexKind && Text.find(
"{{") == StringRef::npos) {
581 diag::err_verify_missing_regex) << Text;
587 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
590 if (D->isValid(Error)) {
591 DL->push_back(std::move(D));
592 FoundDirective =
true;
595 diag::err_verify_invalid_content)
600 return FoundDirective;
610 if (SrcManager && &SM != SrcManager)
622 size_t loc = C.find(
'\\');
623 if (loc == StringRef::npos) {
629 C2.reserve(C.size());
631 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
632 if (loc == StringRef::npos || loc == C.size()) {
633 C2 += C.substr(last);
636 C2 += C.substr(last, loc-last);
639 if (C[last] ==
'\n' || C[last] ==
'\r') {
644 if (C[last] ==
'\n' || C[last] ==
'\r')
645 if (C[last] != C[last-1])
671 const llvm::MemoryBuffer *FromFile = SM.
getBuffer(FID);
672 Lexer RawLex(FID, FromFile, SM, LangOpts);
683 if (!Tok.
is(tok::comment))
continue;
685 std::string Comment = RawLex.
getSpelling(Tok, SM, LangOpts);
686 if (Comment.empty())
continue;
703 if (diag_begin == diag_end)
return 0;
706 llvm::raw_svector_ostream OS(Fmt);
708 if (I->first.isInvalid() || !SourceMgr)
709 OS <<
"\n (frontend)";
714 OS <<
" File " << File->getName();
717 OS <<
": " << I->second;
721 << Kind <<
true << OS.str();
729 std::vector<Directive *> &DL,
const char *
Kind) {
734 llvm::raw_svector_ostream OS(Fmt);
735 for (
const auto *D : DL) {
736 if (D->DiagnosticLoc.isInvalid())
739 OS <<
"\n File " << SourceMgr.
getFilename(D->DiagnosticLoc);
744 if (D->DirectiveLoc != D->DiagnosticLoc)
745 OS <<
" (directive at " 748 OS <<
": " << D->Text;
752 << Kind <<
false << OS.str();
779 bool IgnoreUnexpected) {
780 std::vector<Directive *> LeftOnly;
783 for (
auto &Owner : Left) {
787 for (
unsigned i = 0; i < D.
Max; ++i) {
788 DiagList::iterator II, IE;
789 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
792 if (LineNo1 != LineNo2)
800 const std::string &RightText = II->second;
801 if (D.
match(RightText))
806 if (i >= D.
Min)
break;
807 LeftOnly.push_back(&D);
815 unsigned num =
PrintExpected(Diags, SourceMgr, LeftOnly, Label);
816 if (!IgnoreUnexpected)
832 unsigned NumProblems = 0;
864 setSourceManager(SM);
874 UnparsedFiles.erase(FID);
875 ParsedFiles.insert(std::make_pair(FID, FE));
876 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
880 bool FoundDirectives;
882 FoundDirectives =
false;
884 FoundDirectives = !LangOpts ||
findDirectives(SM, FID, *LangOpts);
887 UnparsedFiles.insert(std::make_pair(FID,
888 UnparsedFileStatus(FE, FoundDirectives)));
893 void VerifyDiagnosticConsumer::CheckDiagnostics() {
896 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
897 Diags.setClient(PrimaryClient,
false);
906 if (!UnparsedFiles.empty()) {
908 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
909 for (
const auto &I : ParsedFiles)
911 ParsedFileCache.insert(FE);
914 for (
const auto &I : UnparsedFiles) {
915 const UnparsedFileStatus &Status = I.second;
919 if (FE && ParsedFileCache.count(FE))
923 if (Status.foundDirectives()) {
924 llvm::report_fatal_error(Twine(
"-verify directives found after rather" 925 " than during normal parsing of ",
926 StringRef(FE ? FE->
getName() :
"(unknown)")));
931 UnparsedFiles.clear();
939 Diags.Report(diag::err_verify_no_directives).setForceEmit();
948 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
951 Buffer->err_end(),
"error");
954 Buffer->warn_end(),
"warn");
957 Buffer->remark_end(),
"remark");
960 Buffer->note_end(),
"note");
963 Diags.setClient(CurClient, Owner.release() !=
nullptr);
973 bool MatchAnyLine, StringRef
Text,
974 unsigned Min,
unsigned Max) {
976 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
977 MatchAnyLine,
Text, Min, Max);
980 std::string RegexStr;
983 if (S.startswith(
"{{")) {
985 size_t RegexMatchLength = S.find(
"}}");
986 assert(RegexMatchLength != StringRef::npos);
989 RegexStr.append(S.data(), RegexMatchLength);
991 S = S.drop_front(RegexMatchLength + 2);
993 size_t VerbatimMatchLength = S.find(
"{{");
994 if (VerbatimMatchLength == StringRef::npos)
995 VerbatimMatchLength = S.size();
997 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
998 S = S.drop_front(VerbatimMatchLength);
1002 return llvm::make_unique<RegexDirective>(
1003 DirectiveLoc, DiagnosticLoc, MatchAnyLine,
Text, Min, Max, RegexStr);
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...
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...
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
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
unsigned NumErrors
Number of errors reported.
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)) {...
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...
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.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
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...
SourceLocation DiagnosticLoc
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...
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
bool hasSourceManager() const
This interface provides a way to observe the actions of the preprocessor as it does its thing...
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].
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.
void setKind(tok::TokenKind K)
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...
const_iterator note_begin() const
const_iterator note_end() 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: ' ', '\t', '\f', '\v', '\n', '\r'.
const SourceLocation & getLocation() const
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.
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.
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.
std::vector< std::pair< SourceLocation, std::string > > DiagList
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. ...
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...
bool isWrittenInMainFile(SourceLocation Loc) const
Returns true if the spelling location for the given location is in the main file buffer.
LLVM_READONLY bool isAlphanumeric(unsigned char c)
Return true if this character is an ASCII letter or digit: [a-zA-Z0-9].
SourceLocation getEnd() const
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
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
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.
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
Cached information about one file (either on disk or in the virtual file system). ...
SourceManager & getSourceManager() const
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
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.
VerifyDiagnosticConsumer::ExpectedData ExpectedData
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
~VerifyDiagnosticConsumer() override
TextDiagnosticBuffer::const_iterator const_diag_iterator
LLVM_READONLY bool isDigit(unsigned char c)
Return true if this character is an ASCII digit: [0-9].
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
Level
The level of the diagnostic, after it has been through mapping.
const_iterator remark_begin() const
bool hasSourceManager() const
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...
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
DiagnosticLevelMask
A bitmask representing the diagnostic levels used by VerifyDiagnosticConsumer.
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.
bool ownsClient() const
Determine whether this DiagnosticsEngine object own its client.