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 {
189 if (Regex.isValid(Error))
194 bool match(StringRef
S)
override {
195 return Regex.match(S);
205 ParseHelper(StringRef
S)
206 : Begin(S.begin()),
End(S.end()),
C(Begin),
P(Begin), PEnd(
nullptr) {}
209 bool Next(StringRef S) {
214 return !memcmp(
P, S.data(), S.size());
219 bool Next(
unsigned &N) {
222 for (; P < End && P[0] >=
'0' &&
P[0] <=
'9'; ++
P) {
235 bool Search(StringRef S,
bool EnsureStartOfWord =
false) {
237 P = std::search(
C,
End, S.begin(), S.end());
241 if (!EnsureStartOfWord
245 || (
P > (Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
255 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
260 if (S.startswith(OpenBrace)) {
262 P += OpenBrace.size();
263 }
else if (S.startswith(CloseBrace)) {
266 PEnd =
P + CloseBrace.size();
269 P += CloseBrace.size();
285 void SkipWhitespace() {
295 const char *
const Begin;
296 const char *
const End;
316 bool FoundDirective =
false;
317 for (ParseHelper PH(S); !PH.Done();) {
319 if (!PH.Search(
"expected",
true))
330 if (PH.Next(
"error"))
331 DL = ED ? &ED->
Errors :
nullptr;
332 else if (PH.Next(
"warning"))
334 else if (PH.Next(
"remark"))
335 DL = ED ? &ED->
Remarks :
nullptr;
336 else if (PH.Next(
"note"))
337 DL = ED ? &ED->
Notes :
nullptr;
338 else if (PH.Next(
"no-diagnostics")) {
340 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
350 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
362 bool RegexKind =
false;
363 const char* KindStr =
"string";
366 if (PH.Next(
"-re")) {
374 bool MatchAnyLine =
false;
380 bool FoundPlus = PH.Next(
"+");
381 if (FoundPlus || PH.Next(
"-")) {
384 bool Invalid =
false;
386 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
387 if (FoundPlus) ExpectedLine +=
Line;
388 else ExpectedLine -=
Line;
391 }
else if (PH.Next(Line)) {
395 }
else if (PP && PH.Search(
":")) {
397 StringRef Filename(PH.C, PH.P-PH.C);
403 PP->
LookupFile(Pos, Filename,
false,
nullptr,
nullptr, CurDir,
404 nullptr,
nullptr,
nullptr);
407 diag::err_verify_missing_file) << Filename << KindStr;
414 if (PH.Next(Line) && Line > 0)
416 else if (PH.Next(
"*")) {
424 diag::err_verify_missing_line) << KindStr;
443 }
else if (PH.Next(
"-")) {
445 if (!PH.Next(Max) || Max < Min) {
447 diag::err_verify_invalid_range) << KindStr;
454 }
else if (PH.Next(
"+")) {
464 if (!PH.Next(
"{{")) {
466 diag::err_verify_missing_start) << KindStr;
470 const char*
const ContentBegin = PH.C;
473 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
475 diag::err_verify_missing_end) << KindStr;
478 const char*
const ContentEnd = PH.P;
483 StringRef NewlineStr =
"\\n";
484 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
487 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
488 Text += Content.substr(CPos, FPos-CPos);
490 CPos = FPos + NewlineStr.size();
493 Text.assign(ContentBegin, ContentEnd);
496 if (RegexKind && Text.find(
"{{") == StringRef::npos) {
498 diag::err_verify_missing_regex) << Text;
504 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
507 if (D->isValid(Error)) {
508 DL->push_back(std::move(D));
509 FoundDirective =
true;
512 diag::err_verify_invalid_content)
517 return FoundDirective;
527 if (SrcManager && &SM != SrcManager)
539 size_t loc = C.find(
'\\');
540 if (loc == StringRef::npos) {
546 C2.reserve(C.size());
548 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
549 if (loc == StringRef::npos || loc == C.size()) {
550 C2 += C.substr(last);
553 C2 += C.substr(last, loc-last);
556 if (C[last] ==
'\n' || C[last] ==
'\r') {
561 if (C[last] ==
'\n' || C[last] ==
'\r')
562 if (C[last] != C[last-1])
588 const llvm::MemoryBuffer *FromFile = SM.
getBuffer(FID);
589 Lexer RawLex(FID, FromFile, SM, LangOpts);
600 if (!Tok.
is(tok::comment))
continue;
602 std::string Comment = RawLex.
getSpelling(Tok, SM, LangOpts);
603 if (Comment.empty())
continue;
620 if (diag_begin == diag_end)
return 0;
623 llvm::raw_svector_ostream OS(Fmt);
626 OS <<
"\n (frontend)";
631 OS <<
" File " << File->getName();
634 OS <<
": " << I->second;
638 << Kind <<
true << OS.str();
639 return std::distance(diag_begin, diag_end);
646 std::vector<Directive *> &DL,
const char *
Kind) {
651 llvm::raw_svector_ostream OS(Fmt);
652 for (
auto *DirPtr : DL) {
660 OS <<
" (directive at "
663 OS <<
": " << D.
Text;
667 << Kind <<
false << OS.str();
695 bool IgnoreUnexpected) {
696 std::vector<Directive *> LeftOnly;
699 for (
auto &Owner : Left) {
703 for (
unsigned i = 0; i < D.
Max; ++i) {
704 DiagList::iterator II, IE;
705 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
708 if (LineNo1 != LineNo2)
715 const std::string &RightText = II->second;
716 if (D.
match(RightText))
721 if (i >= D.
Min)
break;
722 LeftOnly.push_back(&D);
730 unsigned num =
PrintExpected(Diags, SourceMgr, LeftOnly, Label);
731 if (!IgnoreUnexpected)
732 num +=
PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
748 unsigned NumProblems = 0;
780 setSourceManager(SM);
790 UnparsedFiles.erase(FID);
791 ParsedFiles.insert(std::make_pair(FID, FE));
792 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
796 bool FoundDirectives;
798 FoundDirectives =
false;
800 FoundDirectives = !LangOpts ||
findDirectives(SM, FID, *LangOpts);
803 UnparsedFiles.insert(std::make_pair(FID,
804 UnparsedFileStatus(FE, FoundDirectives)));
809 void VerifyDiagnosticConsumer::CheckDiagnostics() {
812 std::unique_ptr<DiagnosticConsumer> Owner = Diags.
takeClient();
822 if (UnparsedFiles.size() > 0) {
824 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
825 for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
826 End = ParsedFiles.end(); I !=
End; ++I) {
828 ParsedFileCache.insert(FE);
832 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
833 End = UnparsedFiles.end(); I !=
End; ++I) {
834 const UnparsedFileStatus &Status = I->second;
838 if (FE && ParsedFileCache.count(FE))
842 if (Status.foundDirectives()) {
843 llvm::report_fatal_error(Twine(
"-verify directives found after rather"
844 " than during normal parsing of ",
845 StringRef(FE ? FE->
getName() :
"(unknown)")));
850 UnparsedFiles.clear();
870 Buffer->err_end(),
"error");
873 Buffer->warn_end(),
"warn");
876 Buffer->remark_end(),
"remark");
879 Buffer->note_end(),
"note");
882 Diags.
setClient(CurClient, Owner.release() !=
nullptr);
892 bool MatchAnyLine, StringRef Text,
893 unsigned Min,
unsigned Max) {
895 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
896 MatchAnyLine, Text, Min, Max);
899 std::string RegexStr;
902 if (S.startswith(
"{{")) {
904 size_t RegexMatchLength = S.find(
"}}");
905 assert(RegexMatchLength != StringRef::npos);
908 RegexStr.append(S.data(), RegexMatchLength);
910 S = S.drop_front(RegexMatchLength + 2);
912 size_t VerbatimMatchLength = S.find(
"{{");
913 if (VerbatimMatchLength == StringRef::npos)
914 VerbatimMatchLength = S.size();
916 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
917 S = S.drop_front(VerbatimMatchLength);
921 return llvm::make_unique<RegexDirective>(
922 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)
SourceLocation getEnd() const
DiagnosticConsumer * getClient()
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)
bool LexFromRawLexer(Token &Result)
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)
VerifyDiagnosticConsumer(DiagnosticsEngine &Diags)
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
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.
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.
SourceLocation DirectiveLoc
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
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.
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. Returns null location if FID is not a file SLo...
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
bool HandleComment(Preprocessor &PP, SourceRange Comment) override
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. The SourceManager can decode this to get at the full include stack...
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
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()))
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. As a Lexer is used rather than a full-blown Preprocessor, directives inside skipped if blocks will still be found.
static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, Preprocessor *PP, SourceLocation Pos, VerifyDiagnosticConsumer::DirectiveStatus &Status)
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)
void SetCommentRetentionState(bool Mode)
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...
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.