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!");
57 : Verify(Verify), SM(SM) { }
76 if (++ActiveSourceFiles == 1) {
78 CurrentPreprocessor = PP;
79 this->LangOpts = &LangOpts;
85 llvm::make_unique<VerifyFileTracker>(*
this, *SrcManager));
90 assert((!PP || CurrentPreprocessor == PP) &&
"Preprocessor changed!");
95 assert(ActiveSourceFiles &&
"No active source files!");
99 if (--ActiveSourceFiles == 0) {
100 if (CurrentPreprocessor)
101 const_cast<Preprocessor*
>(CurrentPreprocessor)->removeCommentHandler(
this);
105 CurrentPreprocessor =
nullptr;
132 if (FE && CurrentPreprocessor && SrcManager->
isLoadedFileID(FID)) {
147 Buffer->HandleDiagnostic(DiagLevel, Info);
161 class StandardDirective :
public Directive {
164 bool MatchAnyLine, StringRef
Text,
unsigned Min,
166 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
168 bool isValid(std::string &
Error)
override {
173 bool match(StringRef
S)
override {
174 return S.find(Text) != StringRef::npos;
180 class RegexDirective :
public Directive {
183 bool MatchAnyLine, StringRef
Text,
unsigned Min,
unsigned Max,
185 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
188 bool isValid(std::string &
Error)
override {
192 bool match(StringRef
S)
override {
193 return Regex.match(S);
203 ParseHelper(StringRef
S)
204 : Begin(S.begin()),
End(S.end()), C(Begin),
P(Begin), PEnd(
nullptr) {}
207 bool Next(StringRef S) {
212 return !memcmp(
P, S.data(), S.size());
217 bool Next(
unsigned &N) {
220 for (; P < End && P[0] >=
'0' &&
P[0] <=
'9'; ++
P) {
233 bool Search(StringRef S,
bool EnsureStartOfWord =
false) {
235 P = std::search(C,
End, S.begin(), S.end());
239 if (!EnsureStartOfWord
243 || (
P > (Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
253 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
258 if (S.startswith(OpenBrace)) {
260 P += OpenBrace.size();
261 }
else if (S.startswith(CloseBrace)) {
264 PEnd =
P + CloseBrace.size();
267 P += CloseBrace.size();
283 void SkipWhitespace() {
293 const char *
const Begin;
294 const char *
const End;
314 bool FoundDirective =
false;
315 for (ParseHelper PH(S); !PH.Done();) {
317 if (!PH.Search(
"expected",
true))
328 if (PH.Next(
"error"))
329 DL = ED ? &ED->
Errors :
nullptr;
330 else if (PH.Next(
"warning"))
332 else if (PH.Next(
"remark"))
333 DL = ED ? &ED->
Remarks :
nullptr;
334 else if (PH.Next(
"note"))
335 DL = ED ? &ED->
Notes :
nullptr;
336 else if (PH.Next(
"no-diagnostics")) {
338 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
348 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
360 bool RegexKind =
false;
361 const char* KindStr =
"string";
364 if (PH.Next(
"-re")) {
372 bool MatchAnyLine =
false;
378 bool FoundPlus = PH.Next(
"+");
379 if (FoundPlus || PH.Next(
"-")) {
382 bool Invalid =
false;
384 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
385 if (FoundPlus) ExpectedLine +=
Line;
386 else ExpectedLine -=
Line;
389 }
else if (PH.Next(Line)) {
393 }
else if (PP && PH.Search(
":")) {
395 StringRef
Filename(PH.C, PH.P-PH.C);
401 PP->
LookupFile(Pos, Filename,
false,
nullptr,
nullptr, CurDir,
402 nullptr,
nullptr,
nullptr);
405 diag::err_verify_missing_file) << Filename << KindStr;
412 if (PH.Next(Line) && Line > 0)
414 else if (PH.Next(
"*")) {
422 diag::err_verify_missing_line) << KindStr;
441 }
else if (PH.Next(
"-")) {
443 if (!PH.Next(Max) || Max < Min) {
445 diag::err_verify_invalid_range) << KindStr;
452 }
else if (PH.Next(
"+")) {
462 if (!PH.Next(
"{{")) {
464 diag::err_verify_missing_start) << KindStr;
468 const char*
const ContentBegin = PH.C;
471 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
473 diag::err_verify_missing_end) << KindStr;
476 const char*
const ContentEnd = PH.P;
481 StringRef NewlineStr =
"\\n";
482 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
485 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
486 Text += Content.substr(CPos, FPos-CPos);
488 CPos = FPos + NewlineStr.size();
491 Text.assign(ContentBegin, ContentEnd);
494 if (RegexKind && Text.find(
"{{") == StringRef::npos) {
496 diag::err_verify_missing_regex) << Text;
502 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
505 if (D->isValid(Error)) {
506 DL->push_back(std::move(D));
507 FoundDirective =
true;
510 diag::err_verify_invalid_content)
515 return FoundDirective;
525 if (SrcManager && &SM != SrcManager)
537 size_t loc = C.find(
'\\');
538 if (loc == StringRef::npos) {
544 C2.reserve(C.size());
546 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
547 if (loc == StringRef::npos || loc == C.size()) {
548 C2 += C.substr(last);
551 C2 += C.substr(last, loc-last);
554 if (C[last] ==
'\n' || C[last] ==
'\r') {
559 if (C[last] ==
'\n' || C[last] ==
'\r')
560 if (C[last] != C[last-1])
586 const llvm::MemoryBuffer *FromFile = SM.
getBuffer(FID);
587 Lexer RawLex(FID, FromFile, SM, LangOpts);
598 if (!Tok.
is(tok::comment))
continue;
600 std::string Comment = RawLex.
getSpelling(Tok, SM, LangOpts);
601 if (Comment.empty())
continue;
618 if (diag_begin == diag_end)
return 0;
621 llvm::raw_svector_ostream OS(Fmt);
623 if (
I->first.isInvalid() || !SourceMgr)
624 OS <<
"\n (frontend)";
629 OS <<
" File " << File->getName();
632 OS <<
": " <<
I->second;
636 << Kind <<
true << OS.str();
644 std::vector<Directive *> &DL,
const char *
Kind) {
649 llvm::raw_svector_ostream OS(Fmt);
650 for (
auto *DirPtr : DL) {
658 OS <<
" (directive at "
661 OS <<
": " << D.
Text;
665 << Kind <<
false << OS.str();
693 bool IgnoreUnexpected) {
694 std::vector<Directive *> LeftOnly;
697 for (
auto &Owner : Left) {
701 for (
unsigned i = 0; i < D.
Max; ++i) {
703 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
706 if (LineNo1 != LineNo2)
713 const std::string &RightText = II->second;
714 if (D.
match(RightText))
719 if (i >= D.
Min)
break;
720 LeftOnly.push_back(&D);
728 unsigned num =
PrintExpected(Diags, SourceMgr, LeftOnly, Label);
729 if (!IgnoreUnexpected)
730 num +=
PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
746 unsigned NumProblems = 0;
778 setSourceManager(SM);
788 UnparsedFiles.erase(FID);
789 ParsedFiles.insert(std::make_pair(FID, FE));
790 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
794 bool FoundDirectives;
796 FoundDirectives =
false;
798 FoundDirectives = !LangOpts ||
findDirectives(SM, FID, *LangOpts);
801 UnparsedFiles.insert(std::make_pair(FID,
802 UnparsedFileStatus(FE, FoundDirectives)));
807 void VerifyDiagnosticConsumer::CheckDiagnostics() {
810 std::unique_ptr<DiagnosticConsumer> Owner = Diags.
takeClient();
820 if (UnparsedFiles.size() > 0) {
822 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
824 End = ParsedFiles.end();
I !=
End; ++
I) {
826 ParsedFileCache.insert(FE);
831 End = UnparsedFiles.end();
I !=
End; ++
I) {
832 const UnparsedFileStatus &Status =
I->second;
836 if (FE && ParsedFileCache.count(FE))
840 if (Status.foundDirectives()) {
841 llvm::report_fatal_error(Twine(
"-verify directives found after rather"
842 " than during normal parsing of ",
843 StringRef(FE ? FE->
getName() :
"(unknown)")));
848 UnparsedFiles.clear();
868 Buffer->err_end(),
"error");
871 Buffer->warn_end(),
"warn");
874 Buffer->remark_end(),
"remark");
877 Buffer->note_end(),
"note");
880 Diags.
setClient(CurClient, Owner.release() !=
nullptr);
890 bool MatchAnyLine, StringRef
Text,
891 unsigned Min,
unsigned Max) {
893 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
894 MatchAnyLine,
Text, Min, Max);
897 std::string RegexStr;
900 if (S.startswith(
"{{")) {
902 size_t RegexMatchLength = S.find(
"}}");
903 assert(RegexMatchLength != StringRef::npos);
906 RegexStr.append(S.data(), RegexMatchLength);
908 S = S.drop_front(RegexMatchLength + 2);
910 size_t VerbatimMatchLength = S.find(
"{{");
911 if (VerbatimMatchLength == StringRef::npos)
912 VerbatimMatchLength = S.size();
914 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
915 S = S.drop_front(VerbatimMatchLength);
919 return llvm::make_unique<RegexDirective>(
920 DirectiveLoc, DiagnosticLoc, MatchAnyLine,
Text, Min, Max, RegexStr);
SourceManager & getSourceManager() const
const_iterator warn_end() const
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...
SourceLocation getEnd() const
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens...
DiagnosticConsumer * getClient()
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'.
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.
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
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.
std::unique_ptr< DiagnosticConsumer > takeClient()
Return the current diagnostic client along with ownership of that client.
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.
SourceLocation DiagnosticLoc
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...
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.
void setClient(DiagnosticConsumer *client, bool ShouldOwnClient=true)
Set the diagnostic client associated with this diagnostic object.
ExpectedData - owns directive objects and deletes on destructor.
SourceLocation DirectiveLoc
Token - This structure provides full information about a lexed token.
void setKind(tok::TokenKind K)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
SourceManager & getSourceManager() const
VerifyDiagnosticConsumer::Directive Directive
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
const SourceLocation & getLocation() const
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.
HeaderSearch & getHeaderSearchInfo() const
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
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.
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
bool isWrittenInSameFile(SourceLocation Loc1, SourceLocation Loc2) const
Returns true if the spelling locations for both SourceLocations are part of the same file buffer...
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...
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. ...
const_iterator note_end() const
bool isNot(tok::TokenKind K) 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...
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.
const char * getName() const
Encodes a location in the source.
const TemplateArgument * iterator
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). ...
DiagList::const_iterator const_iterator
DiagnosticsEngine & getDiagnostics() const
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
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)) {...
virtual bool match(StringRef S)=0
File has diagnostics and may have directives.
DiagnosticsEngine & getDiagnostics() const
const DiagnosticBuilder & setForceEmit() const
Forces the diagnostic to be emitted.
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
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
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
~VerifyDiagnosticConsumer() override
bool hasSourceManager() const
SourceManager & getSourceManager() const
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 SkipCache=false)
Given a "foo" or <foo> reference, look up the indicated file.
static const unsigned MaxCount
Constant representing n or more matches.
Level
The level of the diagnostic, after it has been through mapping.
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.
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.
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.