21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/Support/Regex.h" 23 #include "llvm/Support/raw_ostream.h" 25 using namespace clang;
32 PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()),
34 LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0),
35 Status(HasNoDirectives)
42 assert(!ActiveSourceFiles &&
"Incomplete parsing of source files!");
43 assert(!CurrentPreprocessor &&
"CurrentPreprocessor should be invalid!");
47 "The VerifyDiagnosticConsumer takes over ownership of the client!");
58 : Verify(Verify),
SM(SM) { }
77 if (++ActiveSourceFiles == 1) {
79 CurrentPreprocessor = PP;
80 this->LangOpts = &LangOpts;
86 llvm::make_unique<VerifyFileTracker>(*
this, *SrcManager));
91 assert((!PP || CurrentPreprocessor == PP) &&
"Preprocessor changed!");
92 PrimaryClient->BeginSourceFile(LangOpts, PP);
96 assert(ActiveSourceFiles &&
"No active source files!");
97 PrimaryClient->EndSourceFile();
100 if (--ActiveSourceFiles == 0) {
101 if (CurrentPreprocessor)
102 const_cast<Preprocessor*
>(CurrentPreprocessor)->removeCommentHandler(
this);
106 CurrentPreprocessor =
nullptr;
129 Loc = SrcManager->getExpansionLoc(Loc);
130 FileID FID = SrcManager->getFileID(Loc);
132 const FileEntry *FE = SrcManager->getFileEntryForID(FID);
133 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
136 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
148 Buffer->HandleDiagnostic(DiagLevel, Info);
162 class StandardDirective :
public Directive {
165 bool MatchAnyLine, StringRef
Text,
unsigned Min,
167 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
169 bool isValid(std::string &
Error)
override {
174 bool match(StringRef S)
override {
175 return S.find(Text) != StringRef::npos;
181 class RegexDirective :
public Directive {
184 bool MatchAnyLine, StringRef
Text,
unsigned Min,
unsigned Max,
186 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
189 bool isValid(std::string &
Error)
override {
193 bool match(StringRef S)
override {
194 return Regex.match(S);
204 ParseHelper(StringRef S)
208 bool Next(StringRef S) {
213 return !memcmp(
P, S.data(), S.size());
218 bool Next(
unsigned &N) {
221 for (; P < End && P[0] >=
'0' &&
P[0] <=
'9'; ++
P) {
241 bool Search(StringRef S,
bool EnsureStartOfWord =
false,
242 bool FinishDirectiveToken =
false) {
245 P = std::search(
C,
End, S.begin(), S.end());
257 if (EnsureStartOfWord
261 || (
P > (
Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
264 if (FinishDirectiveToken) {
266 || *PEnd ==
'-' || *PEnd ==
'_'))
273 assert(
isLetter(*
P) &&
"-verify prefix must start with a letter");
274 while (
isDigit(PEnd[-1]) || PEnd[-1] ==
'-')
284 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
288 StringRef S(
P,
End -
P);
289 if (S.startswith(OpenBrace)) {
291 P += OpenBrace.size();
292 }
else if (S.startswith(CloseBrace)) {
295 PEnd =
P + CloseBrace.size();
298 P += CloseBrace.size();
314 void SkipWhitespace() {
324 const char *
const Begin;
325 const char *
const End;
345 bool FoundDirective =
false;
346 for (ParseHelper PH(S); !PH.Done();) {
351 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(),
true,
true)
352 : PH.Search(
"",
true,
true)))
357 bool RegexKind =
false;
358 const char* KindStr =
"string";
365 StringRef DToken(PH.P, PH.C - PH.P);
368 if (DToken.endswith(
"-re")) {
371 DToken = DToken.substr(0, DToken.size()-3);
378 if (DToken.endswith(DType=
"-error"))
379 DL = ED ? &ED->
Errors :
nullptr;
380 else if (DToken.endswith(DType=
"-warning"))
382 else if (DToken.endswith(DType=
"-remark"))
383 DL = ED ? &ED->
Remarks :
nullptr;
384 else if (DToken.endswith(DType=
"-note"))
385 DL = ED ? &ED->
Notes :
nullptr;
386 else if (DToken.endswith(DType=
"-no-diagnostics")) {
393 DToken = DToken.substr(0, DToken.size()-DType.size());
398 if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
403 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
410 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
423 bool MatchAnyLine =
false;
429 bool FoundPlus = PH.Next(
"+");
430 if (FoundPlus || PH.Next(
"-")) {
433 bool Invalid =
false;
435 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
436 if (FoundPlus) ExpectedLine +=
Line;
437 else ExpectedLine -=
Line;
440 }
else if (PH.Next(Line)) {
444 }
else if (PP && PH.Search(
":")) {
446 StringRef
Filename(PH.C, PH.P-PH.C);
452 PP->
LookupFile(Pos, Filename,
false,
nullptr,
nullptr, CurDir,
453 nullptr,
nullptr,
nullptr,
nullptr);
456 diag::err_verify_missing_file) << Filename << KindStr;
463 if (PH.Next(Line) && Line > 0)
465 else if (PH.Next(
"*")) {
469 }
else if (PH.Next(
"*")) {
474 if (ExpectedLoc.
isInvalid() && !MatchAnyLine) {
476 diag::err_verify_missing_line) << KindStr;
495 }
else if (PH.Next(
"-")) {
497 if (!PH.Next(Max) || Max < Min) {
499 diag::err_verify_invalid_range) << KindStr;
506 }
else if (PH.Next(
"+")) {
516 if (!PH.Next(
"{{")) {
518 diag::err_verify_missing_start) << KindStr;
522 const char*
const ContentBegin = PH.C;
525 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
527 diag::err_verify_missing_end) << KindStr;
530 const char*
const ContentEnd = PH.P;
535 StringRef NewlineStr =
"\\n";
536 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
539 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
540 Text += Content.substr(CPos, FPos-CPos);
542 CPos = FPos + NewlineStr.size();
545 Text.assign(ContentBegin, ContentEnd);
548 if (RegexKind && Text.find(
"{{") == StringRef::npos) {
550 diag::err_verify_missing_regex) << Text;
556 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
559 if (D->isValid(Error)) {
560 DL->push_back(std::move(D));
561 FoundDirective =
true;
564 diag::err_verify_invalid_content)
569 return FoundDirective;
579 if (SrcManager && &SM != SrcManager)
591 size_t loc = C.find(
'\\');
592 if (loc == StringRef::npos) {
598 C2.reserve(C.size());
600 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
601 if (loc == StringRef::npos || loc == C.size()) {
602 C2 += C.substr(last);
605 C2 += C.substr(last, loc-last);
608 if (C[last] ==
'\n' || C[last] ==
'\r') {
613 if (C[last] ==
'\n' || C[last] ==
'\r')
614 if (C[last] != C[last-1])
640 const llvm::MemoryBuffer *FromFile = SM.
getBuffer(FID);
641 Lexer RawLex(FID, FromFile, SM, LangOpts);
652 if (!Tok.
is(tok::comment))
continue;
654 std::string Comment = RawLex.
getSpelling(Tok, SM, LangOpts);
655 if (Comment.empty())
continue;
672 if (diag_begin == diag_end)
return 0;
675 llvm::raw_svector_ostream OS(Fmt);
677 if (I->first.isInvalid() || !SourceMgr)
678 OS <<
"\n (frontend)";
683 OS <<
" File " << File->getName();
686 OS <<
": " << I->second;
690 << Kind <<
true << OS.str();
698 std::vector<Directive *> &DL,
const char *
Kind) {
703 llvm::raw_svector_ostream OS(Fmt);
704 for (
auto *DirPtr : DL) {
715 OS <<
" (directive at " 718 OS <<
": " << D.
Text;
722 << Kind <<
false << OS.str();
750 bool IgnoreUnexpected) {
751 std::vector<Directive *> LeftOnly;
754 for (
auto &Owner : Left) {
758 for (
unsigned i = 0; i < D.
Max; ++i) {
759 DiagList::iterator II, IE;
760 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
763 if (LineNo1 != LineNo2)
771 const std::string &RightText = II->second;
772 if (D.
match(RightText))
777 if (i >= D.
Min)
break;
778 LeftOnly.push_back(&D);
786 unsigned num =
PrintExpected(Diags, SourceMgr, LeftOnly, Label);
787 if (!IgnoreUnexpected)
804 unsigned NumProblems = 0;
836 setSourceManager(SM);
846 UnparsedFiles.erase(FID);
847 ParsedFiles.insert(std::make_pair(FID, FE));
848 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
852 bool FoundDirectives;
854 FoundDirectives =
false;
856 FoundDirectives = !LangOpts ||
findDirectives(SM, FID, *LangOpts);
859 UnparsedFiles.insert(std::make_pair(FID,
860 UnparsedFileStatus(FE, FoundDirectives)));
865 void VerifyDiagnosticConsumer::CheckDiagnostics() {
868 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
869 Diags.setClient(PrimaryClient,
false);
878 if (UnparsedFiles.size() > 0) {
880 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
881 for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
882 End = ParsedFiles.end(); I !=
End; ++I) {
884 ParsedFileCache.insert(FE);
888 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
889 End = UnparsedFiles.end(); I !=
End; ++I) {
890 const UnparsedFileStatus &Status = I->second;
894 if (FE && ParsedFileCache.count(FE))
898 if (Status.foundDirectives()) {
899 llvm::report_fatal_error(Twine(
"-verify directives found after rather" 900 " than during normal parsing of ",
901 StringRef(FE ? FE->
getName() :
"(unknown)")));
906 UnparsedFiles.clear();
914 Diags.Report(diag::err_verify_no_directives).setForceEmit();
923 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
926 Buffer->err_end(),
"error");
929 Buffer->warn_end(),
"warn");
932 Buffer->remark_end(),
"remark");
935 Buffer->note_end(),
"note");
938 Diags.setClient(CurClient, Owner.release() !=
nullptr);
948 bool MatchAnyLine, StringRef
Text,
949 unsigned Min,
unsigned Max) {
951 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
952 MatchAnyLine,
Text, Min, Max);
955 std::string RegexStr;
958 if (S.startswith(
"{{")) {
960 size_t RegexMatchLength = S.find(
"}}");
961 assert(RegexMatchLength != StringRef::npos);
964 RegexStr.append(S.data(), RegexMatchLength);
966 S = S.drop_front(RegexMatchLength + 2);
968 size_t VerbatimMatchLength = S.find(
"{{");
969 if (VerbatimMatchLength == StringRef::npos)
970 VerbatimMatchLength = S.size();
972 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
973 S = S.drop_front(VerbatimMatchLength);
977 return llvm::make_unique<RegexDirective>(
978 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()))
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)) {...
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.
VerifyDiagnosticConsumer::DirectiveList DirectiveList
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...
TextDiagnosticBuffer::DiagList DiagList
VerifyDiagnosticConsumer::ExpectedData ExpectedData
TextDiagnosticBuffer::const_iterator const_diag_iterator
void EndSourceFile() override
Callback to inform the diagnostic client that processing of a source file has ended.
ExpectedData - owns directive objects and deletes on destructor.
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.
SourceLocation DirectiveLoc
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
VerifyDiagnosticConsumer::Directive Directive
const_iterator note_end() const
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
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
Concrete class used by the front-end to report problems and issues.
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
DiagnosticsEngine & getDiagnostics() const
const_iterator err_begin() const
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
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 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.
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.
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). ...
DiagList::const_iterator const_iterator
SourceManager & getSourceManager() const
virtual bool match(StringRef S)=0
File has diagnostics and may have directives.
std::vector< std::unique_ptr< Directive > > DirectiveList
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isNot(tok::TokenKind K) const
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.
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
~VerifyDiagnosticConsumer() override
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.
const_iterator warn_begin() const
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
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.