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" 47 using namespace clang;
54 : Diags(Diags_), PrimaryClient(Diags.getClient()),
55 PrimaryClientOwner(Diags.takeClient()),
62 assert(!ActiveSourceFiles &&
"Incomplete parsing of source files!");
63 assert(!CurrentPreprocessor &&
"CurrentPreprocessor should be invalid!");
67 "The VerifyDiagnosticConsumer takes over ownership of the client!");
80 : Verify(Verify),
SM(SM) {}
101 if (++ActiveSourceFiles == 1) {
103 CurrentPreprocessor = PP;
104 this->LangOpts = &LangOpts;
106 const_cast<Preprocessor *
>(PP)->addCommentHandler(
this);
110 llvm::make_unique<VerifyFileTracker>(*
this, *SrcManager));
115 assert((!PP || CurrentPreprocessor == PP) &&
"Preprocessor changed!");
116 PrimaryClient->BeginSourceFile(LangOpts, PP);
120 assert(ActiveSourceFiles &&
"No active source files!");
121 PrimaryClient->EndSourceFile();
124 if (--ActiveSourceFiles == 0) {
125 if (CurrentPreprocessor)
127 removeCommentHandler(
this);
131 CurrentPreprocessor =
nullptr;
154 Loc = SrcManager->getExpansionLoc(Loc);
155 FileID FID = SrcManager->getFileID(Loc);
157 const FileEntry *FE = SrcManager->getFileEntryForID(FID);
158 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
161 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
173 Buffer->HandleDiagnostic(DiagLevel, Info);
186 class StandardDirective :
public Directive {
189 bool MatchAnyLine, StringRef
Text,
unsigned Min,
191 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {}
193 bool isValid(std::string &
Error)
override {
198 bool match(StringRef S)
override {
199 return S.find(Text) != StringRef::npos;
204 class RegexDirective :
public Directive {
207 bool MatchAnyLine, StringRef
Text,
unsigned Min,
unsigned Max,
209 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
212 bool isValid(std::string &
Error)
override {
216 bool match(StringRef S)
override {
217 return Regex.match(S);
227 ParseHelper(StringRef S)
231 bool Next(StringRef S) {
236 return memcmp(
P, S.data(), S.size()) == 0;
241 bool Next(
unsigned &N) {
244 for (; P < End && P[0] >=
'0' &&
P[0] <=
'9'; ++
P) {
264 bool Search(StringRef S,
bool EnsureStartOfWord =
false,
265 bool FinishDirectiveToken =
false) {
268 P = std::search(
C,
End, S.begin(), S.end());
280 if (EnsureStartOfWord
284 || (
P > (
Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
287 if (FinishDirectiveToken) {
289 || *PEnd ==
'-' || *PEnd ==
'_'))
296 assert(
isLetter(*
P) &&
"-verify prefix must start with a letter");
297 while (
isDigit(PEnd[-1]) || PEnd[-1] ==
'-')
307 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
311 StringRef S(
P,
End -
P);
312 if (S.startswith(OpenBrace)) {
314 P += OpenBrace.size();
315 }
else if (S.startswith(CloseBrace)) {
318 PEnd =
P + CloseBrace.size();
321 P += CloseBrace.size();
337 void SkipWhitespace() {
348 const char *
const Begin;
351 const char *
const End;
360 const char *PEnd =
nullptr;
375 bool FoundDirective =
false;
376 for (ParseHelper PH(S); !PH.Done();) {
381 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(),
true,
true)
382 : PH.Search(
"",
true,
true)))
387 bool RegexKind =
false;
388 const char* KindStr =
"string";
395 StringRef DToken(PH.P, PH.C - PH.P);
398 if (DToken.endswith(
"-re")) {
401 DToken = DToken.substr(0, DToken.size()-3);
408 if (DToken.endswith(DType=
"-error"))
409 DL = ED ? &ED->
Errors :
nullptr;
410 else if (DToken.endswith(DType=
"-warning"))
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")) {
423 DToken = DToken.substr(0, DToken.size()-DType.size());
428 if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
433 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
440 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
453 bool MatchAnyLine =
false;
459 bool FoundPlus = PH.Next(
"+");
460 if (FoundPlus || PH.Next(
"-")) {
463 bool Invalid =
false;
465 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
466 if (FoundPlus) ExpectedLine +=
Line;
467 else ExpectedLine -=
Line;
470 }
else if (PH.Next(Line)) {
474 }
else if (PP && PH.Search(
":")) {
476 StringRef
Filename(PH.C, PH.P-PH.C);
482 PP->
LookupFile(Pos, Filename,
false,
nullptr,
nullptr, CurDir,
483 nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
486 diag::err_verify_missing_file) << Filename << KindStr;
493 if (PH.Next(Line) && Line > 0)
495 else if (PH.Next(
"*")) {
499 }
else if (PH.Next(
"*")) {
504 if (ExpectedLoc.
isInvalid() && !MatchAnyLine) {
506 diag::err_verify_missing_line) << KindStr;
525 }
else if (PH.Next(
"-")) {
527 if (!PH.Next(Max) || Max < Min) {
529 diag::err_verify_invalid_range) << KindStr;
536 }
else if (PH.Next(
"+")) {
546 if (!PH.Next(
"{{")) {
548 diag::err_verify_missing_start) << KindStr;
552 const char*
const ContentBegin = PH.C;
555 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
557 diag::err_verify_missing_end) << KindStr;
560 const char*
const ContentEnd = PH.P;
565 StringRef NewlineStr =
"\\n";
566 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
569 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
570 Text += Content.substr(CPos, FPos-CPos);
572 CPos = FPos + NewlineStr.size();
575 Text.assign(ContentBegin, ContentEnd);
578 if (RegexKind && Text.find(
"{{") == StringRef::npos) {
580 diag::err_verify_missing_regex) << Text;
586 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
589 if (D->isValid(Error)) {
590 DL->push_back(std::move(D));
591 FoundDirective =
true;
594 diag::err_verify_invalid_content)
599 return FoundDirective;
609 if (SrcManager && &SM != SrcManager)
621 size_t loc = C.find(
'\\');
622 if (loc == StringRef::npos) {
628 C2.reserve(C.size());
630 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
631 if (loc == StringRef::npos || loc == C.size()) {
632 C2 += C.substr(last);
635 C2 += C.substr(last, loc-last);
638 if (C[last] ==
'\n' || C[last] ==
'\r') {
643 if (C[last] ==
'\n' || C[last] ==
'\r')
644 if (C[last] != C[last-1])
670 const llvm::MemoryBuffer *FromFile = SM.
getBuffer(FID);
671 Lexer RawLex(FID, FromFile, SM, LangOpts);
682 if (!Tok.
is(tok::comment))
continue;
684 std::string Comment = RawLex.
getSpelling(Tok, SM, LangOpts);
685 if (Comment.empty())
continue;
702 if (diag_begin == diag_end)
return 0;
705 llvm::raw_svector_ostream OS(Fmt);
707 if (I->first.isInvalid() || !SourceMgr)
708 OS <<
"\n (frontend)";
713 OS <<
" File " << File->getName();
716 OS <<
": " << I->second;
720 << Kind <<
true << OS.str();
728 std::vector<Directive *> &DL,
const char *
Kind) {
733 llvm::raw_svector_ostream OS(Fmt);
734 for (
const auto *D : DL) {
735 if (D->DiagnosticLoc.isInvalid())
738 OS <<
"\n File " << SourceMgr.
getFilename(D->DiagnosticLoc);
743 if (D->DirectiveLoc != D->DiagnosticLoc)
744 OS <<
" (directive at " 747 OS <<
": " << D->Text;
751 << Kind <<
false << OS.str();
778 bool IgnoreUnexpected) {
779 std::vector<Directive *> LeftOnly;
782 for (
auto &Owner : Left) {
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) {
791 if (LineNo1 != LineNo2)
799 const std::string &RightText = II->second;
800 if (D.
match(RightText))
805 if (i >= D.
Min)
break;
806 LeftOnly.push_back(&D);
814 unsigned num =
PrintExpected(Diags, SourceMgr, LeftOnly, Label);
815 if (!IgnoreUnexpected)
831 unsigned NumProblems = 0;
863 setSourceManager(SM);
873 UnparsedFiles.erase(FID);
874 ParsedFiles.insert(std::make_pair(FID, FE));
875 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
879 bool FoundDirectives;
881 FoundDirectives =
false;
883 FoundDirectives = !LangOpts ||
findDirectives(SM, FID, *LangOpts);
886 UnparsedFiles.insert(std::make_pair(FID,
887 UnparsedFileStatus(FE, FoundDirectives)));
892 void VerifyDiagnosticConsumer::CheckDiagnostics() {
895 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
896 Diags.setClient(PrimaryClient,
false);
905 if (!UnparsedFiles.empty()) {
907 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
908 for (
const auto &I : ParsedFiles)
910 ParsedFileCache.insert(FE);
913 for (
const auto &I : UnparsedFiles) {
914 const UnparsedFileStatus &Status = I.second;
918 if (FE && ParsedFileCache.count(FE))
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)")));
930 UnparsedFiles.clear();
938 Diags.Report(diag::err_verify_no_directives).setForceEmit();
947 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
950 Buffer->err_end(),
"error");
953 Buffer->warn_end(),
"warn");
956 Buffer->remark_end(),
"remark");
959 Buffer->note_end(),
"note");
962 Diags.setClient(CurClient, Owner.release() !=
nullptr);
972 bool MatchAnyLine, StringRef
Text,
973 unsigned Min,
unsigned Max) {
975 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
976 MatchAnyLine,
Text, Min, Max);
979 std::string RegexStr;
982 if (S.startswith(
"{{")) {
984 size_t RegexMatchLength = S.find(
"}}");
985 assert(RegexMatchLength != StringRef::npos);
988 RegexStr.append(S.data(), RegexMatchLength);
990 S = S.drop_front(RegexMatchLength + 2);
992 size_t VerbatimMatchLength = S.find(
"{{");
993 if (VerbatimMatchLength == StringRef::npos)
994 VerbatimMatchLength = S.size();
996 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
997 S = S.drop_front(VerbatimMatchLength);
1001 return llvm::make_unique<RegexDirective>(
1002 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
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). ...
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
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.