1 //===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This is a concrete diagnostic client, which buffers the diagnostic messages.
11 //===----------------------------------------------------------------------===//
13 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
14 #include "clang/Basic/CharInfo.h"
15 #include "clang/Basic/Diagnostic.h"
16 #include "clang/Basic/DiagnosticOptions.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/LLVM.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Basic/TokenKinds.h"
22 #include "clang/Frontend/FrontendDiagnostic.h"
23 #include "clang/Frontend/TextDiagnosticBuffer.h"
24 #include "clang/Lex/HeaderSearch.h"
25 #include "clang/Lex/Lexer.h"
26 #include "clang/Lex/PPCallbacks.h"
27 #include "clang/Lex/Preprocessor.h"
28 #include "clang/Lex/Token.h"
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
;
49 using Directive
= VerifyDiagnosticConsumer::Directive
;
50 using DirectiveList
= VerifyDiagnosticConsumer::DirectiveList
;
51 using ExpectedData
= VerifyDiagnosticConsumer::ExpectedData
;
57 class VerifyFileTracker
: public PPCallbacks
{
58 VerifyDiagnosticConsumer
&Verify
;
62 VerifyFileTracker(VerifyDiagnosticConsumer
&Verify
, SourceManager
&SM
)
63 : Verify(Verify
), SM(SM
) {}
65 /// Hook into the preprocessor and update the list of parsed
66 /// files when the preprocessor indicates a new file is entered.
67 void FileChanged(SourceLocation Loc
, FileChangeReason Reason
,
68 SrcMgr::CharacteristicKind FileType
,
69 FileID PrevFID
) override
{
70 Verify
.UpdateParsedFileStatus(SM
, SM
.getFileID(Loc
),
71 VerifyDiagnosticConsumer::IsParsed
);
79 //===----------------------------------------------------------------------===//
80 // Checking diagnostics implementation.
81 //===----------------------------------------------------------------------===//
83 using DiagList
= TextDiagnosticBuffer::DiagList
;
84 using const_diag_iterator
= TextDiagnosticBuffer::const_iterator
;
88 /// StandardDirective - Directive with string matching.
89 class StandardDirective
: public Directive
{
91 StandardDirective(SourceLocation DirectiveLoc
, SourceLocation DiagnosticLoc
,
92 bool MatchAnyFileAndLine
, bool MatchAnyLine
, StringRef Text
,
93 unsigned Min
, unsigned Max
)
94 : Directive(DirectiveLoc
, DiagnosticLoc
, MatchAnyFileAndLine
,
95 MatchAnyLine
, Text
, Min
, Max
) {}
97 bool isValid(std::string
&Error
) override
{
98 // all strings are considered valid; even empty ones
102 bool match(StringRef S
) override
{ return S
.contains(Text
); }
105 /// RegexDirective - Directive with regular-expression matching.
106 class RegexDirective
: public Directive
{
108 RegexDirective(SourceLocation DirectiveLoc
, SourceLocation DiagnosticLoc
,
109 bool MatchAnyFileAndLine
, bool MatchAnyLine
, StringRef Text
,
110 unsigned Min
, unsigned Max
, StringRef RegexStr
)
111 : Directive(DirectiveLoc
, DiagnosticLoc
, MatchAnyFileAndLine
,
112 MatchAnyLine
, Text
, Min
, Max
),
115 bool isValid(std::string
&Error
) override
{
116 return Regex
.isValid(Error
);
119 bool match(StringRef S
) override
{
120 return Regex
.match(S
);
130 ParseHelper(StringRef S
)
131 : Begin(S
.begin()), End(S
.end()), C(Begin
), P(Begin
) {}
133 // Return true if string literal is next.
134 bool Next(StringRef S
) {
139 return memcmp(P
, S
.data(), S
.size()) == 0;
142 // Return true if number is next.
143 // Output N only if number is next.
144 bool Next(unsigned &N
) {
148 for (; PEnd
< End
&& *PEnd
>= '0' && *PEnd
<= '9'; ++PEnd
) {
158 // Return true if a marker is next.
159 // A marker is the longest match for /#[A-Za-z0-9_-]+/.
162 if (P
== End
|| *P
!= '#')
166 while ((isAlphanumeric(*PEnd
) || *PEnd
== '-' || *PEnd
== '_') &&
172 // Return true if string literal S is matched in content.
173 // When true, P marks begin-position of the match, and calling Advance sets C
174 // to end-position of the match.
175 // If S is the empty string, then search for any letter instead (makes sense
176 // with FinishDirectiveToken=true).
177 // If EnsureStartOfWord, then skip matches that don't start a new word.
178 // If FinishDirectiveToken, then assume the match is the start of a comment
179 // directive for -verify, and extend the match to include the entire first
180 // token of that directive.
181 bool Search(StringRef S
, bool EnsureStartOfWord
= false,
182 bool FinishDirectiveToken
= false) {
185 P
= std::search(C
, End
, S
.begin(), S
.end());
190 while (P
!= End
&& !isLetter(*P
))
196 // If not start of word but required, skip and search again.
197 if (EnsureStartOfWord
198 // Check if string literal starts a new word.
199 && !(P
== Begin
|| isWhitespace(P
[-1])
200 // Or it could be preceded by the start of a comment.
201 || (P
> (Begin
+ 1) && (P
[-1] == '/' || P
[-1] == '*')
204 if (FinishDirectiveToken
) {
205 while (PEnd
!= End
&& (isAlphanumeric(*PEnd
)
206 || *PEnd
== '-' || *PEnd
== '_'))
208 // Put back trailing digits and hyphens to be parsed later as a count
209 // or count range. Because -verify prefixes must start with letters,
210 // we know the actual directive we found starts with a letter, so
211 // we won't put back the entire directive word and thus record an empty
213 assert(isLetter(*P
) && "-verify prefix must start with a letter");
214 while (isDigit(PEnd
[-1]) || PEnd
[-1] == '-')
222 // Return true if a CloseBrace that closes the OpenBrace at the current nest
223 // level is found. When true, P marks begin-position of CloseBrace.
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();
246 // Advance 1-past previous next/search.
247 // Behavior is undefined if previous next/search failed.
253 // Return the text matched by the previous next/search.
254 // Behavior is undefined if previous next/search failed.
255 StringRef
Match() { return StringRef(P
, PEnd
- P
); }
257 // Skip zero or more whitespace.
258 void SkipWhitespace() {
259 for (; C
< End
&& isWhitespace(*C
); ++C
)
263 // Return true if EOF reached.
268 // Beginning of expected content.
269 const char * const Begin
;
271 // End of expected content (1-past).
272 const char * const End
;
274 // Position of next char in content.
277 // Previous next/search subject start.
281 // Previous next/search subject end (1-past).
282 const char *PEnd
= nullptr;
285 // The information necessary to create a directive.
286 struct UnattachedDirective
{
287 DirectiveList
*DL
= nullptr;
288 bool RegexKind
= false;
289 SourceLocation DirectivePos
, ContentBegin
;
291 unsigned Min
= 1, Max
= 1;
294 // Attach the specified directive to the line of code indicated by
296 void attachDirective(DiagnosticsEngine
&Diags
, const UnattachedDirective
&UD
,
297 SourceLocation ExpectedLoc
,
298 bool MatchAnyFileAndLine
= false,
299 bool MatchAnyLine
= false) {
300 // Construct new directive.
301 std::unique_ptr
<Directive
> D
= Directive::create(
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
));
316 // Tracker for markers in the input files. A marker is a comment of the form
320 // ... that can be referred to by a later expected-* directive:
322 // // expected-error@#123 {{undeclared identifier 'n'}}
324 // Marker declarations must be at the start of a comment or preceded by
325 // whitespace to distinguish them from uses of markers in directives.
326 class VerifyDiagnosticConsumer::MarkerTracker
{
327 DiagnosticsEngine
&Diags
;
330 SourceLocation DefLoc
;
331 SourceLocation RedefLoc
;
332 SourceLocation UseLoc
;
334 llvm::StringMap
<Marker
> Markers
;
336 // Directives that couldn't be created yet because they name an unknown
338 llvm::StringMap
<llvm::SmallVector
<UnattachedDirective
, 2>> DeferredDirectives
;
341 MarkerTracker(DiagnosticsEngine
&Diags
) : Diags(Diags
) {}
343 // Register a marker.
344 void addMarker(StringRef MarkerName
, SourceLocation Pos
) {
345 auto InsertResult
= Markers
.insert(
346 {MarkerName
, Marker
{Pos
, SourceLocation(), SourceLocation()}});
348 Marker
&M
= InsertResult
.first
->second
;
349 if (!InsertResult
.second
) {
350 // Marker was redefined.
353 // First definition: build any deferred directives.
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
);
366 // Register a directive at the specified marker.
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
);
378 // Ensure we have no remaining deferred directives, and no
379 // multiply-defined-and-used markers.
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();
399 /// ParseDirective - Go through the comment and see if it indicates expected
400 /// diagnostics. If so, then put them in the appropriate directive list.
402 /// Returns true if any valid directives were found.
403 static bool ParseDirective(StringRef S
, ExpectedData
*ED
, SourceManager
&SM
,
404 Preprocessor
*PP
, SourceLocation Pos
,
405 VerifyDiagnosticConsumer::DirectiveStatus
&Status
,
406 VerifyDiagnosticConsumer::MarkerTracker
&Markers
) {
407 DiagnosticsEngine
&Diags
= PP
? PP
->getDiagnostics() : SM
.getDiagnostics();
409 // First, scan the comment looking for markers.
410 for (ParseHelper
PH(S
); !PH
.Done();) {
411 if (!PH
.Search("#", true))
414 if (!PH
.NextMarker()) {
420 Markers
.addMarker(PH
.Match(), Pos
);
423 // A single comment may contain multiple directives.
424 bool FoundDirective
= false;
425 for (ParseHelper
PH(S
); !PH
.Done();) {
426 // Search for the initial directive token.
427 // If one prefix, save time by searching only for its directives.
428 // Otherwise, search for any potential directive token and check it later.
429 const auto &Prefixes
= Diags
.getDiagnosticOptions().VerifyPrefixes
;
430 if (!(Prefixes
.size() == 1 ? PH
.Search(*Prefixes
.begin(), true, true)
431 : PH
.Search("", true, true)))
434 StringRef DToken
= PH
.Match();
437 // Default directive kind.
438 UnattachedDirective D
;
439 const char *KindStr
= "string";
441 // Parse the initial directive token in reverse so we can easily determine
442 // its exact actual prefix. If we were to parse it from the front instead,
443 // it would be harder to determine where the prefix ends because there
444 // might be multiple matching -verify prefixes because some might prefix
447 // Regex in initial directive token: -re
448 if (DToken
.endswith("-re")) {
451 DToken
= DToken
.substr(0, DToken
.size()-3);
454 // Type in initial directive token: -{error|warning|note|no-diagnostics}
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());
474 // What's left in DToken is the actual prefix. That might not be a -verify
475 // prefix even if there is only one -verify prefix (for example, the full
476 // DToken is foo-bar-warning, but foo is the only -verify prefix).
477 if (!std::binary_search(Prefixes
.begin(), Prefixes
.end(), DToken
))
481 if (Status
== VerifyDiagnosticConsumer::HasOtherExpectedDirectives
)
482 Diags
.Report(Pos
, diag::err_verify_invalid_no_diags
)
483 << /*IsExpectedNoDiagnostics=*/true;
485 Status
= VerifyDiagnosticConsumer::HasExpectedNoDiagnostics
;
488 if (Status
== VerifyDiagnosticConsumer::HasExpectedNoDiagnostics
) {
489 Diags
.Report(Pos
, diag::err_verify_invalid_no_diags
)
490 << /*IsExpectedNoDiagnostics=*/false;
493 Status
= VerifyDiagnosticConsumer::HasOtherExpectedDirectives
;
495 // If a directive has been found but we're not interested
496 // in storing the directive information, return now.
500 // Next optional token: @
501 SourceLocation ExpectedLoc
;
503 bool MatchAnyFileAndLine
= false;
504 bool MatchAnyLine
= false;
510 bool FoundPlus
= PH
.Next("+");
511 if (FoundPlus
|| PH
.Next("-")) {
512 // Relative to current line.
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
)) {
522 // Absolute line number.
524 ExpectedLoc
= SM
.translateLineCol(SM
.getFileID(Pos
), Line
, 1);
525 } else if (PH
.NextMarker()) {
527 } else if (PP
&& PH
.Search(":")) {
528 // Specific source file.
529 StringRef
Filename(PH
.C
, PH
.P
-PH
.C
);
532 if (Filename
== "*") {
533 MatchAnyFileAndLine
= true;
535 Diags
.Report(Pos
.getLocWithOffset(PH
.C
- PH
.Begin
),
536 diag::err_verify_missing_line
)
541 ExpectedLoc
= SourceLocation();
543 // Lookup file via Preprocessor, like a #include.
544 OptionalFileEntryRef File
=
545 PP
->LookupFile(Pos
, Filename
, false, nullptr, nullptr, nullptr,
546 nullptr, nullptr, nullptr, nullptr, nullptr);
548 Diags
.Report(Pos
.getLocWithOffset(PH
.C
- PH
.Begin
),
549 diag::err_verify_missing_file
)
550 << Filename
<< KindStr
;
554 FileID FID
= SM
.translateFile(*File
);
556 FID
= SM
.createFileID(*File
, Pos
, SrcMgr::C_User
);
558 if (PH
.Next(Line
) && Line
> 0)
559 ExpectedLoc
= SM
.translateLineCol(FID
, Line
, 1);
560 else if (PH
.Next("*")) {
562 ExpectedLoc
= SM
.translateLineCol(FID
, 1, 1);
565 } else if (PH
.Next("*")) {
567 ExpectedLoc
= SourceLocation();
570 if (ExpectedLoc
.isInvalid() && !MatchAnyLine
&& Marker
.empty()) {
571 Diags
.Report(Pos
.getLocWithOffset(PH
.C
-PH
.Begin
),
572 diag::err_verify_missing_line
) << KindStr
;
578 // Skip optional whitespace.
581 // Next optional token: positive integer or a '+'.
582 if (PH
.Next(D
.Min
)) {
584 // A positive integer can be followed by a '+' meaning min
585 // or more, or by a '-' meaning a range from min to max.
587 D
.Max
= Directive::MaxCount
;
589 } else if (PH
.Next("-")) {
591 if (!PH
.Next(D
.Max
) || D
.Max
< D
.Min
) {
592 Diags
.Report(Pos
.getLocWithOffset(PH
.C
-PH
.Begin
),
593 diag::err_verify_invalid_range
) << KindStr
;
600 } else if (PH
.Next("+")) {
601 // '+' on its own means "1 or more".
602 D
.Max
= Directive::MaxCount
;
606 // Skip optional whitespace.
610 if (!PH
.Next("{{")) {
611 Diags
.Report(Pos
.getLocWithOffset(PH
.C
-PH
.Begin
),
612 diag::err_verify_missing_start
) << KindStr
;
616 const char* const ContentBegin
= PH
.C
; // mark content begin
617 // Search for token: }}
618 if (!PH
.SearchClosingBrace("{{", "}}")) {
619 Diags
.Report(Pos
.getLocWithOffset(PH
.C
-PH
.Begin
),
620 diag::err_verify_missing_end
) << KindStr
;
623 const char* const ContentEnd
= PH
.P
; // mark content end
626 D
.DirectivePos
= Pos
;
627 D
.ContentBegin
= Pos
.getLocWithOffset(ContentBegin
- PH
.Begin
);
629 // Build directive text; convert \n to newlines.
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
);
642 // Check that regex directives contain at least one regex.
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
);
651 Markers
.addDirective(Marker
, D
);
652 FoundDirective
= true;
655 return FoundDirective
;
658 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine
&Diags_
)
659 : Diags(Diags_
), PrimaryClient(Diags
.getClient()),
660 PrimaryClientOwner(Diags
.takeClient()),
661 Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags
)),
662 Status(HasNoDirectives
) {
663 if (Diags
.hasSourceManager())
664 setSourceManager(Diags
.getSourceManager());
667 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
668 assert(!ActiveSourceFiles
&& "Incomplete parsing of source files!");
669 assert(!CurrentPreprocessor
&& "CurrentPreprocessor should be invalid!");
670 SrcManager
= nullptr;
672 assert(!Diags
.ownsClient() &&
673 "The VerifyDiagnosticConsumer takes over ownership of the client!");
676 // DiagnosticConsumer interface.
678 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions
&LangOpts
,
679 const Preprocessor
*PP
) {
680 // Attach comment handler on first invocation.
681 if (++ActiveSourceFiles
== 1) {
683 CurrentPreprocessor
= PP
;
684 this->LangOpts
= &LangOpts
;
685 setSourceManager(PP
->getSourceManager());
686 const_cast<Preprocessor
*>(PP
)->addCommentHandler(this);
688 // Debug build tracks parsed files.
689 const_cast<Preprocessor
*>(PP
)->addPPCallbacks(
690 std::make_unique
<VerifyFileTracker
>(*this, *SrcManager
));
695 assert((!PP
|| CurrentPreprocessor
== PP
) && "Preprocessor changed!");
696 PrimaryClient
->BeginSourceFile(LangOpts
, PP
);
699 void VerifyDiagnosticConsumer::EndSourceFile() {
700 assert(ActiveSourceFiles
&& "No active source files!");
701 PrimaryClient
->EndSourceFile();
703 // Detach comment handler once last active source file completed.
704 if (--ActiveSourceFiles
== 0) {
705 if (CurrentPreprocessor
)
706 const_cast<Preprocessor
*>(CurrentPreprocessor
)->
707 removeCommentHandler(this);
709 // Diagnose any used-but-not-defined markers.
712 // Check diagnostics once last file completed.
714 CurrentPreprocessor
= nullptr;
719 void VerifyDiagnosticConsumer::HandleDiagnostic(
720 DiagnosticsEngine::Level DiagLevel
, const Diagnostic
&Info
) {
721 if (Info
.hasSourceManager()) {
722 // If this diagnostic is for a different source manager, ignore it.
723 if (SrcManager
&& &Info
.getSourceManager() != SrcManager
)
726 setSourceManager(Info
.getSourceManager());
730 // Debug build tracks unparsed files for possible
731 // unparsed expected-* directives.
733 SourceLocation Loc
= Info
.getLocation();
735 ParsedStatus PS
= IsUnparsed
;
737 Loc
= SrcManager
->getExpansionLoc(Loc
);
738 FileID FID
= SrcManager
->getFileID(Loc
);
740 const FileEntry
*FE
= SrcManager
->getFileEntryForID(FID
);
741 if (FE
&& CurrentPreprocessor
&& SrcManager
->isLoadedFileID(FID
)) {
742 // If the file is a modules header file it shall not be parsed
743 // for expected-* directives.
744 HeaderSearch
&HS
= CurrentPreprocessor
->getHeaderSearchInfo();
745 if (HS
.findModuleForHeader(FE
))
746 PS
= IsUnparsedNoDirectives
;
749 UpdateParsedFileStatus(*SrcManager
, FID
, PS
);
754 // Send the diagnostic to the buffer, we will check it once we reach the end
755 // of the source file (or are destructed).
756 Buffer
->HandleDiagnostic(DiagLevel
, Info
);
759 /// HandleComment - Hook into the preprocessor and extract comments containing
760 /// expected errors and warnings.
761 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor
&PP
,
762 SourceRange Comment
) {
763 SourceManager
&SM
= PP
.getSourceManager();
765 // If this comment is for a different source manager, ignore it.
766 if (SrcManager
&& &SM
!= SrcManager
)
769 SourceLocation CommentBegin
= Comment
.getBegin();
771 const char *CommentRaw
= SM
.getCharacterData(CommentBegin
);
772 StringRef
C(CommentRaw
, SM
.getCharacterData(Comment
.getEnd()) - CommentRaw
);
777 // Fold any "\<EOL>" sequences
778 size_t loc
= C
.find('\\');
779 if (loc
== StringRef::npos
) {
780 ParseDirective(C
, &ED
, SM
, &PP
, CommentBegin
, Status
, *Markers
);
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') {
798 // Escape \r\n or \n\r, but not \n\n.
800 if (C
[last
] == '\n' || C
[last
] == '\r')
801 if (C
[last
] != C
[last
-1])
804 // This was just a normal backslash.
810 ParseDirective(C2
, &ED
, SM
, &PP
, CommentBegin
, Status
, *Markers
);
815 /// Lex the specified source file to determine whether it contains
816 /// any expected-* directives. As a Lexer is used rather than a full-blown
817 /// Preprocessor, directives inside skipped #if blocks will still be found.
819 /// \return true if any directives were found.
820 static bool findDirectives(SourceManager
&SM
, FileID FID
,
821 const LangOptions
&LangOpts
) {
822 // Create a raw lexer to pull all the comments out of FID.
826 // Create a lexer to lex all the tokens of the main file in raw mode.
827 llvm::MemoryBufferRef FromFile
= SM
.getBufferOrFake(FID
);
828 Lexer
RawLex(FID
, FromFile
, SM
, LangOpts
);
830 // Return comments as tokens, this is how we find expected diagnostics.
831 RawLex
.SetCommentRetentionState(true);
834 Tok
.setKind(tok::comment
);
835 VerifyDiagnosticConsumer::DirectiveStatus Status
=
836 VerifyDiagnosticConsumer::HasNoDirectives
;
837 while (Tok
.isNot(tok::eof
)) {
838 RawLex
.LexFromRawLexer(Tok
);
839 if (!Tok
.is(tok::comment
)) continue;
841 std::string Comment
= RawLex
.getSpelling(Tok
, SM
, LangOpts
);
842 if (Comment
.empty()) continue;
844 // We don't care about tracking markers for this phase.
845 VerifyDiagnosticConsumer::MarkerTracker
Markers(SM
.getDiagnostics());
847 // Find first directive.
848 if (ParseDirective(Comment
, nullptr, SM
, nullptr, Tok
.getLocation(),
856 /// Takes a list of diagnostics that have been generated but not matched
857 /// by an expected-* directive and produces a diagnostic to the user from this.
858 static unsigned PrintUnexpected(DiagnosticsEngine
&Diags
, SourceManager
*SourceMgr
,
859 const_diag_iterator diag_begin
,
860 const_diag_iterator diag_end
,
862 if (diag_begin
== diag_end
) return 0;
864 SmallString
<256> Fmt
;
865 llvm::raw_svector_ostream
OS(Fmt
);
866 for (const_diag_iterator I
= diag_begin
, E
= diag_end
; I
!= E
; ++I
) {
867 if (I
->first
.isInvalid() || !SourceMgr
)
868 OS
<< "\n (frontend)";
871 if (const FileEntry
*File
= SourceMgr
->getFileEntryForID(
872 SourceMgr
->getFileID(I
->first
)))
873 OS
<< " File " << File
->getName();
874 OS
<< " Line " << SourceMgr
->getPresumedLineNumber(I
->first
);
876 OS
<< ": " << I
->second
;
879 Diags
.Report(diag::err_verify_inconsistent_diags
).setForceEmit()
880 << Kind
<< /*Unexpected=*/true << OS
.str();
881 return std::distance(diag_begin
, diag_end
);
884 /// Takes a list of diagnostics that were expected to have been generated
885 /// but were not and produces a diagnostic to the user from this.
886 static unsigned PrintExpected(DiagnosticsEngine
&Diags
,
887 SourceManager
&SourceMgr
,
888 std::vector
<Directive
*> &DL
, const char *Kind
) {
892 SmallString
<256> Fmt
;
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
);
902 OS
<< " Line " << SourceMgr
.getPresumedLineNumber(D
->DiagnosticLoc
);
903 if (D
->DirectiveLoc
!= D
->DiagnosticLoc
)
904 OS
<< " (directive at "
905 << SourceMgr
.getFilename(D
->DirectiveLoc
) << ':'
906 << SourceMgr
.getPresumedLineNumber(D
->DirectiveLoc
) << ')';
907 OS
<< ": " << D
->Text
;
910 Diags
.Report(diag::err_verify_inconsistent_diags
).setForceEmit()
911 << Kind
<< /*Unexpected=*/false << OS
.str();
915 /// Determine whether two source locations come from the same file.
916 static bool IsFromSameFile(SourceManager
&SM
, SourceLocation DirectiveLoc
,
917 SourceLocation DiagnosticLoc
) {
918 while (DiagnosticLoc
.isMacroID())
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
)));
931 /// CheckLists - Compare expected to seen diagnostic lists and return the
932 /// the difference between them.
933 static unsigned CheckLists(DiagnosticsEngine
&Diags
, SourceManager
&SourceMgr
,
936 const_diag_iterator d2_begin
,
937 const_diag_iterator d2_end
,
938 bool IgnoreUnexpected
) {
939 std::vector
<Directive
*> LeftOnly
;
940 DiagList
Right(d2_begin
, d2_end
);
942 for (auto &Owner
: Left
) {
943 Directive
&D
= *Owner
;
944 unsigned LineNo1
= SourceMgr
.getPresumedLineNumber(D
.DiagnosticLoc
);
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
) {
949 if (!D
.MatchAnyLine
) {
950 unsigned LineNo2
= SourceMgr
.getPresumedLineNumber(II
->first
);
951 if (LineNo1
!= LineNo2
)
955 if (!D
.DiagnosticLoc
.isInvalid() && !D
.MatchAnyFileAndLine
&&
956 !IsFromSameFile(SourceMgr
, D
.DiagnosticLoc
, II
->first
))
959 const std::string
&RightText
= II
->second
;
960 if (D
.match(RightText
))
965 if (i
>= D
.Min
) break;
966 LeftOnly
.push_back(&D
);
968 // Found. The same cannot be found twice.
973 // Now all that's left in Right are those that were not matched.
974 unsigned num
= PrintExpected(Diags
, SourceMgr
, LeftOnly
, Label
);
975 if (!IgnoreUnexpected
)
976 num
+= PrintUnexpected(Diags
, &SourceMgr
, Right
.begin(), Right
.end(), Label
);
980 /// CheckResults - This compares the expected results to those that
981 /// were actually reported. It emits any discrepencies. Return "true" if there
982 /// were problems. Return "false" otherwise.
983 static unsigned CheckResults(DiagnosticsEngine
&Diags
, SourceManager
&SourceMgr
,
984 const TextDiagnosticBuffer
&Buffer
,
986 // We want to capture the delta between what was expected and what was
989 // Expected \ Seen - set expected but not seen
990 // Seen \ Expected - set seen but not expected
991 unsigned NumProblems
= 0;
993 const DiagnosticLevelMask DiagMask
=
994 Diags
.getDiagnosticOptions().getVerifyIgnoreUnexpected();
996 // See if there are error mismatches.
997 NumProblems
+= CheckLists(Diags
, SourceMgr
, "error", ED
.Errors
,
998 Buffer
.err_begin(), Buffer
.err_end(),
999 bool(DiagnosticLevelMask::Error
& DiagMask
));
1001 // See if there are warning mismatches.
1002 NumProblems
+= CheckLists(Diags
, SourceMgr
, "warning", ED
.Warnings
,
1003 Buffer
.warn_begin(), Buffer
.warn_end(),
1004 bool(DiagnosticLevelMask::Warning
& DiagMask
));
1006 // See if there are remark mismatches.
1007 NumProblems
+= CheckLists(Diags
, SourceMgr
, "remark", ED
.Remarks
,
1008 Buffer
.remark_begin(), Buffer
.remark_end(),
1009 bool(DiagnosticLevelMask::Remark
& DiagMask
));
1011 // See if there are note mismatches.
1012 NumProblems
+= CheckLists(Diags
, SourceMgr
, "note", ED
.Notes
,
1013 Buffer
.note_begin(), Buffer
.note_end(),
1014 bool(DiagnosticLevelMask::Note
& DiagMask
));
1019 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager
&SM
,
1022 // Check SourceManager hasn't changed.
1023 setSourceManager(SM
);
1026 if (FID
.isInvalid())
1029 const FileEntry
*FE
= SM
.getFileEntryForID(FID
);
1031 if (PS
== IsParsed
) {
1032 // Move the FileID from the unparsed set to the parsed set.
1033 UnparsedFiles
.erase(FID
);
1034 ParsedFiles
.insert(std::make_pair(FID
, FE
));
1035 } else if (!ParsedFiles
.count(FID
) && !UnparsedFiles
.count(FID
)) {
1036 // Add the FileID to the unparsed set if we haven't seen it before.
1038 // Check for directives.
1039 bool FoundDirectives
;
1040 if (PS
== IsUnparsedNoDirectives
)
1041 FoundDirectives
= false;
1043 FoundDirectives
= !LangOpts
|| findDirectives(SM
, FID
, *LangOpts
);
1045 // Add the FileID to the unparsed set.
1046 UnparsedFiles
.insert(std::make_pair(FID
,
1047 UnparsedFileStatus(FE
, FoundDirectives
)));
1052 void VerifyDiagnosticConsumer::CheckDiagnostics() {
1053 // Ensure any diagnostics go to the primary client.
1054 DiagnosticConsumer
*CurClient
= Diags
.getClient();
1055 std::unique_ptr
<DiagnosticConsumer
> Owner
= Diags
.takeClient();
1056 Diags
.setClient(PrimaryClient
, false);
1059 // In a debug build, scan through any files that may have been missed
1060 // during parsing and issue a fatal error if directives are contained
1061 // within these files. If a fatal error occurs, this suggests that
1062 // this file is being parsed separately from the main file, in which
1063 // case consider moving the directives to the correct place, if this
1065 if (!UnparsedFiles
.empty()) {
1066 // Generate a cache of parsed FileEntry pointers for alias lookups.
1067 llvm::SmallPtrSet
<const FileEntry
*, 8> ParsedFileCache
;
1068 for (const auto &I
: ParsedFiles
)
1069 if (const FileEntry
*FE
= I
.second
)
1070 ParsedFileCache
.insert(FE
);
1072 // Iterate through list of unparsed files.
1073 for (const auto &I
: UnparsedFiles
) {
1074 const UnparsedFileStatus
&Status
= I
.second
;
1075 const FileEntry
*FE
= Status
.getFile();
1077 // Skip files that have been parsed via an alias.
1078 if (FE
&& ParsedFileCache
.count(FE
))
1081 // Report a fatal error if this file contained directives.
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)")));
1089 // UnparsedFiles has been processed now, so clear it.
1090 UnparsedFiles
.clear();
1095 // Produce an error if no expected-* directives could be found in the
1096 // source file(s) processed.
1097 if (Status
== HasNoDirectives
) {
1098 Diags
.Report(diag::err_verify_no_directives
).setForceEmit();
1100 Status
= HasNoDirectivesReported
;
1103 // Check that the expected diagnostics occurred.
1104 NumErrors
+= CheckResults(Diags
, *SrcManager
, *Buffer
, ED
);
1106 const DiagnosticLevelMask DiagMask
=
1107 ~Diags
.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1108 if (bool(DiagnosticLevelMask::Error
& DiagMask
))
1109 NumErrors
+= PrintUnexpected(Diags
, nullptr, Buffer
->err_begin(),
1110 Buffer
->err_end(), "error");
1111 if (bool(DiagnosticLevelMask::Warning
& DiagMask
))
1112 NumErrors
+= PrintUnexpected(Diags
, nullptr, Buffer
->warn_begin(),
1113 Buffer
->warn_end(), "warn");
1114 if (bool(DiagnosticLevelMask::Remark
& DiagMask
))
1115 NumErrors
+= PrintUnexpected(Diags
, nullptr, Buffer
->remark_begin(),
1116 Buffer
->remark_end(), "remark");
1117 if (bool(DiagnosticLevelMask::Note
& DiagMask
))
1118 NumErrors
+= PrintUnexpected(Diags
, nullptr, Buffer
->note_begin(),
1119 Buffer
->note_end(), "note");
1122 Diags
.setClient(CurClient
, Owner
.release() != nullptr);
1124 // Reset the buffer, we have processed all the diagnostics in it.
1125 Buffer
.reset(new TextDiagnosticBuffer());
1129 std::unique_ptr
<Directive
> Directive::create(bool RegexKind
,
1130 SourceLocation DirectiveLoc
,
1131 SourceLocation DiagnosticLoc
,
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
);
1140 // Parse the directive into a regular expression.
1141 std::string RegexStr
;
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
);
1148 // Append the regex, enclosed in parentheses.
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();
1157 // Escape and append the fixed string.
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
);