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;
63 : Verify(Verify),
SM(
SM) {}
89 class StandardDirective :
public Directive {
92 bool MatchAnyFileAndLine,
bool MatchAnyLine, StringRef
Text,
93 unsigned Min,
unsigned Max)
94 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
95 MatchAnyLine,
Text, Min, Max) {}
102 bool match(StringRef S)
override {
return S.contains(
Text); }
106 class RegexDirective :
public Directive {
109 bool MatchAnyFileAndLine,
bool MatchAnyLine, StringRef
Text,
110 unsigned Min,
unsigned Max, StringRef RegexStr)
111 :
Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
112 MatchAnyLine,
Text, Min, Max),
119 bool match(StringRef S)
override {
120 return Regex.match(S);
130 ParseHelper(StringRef S)
134 bool Next(StringRef S) {
139 return memcmp(
P, S.data(), S.size()) == 0;
144 bool Next(
unsigned &N) {
148 for (; PEnd < End && *PEnd >=
'0' && *PEnd <=
'9'; ++PEnd) {
162 if (
P ==
End || *
P !=
'#')
181 bool Search(StringRef S,
bool EnsureStartOfWord =
false,
182 bool FinishDirectiveToken =
false) {
185 P = std::search(C,
End, S.begin(), S.end());
197 if (EnsureStartOfWord
201 || (
P > (
Begin + 1) && (
P[-1] ==
'/' ||
P[-1] ==
'*')
204 if (FinishDirectiveToken) {
206 || *PEnd ==
'-' || *PEnd ==
'_'))
213 assert(
isLetter(*
P) &&
"-verify prefix must start with a letter");
214 while (
isDigit(PEnd[-1]) || PEnd[-1] ==
'-')
224 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
228 StringRef S(
P,
End -
P);
229 if (S.startswith(OpenBrace)) {
231 P += OpenBrace.size();
232 }
else if (S.startswith(CloseBrace)) {
235 PEnd =
P + CloseBrace.size();
238 P += CloseBrace.size();
255 StringRef Match() {
return StringRef(
P, PEnd -
P); }
258 void SkipWhitespace() {
269 const char *
const Begin;
272 const char *
const End;
282 const char *PEnd =
nullptr;
286 struct UnattachedDirective {
288 bool RegexKind =
false;
291 unsigned Min = 1, Max = 1;
298 bool MatchAnyFileAndLine =
false,
299 bool MatchAnyLine =
false) {
302 UD.RegexKind, UD.DirectivePos, ExpectedLoc, MatchAnyFileAndLine,
303 MatchAnyLine, UD.Text, UD.Min, UD.Max);
306 if (!D->isValid(
Error)) {
307 Diags.
Report(UD.ContentBegin, diag::err_verify_invalid_content)
308 << (UD.RegexKind ?
"regex" :
"string") <<
Error;
311 UD.DL->push_back(std::move(D));
334 llvm::StringMap<Marker> Markers;
338 llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives;
345 auto InsertResult = Markers.insert(
348 Marker &M = InsertResult.first->second;
349 if (!InsertResult.second) {
354 auto Deferred = DeferredDirectives.find(MarkerName);
355 if (Deferred != DeferredDirectives.end()) {
356 for (
auto &UD : Deferred->second) {
357 if (M.UseLoc.isInvalid())
358 M.UseLoc = UD.DirectivePos;
359 attachDirective(Diags, UD, Pos);
361 DeferredDirectives.erase(Deferred);
367 void addDirective(StringRef MarkerName,
const UnattachedDirective &UD) {
368 auto MarkerIt = Markers.find(MarkerName);
369 if (MarkerIt != Markers.end()) {
370 Marker &M = MarkerIt->second;
371 if (M.UseLoc.isInvalid())
372 M.UseLoc = UD.DirectivePos;
373 return attachDirective(Diags, UD, M.DefLoc);
375 DeferredDirectives[MarkerName].push_back(UD);
381 for (
auto &MarkerInfo : Markers) {
382 StringRef Name = MarkerInfo.first();
383 Marker &M = MarkerInfo.second;
384 if (M.RedefLoc.isValid() && M.UseLoc.isValid()) {
385 Diags.
Report(M.UseLoc, diag::err_verify_ambiguous_marker) << Name;
386 Diags.
Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name;
387 Diags.
Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name;
391 for (
auto &DeferredPair : DeferredDirectives) {
392 Diags.
Report(DeferredPair.second.front().DirectivePos,
393 diag::err_verify_no_such_marker)
394 << DeferredPair.first();
410 for (ParseHelper PH(S); !PH.Done();) {
411 if (!PH.Search(
"#",
true))
414 if (!PH.NextMarker()) {
424 bool FoundDirective =
false;
425 for (ParseHelper PH(S); !PH.Done();) {
430 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(),
true,
true)
431 : PH.Search(
"",
true,
true)))
434 StringRef DToken = PH.Match();
438 UnattachedDirective D;
439 const char *KindStr =
"string";
448 if (DToken.endswith(
"-re")) {
451 DToken = DToken.substr(0, DToken.size()-3);
457 if (DToken.endswith(DType=
"-error"))
458 D.DL = ED ? &ED->
Errors :
nullptr;
459 else if (DToken.endswith(DType=
"-warning"))
460 D.DL = ED ? &ED->
Warnings :
nullptr;
461 else if (DToken.endswith(DType=
"-remark"))
462 D.DL = ED ? &ED->
Remarks :
nullptr;
463 else if (DToken.endswith(DType=
"-note"))
464 D.DL = ED ? &ED->
Notes :
nullptr;
465 else if (DToken.endswith(DType=
"-no-diagnostics")) {
472 DToken = DToken.substr(0, DToken.size()-DType.size());
477 if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
482 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
489 Diags.
Report(Pos, diag::err_verify_invalid_no_diags)
503 bool MatchAnyFileAndLine =
false;
504 bool MatchAnyLine =
false;
510 bool FoundPlus = PH.Next(
"+");
511 if (FoundPlus || PH.Next(
"-")) {
514 bool Invalid =
false;
515 unsigned ExpectedLine =
SM.getSpellingLineNumber(Pos, &Invalid);
516 if (!Invalid && PH.Next(
Line) && (FoundPlus ||
Line < ExpectedLine)) {
517 if (FoundPlus) ExpectedLine +=
Line;
518 else ExpectedLine -=
Line;
519 ExpectedLoc =
SM.translateLineCol(
SM.getFileID(Pos), ExpectedLine, 1);
521 }
else if (PH.Next(
Line)) {
524 ExpectedLoc =
SM.translateLineCol(
SM.getFileID(Pos),
Line, 1);
525 }
else if (PH.NextMarker()) {
527 }
else if (PP && PH.Search(
":")) {
529 StringRef
Filename(PH.C, PH.P-PH.C);
533 MatchAnyFileAndLine =
true;
536 diag::err_verify_missing_line)
546 nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
549 diag::err_verify_missing_file)
559 ExpectedLoc =
SM.translateLineCol(FID,
Line, 1);
560 else if (PH.Next(
"*")) {
562 ExpectedLoc =
SM.translateLineCol(FID, 1, 1);
565 }
else if (PH.Next(
"*")) {
570 if (ExpectedLoc.
isInvalid() && !MatchAnyLine && Marker.empty()) {
572 diag::err_verify_missing_line) << KindStr;
582 if (PH.Next(D.Min)) {
589 }
else if (PH.Next(
"-")) {
591 if (!PH.Next(D.Max) || D.Max < D.Min) {
593 diag::err_verify_invalid_range) << KindStr;
600 }
else if (PH.Next(
"+")) {
610 if (!PH.Next(
"{{")) {
612 diag::err_verify_missing_start) << KindStr;
616 const char*
const ContentBegin = PH.C;
618 if (!PH.SearchClosingBrace(
"{{",
"}}")) {
620 diag::err_verify_missing_end) << KindStr;
623 const char*
const ContentEnd = PH.P;
626 D.DirectivePos = Pos;
630 StringRef NewlineStr =
"\\n";
631 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
634 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
635 D.Text += Content.substr(CPos, FPos-CPos);
637 CPos = FPos + NewlineStr.size();
640 D.Text.assign(ContentBegin, ContentEnd);
643 if (D.RegexKind && D.Text.find(
"{{") == StringRef::npos) {
644 Diags.
Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text;
649 attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine);
652 FoundDirective =
true;
655 return FoundDirective;
659 : Diags(Diags_), PrimaryClient(Diags.getClient()),
660 PrimaryClientOwner(Diags.takeClient()),
662 Status(HasNoDirectives) {
668 assert(!ActiveSourceFiles &&
"Incomplete parsing of source files!");
669 assert(!CurrentPreprocessor &&
"CurrentPreprocessor should be invalid!");
670 SrcManager =
nullptr;
673 "The VerifyDiagnosticConsumer takes over ownership of the client!");
681 if (++ActiveSourceFiles == 1) {
683 CurrentPreprocessor = PP;
684 this->LangOpts = &LangOpts;
686 const_cast<Preprocessor *
>(PP)->addCommentHandler(
this);
690 std::make_unique<VerifyFileTracker>(*
this, *SrcManager));
695 assert((!PP || CurrentPreprocessor == PP) &&
"Preprocessor changed!");
700 assert(ActiveSourceFiles &&
"No active source files!");
704 if (--ActiveSourceFiles == 0) {
705 if (CurrentPreprocessor)
707 removeCommentHandler(
this);
714 CurrentPreprocessor =
nullptr;
741 if (FE && CurrentPreprocessor && SrcManager->
isLoadedFileID(FID)) {
756 Buffer->HandleDiagnostic(DiagLevel, Info);
766 if (SrcManager && &
SM != SrcManager)
771 const char *CommentRaw =
SM.getCharacterData(CommentBegin);
772 StringRef C(CommentRaw,
SM.getCharacterData(Comment.
getEnd()) - CommentRaw);
778 size_t loc = C.find(
'\\');
779 if (loc == StringRef::npos) {
785 C2.reserve(C.size());
787 for (
size_t last = 0;; loc = C.find(
'\\', last)) {
788 if (loc == StringRef::npos || loc == C.size()) {
789 C2 += C.substr(last);
792 C2 += C.substr(last, loc-last);
795 if (C[last] ==
'\n' || C[last] ==
'\r') {
800 if (C[last] ==
'\n' || C[last] ==
'\r')
801 if (C[last] != C[last-1])
827 llvm::MemoryBufferRef FromFile =
SM.getBufferOrFake(FID);
828 Lexer RawLex(FID, FromFile,
SM, LangOpts);
839 if (!Tok.
is(tok::comment))
continue;
842 if (Comment.empty())
continue;
862 if (diag_begin == diag_end)
return 0;
865 llvm::raw_svector_ostream OS(Fmt);
867 if (I->first.isInvalid() || !SourceMgr)
868 OS <<
"\n (frontend)";
873 OS <<
" File " <<
File->getName();
876 OS <<
": " << I->second;
880 <<
Kind <<
true << OS.str();
888 std::vector<Directive *> &DL,
const char *
Kind) {
893 llvm::raw_svector_ostream OS(Fmt);
894 for (
const auto *D : DL) {
895 if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
898 OS <<
"\n File " << SourceMgr.
getFilename(D->DiagnosticLoc);
903 if (D->DirectiveLoc != D->DiagnosticLoc)
904 OS <<
" (directive at "
907 OS <<
": " << D->Text;
911 <<
Kind <<
false << OS.str();
919 DiagnosticLoc =
SM.getImmediateMacroCallerLoc(DiagnosticLoc);
921 if (
SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
924 const FileEntry *DiagFile =
SM.getFileEntryForID(
SM.getFileID(DiagnosticLoc));
925 if (!DiagFile &&
SM.isWrittenInMainFile(DirectiveLoc))
928 return (DiagFile ==
SM.getFileEntryForID(
SM.getFileID(DirectiveLoc)));
938 bool IgnoreUnexpected) {
939 std::vector<Directive *> LeftOnly;
942 for (
auto &Owner : Left) {
946 for (
unsigned i = 0; i < D.
Max; ++i) {
947 DiagList::iterator II, IE;
948 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
951 if (LineNo1 != LineNo2)
960 if (D.
match(RightText))
965 if (i >= D.
Min)
break;
966 LeftOnly.push_back(&D);
975 if (!IgnoreUnexpected)
991 unsigned NumProblems = 0;
1023 setSourceManager(
SM);
1033 UnparsedFiles.erase(FID);
1034 ParsedFiles.insert(std::make_pair(FID, FE));
1035 }
else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
1039 bool FoundDirectives;
1041 FoundDirectives =
false;
1046 UnparsedFiles.insert(std::make_pair(FID,
1047 UnparsedFileStatus(FE, FoundDirectives)));
1052 void VerifyDiagnosticConsumer::CheckDiagnostics() {
1055 std::unique_ptr<DiagnosticConsumer> Owner = Diags.
takeClient();
1065 if (!UnparsedFiles.empty()) {
1068 for (
const auto &I : ParsedFiles)
1070 ParsedFileCache.insert(FE);
1073 for (
const auto &I : UnparsedFiles) {
1074 const UnparsedFileStatus &Status = I.second;
1078 if (FE && ParsedFileCache.count(FE))
1082 if (Status.foundDirectives()) {
1083 llvm::report_fatal_error(Twine(
"-verify directives found after rather"
1084 " than during normal parsing of ",
1085 StringRef(FE ? FE->
getName() :
"(unknown)")));
1090 UnparsedFiles.clear();
1110 Buffer->err_end(),
"error");
1113 Buffer->warn_end(),
"warn");
1116 Buffer->remark_end(),
"remark");
1119 Buffer->note_end(),
"note");
1122 Diags.
setClient(CurClient, Owner.release() !=
nullptr);
1132 bool MatchAnyFileAndLine,
1133 bool MatchAnyLine, StringRef
Text,
1134 unsigned Min,
unsigned Max) {
1136 return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
1137 MatchAnyFileAndLine,
1138 MatchAnyLine,
Text, Min, Max);
1143 while (!S.empty()) {
1144 if (S.startswith(
"{{")) {
1145 S = S.drop_front(2);
1146 size_t RegexMatchLength = S.find(
"}}");
1147 assert(RegexMatchLength != StringRef::npos);
1150 RegexStr.append(S.data(), RegexMatchLength);
1152 S = S.drop_front(RegexMatchLength + 2);
1154 size_t VerbatimMatchLength = S.find(
"{{");
1155 if (VerbatimMatchLength == StringRef::npos)
1156 VerbatimMatchLength = S.size();
1158 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
1159 S = S.drop_front(VerbatimMatchLength);
1163 return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc,
1164 MatchAnyFileAndLine, MatchAnyLine,
1165 Text, Min, Max, RegexStr);