clang 23.0.0git
VerifyDiagnosticConsumer.cpp
Go to the documentation of this file.
1//===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This is a concrete diagnostic client, which buffers the diagnostic messages.
10//
11//===----------------------------------------------------------------------===//
12
18#include "clang/Basic/LLVM.h"
24#include "clang/Lex/Lexer.h"
27#include "clang/Lex/Token.h"
28#include "llvm/ADT/STLExtras.h"
29#include "llvm/ADT/SmallPtrSet.h"
30#include "llvm/ADT/SmallString.h"
31#include "llvm/ADT/StringRef.h"
32#include "llvm/ADT/Twine.h"
33#include "llvm/Support/ErrorHandling.h"
34#include "llvm/Support/Regex.h"
35#include "llvm/Support/raw_ostream.h"
36#include <algorithm>
37#include <cassert>
38#include <cstddef>
39#include <cstring>
40#include <iterator>
41#include <memory>
42#include <string>
43#include <utility>
44#include <vector>
45
46using namespace clang;
47
51
52#ifndef NDEBUG
53
54namespace {
55
56class VerifyFileTracker : public PPCallbacks {
59
60public:
61 VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
62 : Verify(Verify), SM(SM) {}
63
64 /// Hook into the preprocessor and update the list of parsed
65 /// files when the preprocessor indicates a new file is entered.
66 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
68 FileID PrevFID) override {
69 Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
71 }
72};
73
74} // namespace
75
76#endif
77
78//===----------------------------------------------------------------------===//
79// Checking diagnostics implementation.
80//===----------------------------------------------------------------------===//
81
84
85namespace {
86
87/// StandardDirective - Directive with string matching.
88class StandardDirective : public Directive {
89public:
90 StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
91 StringRef Spelling, bool MatchAnyFileAndLine,
92 bool MatchAnyLine, StringRef Text, unsigned Min,
93 unsigned Max, bool FullMatchRequired)
94 : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
95 MatchAnyLine, Text, Min, Max, FullMatchRequired) {}
96
97 bool isValid(std::string &Error) override {
98 // all strings are considered valid; even empty ones
99 return true;
100 }
101
102 DiagnosticMatchResult match(StringRef S) const override {
103 if (!S.contains(Text)) {
105 }
106 if (!FullMatchRequired) {
108 }
109 return S.trim() == Text ? DiagnosticMatchResult::Full
111 }
112};
113
114/// RegexDirective - Directive with regular-expression matching.
115class RegexDirective : public Directive {
116public:
117 RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
118 StringRef Spelling, bool MatchAnyFileAndLine,
119 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
120 StringRef RegexStr, bool FullMatchRequired)
121 : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
122 MatchAnyLine, Text, Min, Max, FullMatchRequired),
123 Regex(RegexStr) {}
124
125 bool isValid(std::string &Error) override {
126 return Regex.isValid(Error);
127 }
128
129 DiagnosticMatchResult match(StringRef S) const override {
130 if (!FullMatchRequired) {
131 return Regex.match(S) ? DiagnosticMatchResult::AtLeastPartial
133 }
134
136 llvm::StringRef TrimmedText = S.trim();
137 Regex.match(TrimmedText, &Matches);
138 if (Matches.empty()) {
140 }
141 return Matches[0].size() == TrimmedText.size()
144 }
145
146private:
147 llvm::Regex Regex;
148};
149
150class ParseHelper
151{
152public:
153 ParseHelper(StringRef S)
154 : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
155
156 // Return true if string literal is next.
157 bool Next(StringRef S) {
158 P = C;
159 PEnd = C + S.size();
160 if (PEnd > End)
161 return false;
162 return memcmp(P, S.data(), S.size()) == 0;
163 }
164
165 // Return true if number is next.
166 // Output N only if number is next.
167 bool Next(unsigned &N) {
168 unsigned TMP = 0;
169 P = C;
170 PEnd = P;
171 for (; PEnd < End && *PEnd >= '0' && *PEnd <= '9'; ++PEnd) {
172 TMP *= 10;
173 TMP += *PEnd - '0';
174 }
175 if (PEnd == C)
176 return false;
177 N = TMP;
178 return true;
179 }
180
181 // Return true if a marker is next.
182 // A marker is the longest match for /#[A-Za-z0-9_-]+/.
183 bool NextMarker() {
184 P = C;
185 if (P == End || *P != '#')
186 return false;
187 PEnd = P;
188 ++PEnd;
189 while ((isAlphanumeric(*PEnd) || *PEnd == '-' || *PEnd == '_') &&
190 PEnd < End)
191 ++PEnd;
192 return PEnd > P + 1;
193 }
194
195 // Return true if string literal S is matched in content.
196 // When true, P marks begin-position of the match, and calling Advance sets C
197 // to end-position of the match.
198 // If S is the empty string, then search for any letter instead (makes sense
199 // with FinishDirectiveToken=true).
200 // If EnsureStartOfWord, then skip matches that don't start a new word.
201 // If FinishDirectiveToken, then assume the match is the start of a comment
202 // directive for -verify, and extend the match to include the entire first
203 // token of that directive.
204 bool Search(StringRef S, bool EnsureStartOfWord = false,
205 bool FinishDirectiveToken = false) {
206 do {
207 if (!S.empty()) {
208 P = std::search(C, End, S.begin(), S.end());
209 PEnd = P + S.size();
210 }
211 else {
212 P = C;
213 while (P != End && !isLetter(*P))
214 ++P;
215 PEnd = P + 1;
216 }
217 if (P == End)
218 break;
219 // If not start of word but required, skip and search again.
220 if (EnsureStartOfWord
221 // Check if string literal starts a new word.
222 && !(P == Begin || isWhitespace(P[-1])
223 // Or it could be preceded by the start of a comment.
224 || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
225 && P[-2] == '/')))
226 continue;
227 if (FinishDirectiveToken) {
228 while (PEnd != End && (isAlphanumeric(*PEnd)
229 || *PEnd == '-' || *PEnd == '_'))
230 ++PEnd;
231 // Put back trailing digits and hyphens to be parsed later as a count
232 // or count range. Because -verify prefixes must start with letters,
233 // we know the actual directive we found starts with a letter, so
234 // we won't put back the entire directive word and thus record an empty
235 // string.
236 assert(isLetter(*P) && "-verify prefix must start with a letter");
237 while (isDigit(PEnd[-1]) || PEnd[-1] == '-')
238 --PEnd;
239 }
240 return true;
241 } while (Advance());
242 return false;
243 }
244
245 // Return true if a CloseBrace that closes the OpenBrace at the current nest
246 // level is found. When true, P marks begin-position of CloseBrace.
247 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
248 unsigned Depth = 1;
249 P = C;
250 while (P < End) {
251 StringRef S(P, End - P);
252 if (S.starts_with(OpenBrace)) {
253 ++Depth;
254 P += OpenBrace.size();
255 } else if (S.starts_with(CloseBrace)) {
256 --Depth;
257 if (Depth == 0) {
258 PEnd = P + CloseBrace.size();
259 return true;
260 }
261 P += CloseBrace.size();
262 } else {
263 ++P;
264 }
265 }
266 return false;
267 }
268
269 // Advance 1-past previous next/search.
270 // Behavior is undefined if previous next/search failed.
271 bool Advance() {
272 C = PEnd;
273 return C < End;
274 }
275
276 // Return the text matched by the previous next/search.
277 // Behavior is undefined if previous next/search failed.
278 StringRef Match() { return StringRef(P, PEnd - P); }
279
280 // Skip zero or more whitespace.
281 void SkipWhitespace() {
282 for (; C < End && isWhitespace(*C); ++C)
283 ;
284 }
285
286 // Return true if EOF reached.
287 bool Done() {
288 return !(C < End);
289 }
290
291 // Beginning of expected content.
292 const char * const Begin;
293
294 // End of expected content (1-past).
295 const char * const End;
296
297 // Position of next char in content.
298 const char *C;
299
300 // Previous next/search subject start.
301 const char *P;
302
303private:
304 // Previous next/search subject end (1-past).
305 const char *PEnd = nullptr;
306};
307
308// The information necessary to create a directive.
309struct UnattachedDirective {
310 DirectiveList *DL = nullptr;
311 std::string Spelling;
312 bool RegexKind = false;
313 SourceLocation DirectivePos, ContentBegin;
314 std::string Text;
315 unsigned Min = 1, Max = 1;
316};
317
318// Attach the specified directive to the line of code indicated by
319// \p ExpectedLoc.
320void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
321 SourceLocation ExpectedLoc,
322 bool MatchAnyFileAndLine = false,
323 bool MatchAnyLine = false) {
324 // Construct new directive.
325 std::unique_ptr<Directive> D =
326 Directive::create(UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
327 MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min,
328 UD.Max, Diags.getDiagnosticOptions().VerifyDirectives);
329
330 std::string Error;
331 if (!D->isValid(Error)) {
332 Diags.Report(UD.ContentBegin, diag::err_verify_invalid_content)
333 << (UD.RegexKind ? "regex" : "string") << Error;
334 }
335
336 UD.DL->push_back(std::move(D));
337}
338
339} // anonymous
340
341// Tracker for markers in the input files. A marker is a comment of the form
342//
343// n = 123; // #123
344//
345// ... that can be referred to by a later expected-* directive:
346//
347// // expected-error@#123 {{undeclared identifier 'n'}}
348//
349// Marker declarations must be at the start of a comment or preceded by
350// whitespace to distinguish them from uses of markers in directives.
352 DiagnosticsEngine &Diags;
353
354 struct Marker {
355 SourceLocation DefLoc;
356 SourceLocation RedefLoc;
357 SourceLocation UseLoc;
358 };
359 llvm::StringMap<Marker> Markers;
360
361 // Directives that couldn't be created yet because they name an unknown
362 // marker.
363 llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives;
364
365public:
366 MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {}
367
368 // Register a marker.
369 void addMarker(StringRef MarkerName, SourceLocation Pos) {
370 auto InsertResult = Markers.insert(
371 {MarkerName, Marker{Pos, SourceLocation(), SourceLocation()}});
372
373 Marker &M = InsertResult.first->second;
374 if (!InsertResult.second) {
375 // Marker was redefined.
376 M.RedefLoc = Pos;
377 } else {
378 // First definition: build any deferred directives.
379 auto Deferred = DeferredDirectives.find(MarkerName);
380 if (Deferred != DeferredDirectives.end()) {
381 for (auto &UD : Deferred->second) {
382 if (M.UseLoc.isInvalid())
383 M.UseLoc = UD.DirectivePos;
384 attachDirective(Diags, UD, Pos);
385 }
386 DeferredDirectives.erase(Deferred);
387 }
388 }
389 }
390
391 // Register a directive at the specified marker.
392 void addDirective(StringRef MarkerName, const UnattachedDirective &UD) {
393 auto MarkerIt = Markers.find(MarkerName);
394 if (MarkerIt != Markers.end()) {
395 Marker &M = MarkerIt->second;
396 if (M.UseLoc.isInvalid())
397 M.UseLoc = UD.DirectivePos;
398 return attachDirective(Diags, UD, M.DefLoc);
399 }
400 DeferredDirectives[MarkerName].push_back(UD);
401 }
402
403 // Ensure we have no remaining deferred directives, and no
404 // multiply-defined-and-used markers.
405 void finalize() {
406 for (auto &MarkerInfo : Markers) {
407 StringRef Name = MarkerInfo.first();
408 Marker &M = MarkerInfo.second;
409 if (M.RedefLoc.isValid() && M.UseLoc.isValid()) {
410 Diags.Report(M.UseLoc, diag::err_verify_ambiguous_marker) << Name;
411 Diags.Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name;
412 Diags.Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name;
413 }
414 }
415
416 for (auto &DeferredPair : DeferredDirectives) {
417 Diags.Report(DeferredPair.second.front().DirectivePos,
418 diag::err_verify_no_such_marker)
419 << DeferredPair.first();
420 }
421 }
422};
423
424static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
425 if (Diags.getDiagnosticOptions().VerifyPrefixes.empty())
426 return "expected";
427 return *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
428}
429
430/// ParseDirective - Go through the comment and see if it indicates expected
431/// diagnostics. If so, then put them in the appropriate directive list.
432///
433/// Returns true if any valid directives were found.
434static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
438 bool OneDiagPerDirective,
439 bool DisableWildcardInDiagLoc) {
440 DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
441
442 // First, scan the comment looking for markers.
443 for (ParseHelper PH(S); !PH.Done();) {
444 if (!PH.Search("#", true))
445 break;
446 PH.C = PH.P;
447 if (!PH.NextMarker()) {
448 PH.Next("#");
449 PH.Advance();
450 continue;
451 }
452 PH.Advance();
453 Markers.addMarker(PH.Match(), Pos);
454 }
455
456 // A single comment may contain multiple directives.
457 bool FoundDirective = false;
458 for (ParseHelper PH(S); !PH.Done();) {
459 // Search for the initial directive token.
460 // If one prefix, save time by searching only for its directives.
461 // Otherwise, search for any potential directive token and check it later.
462 const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes;
463 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true)
464 : PH.Search("", true, true)))
465 break;
466
467 StringRef DToken = PH.Match();
468 PH.Advance();
469
470 UnattachedDirective D;
471 D.Spelling = DToken;
472 // Default directive kind.
473 const char *KindStr = "string";
474
475 // Parse the initial directive token in reverse so we can easily determine
476 // its exact actual prefix. If we were to parse it from the front instead,
477 // it would be harder to determine where the prefix ends because there
478 // might be multiple matching -verify prefixes because some might prefix
479 // others.
480
481 // Regex in initial directive token: -re
482 if (DToken.consume_back("-re")) {
483 D.RegexKind = true;
484 KindStr = "regex";
485 }
486
487 // Type in initial directive token: -{error|warning|note|no-diagnostics}
488 bool NoDiag = false;
489 StringRef DType;
490 if (DToken.ends_with(DType = "-error"))
491 D.DL = ED ? &ED->Errors : nullptr;
492 else if (DToken.ends_with(DType = "-warning"))
493 D.DL = ED ? &ED->Warnings : nullptr;
494 else if (DToken.ends_with(DType = "-remark"))
495 D.DL = ED ? &ED->Remarks : nullptr;
496 else if (DToken.ends_with(DType = "-note"))
497 D.DL = ED ? &ED->Notes : nullptr;
498 else if (DToken.ends_with(DType = "-no-diagnostics")) {
499 NoDiag = true;
500 if (D.RegexKind)
501 continue;
502 } else
503 continue;
504 DToken = DToken.substr(0, DToken.size()-DType.size());
505
506 // What's left in DToken is the actual prefix. That might not be a -verify
507 // prefix even if there is only one -verify prefix (for example, the full
508 // DToken is foo-bar-warning, but foo is the only -verify prefix).
509 if (!llvm::binary_search(Prefixes, DToken))
510 continue;
511
512 if (NoDiag) {
513 if (State.Status ==
515 Diags.Report(Pos, diag::err_verify_invalid_no_diags)
516 << D.Spelling << /*IsExpectedNoDiagnostics=*/true;
517 } else if (State.Status !=
520 State.FirstNoDiagnosticsDirective = D.Spelling;
521 }
522 continue;
523 }
525 Diags.Report(Pos, diag::err_verify_invalid_no_diags)
526 << D.Spelling << /*IsExpectedNoDiagnostics=*/false
527 << State.FirstNoDiagnosticsDirective;
528 continue;
529 }
531
532 // If a directive has been found but we're not interested
533 // in storing the directive information, return now.
534 if (!D.DL)
535 return true;
536
537 // Next optional token: @
538 SourceLocation ExpectedLoc;
539 StringRef Marker;
540 bool MatchAnyFileAndLine = false;
541 bool MatchAnyLine = false;
542 if (!PH.Next("@")) {
543 ExpectedLoc = Pos;
544
545 // If an implicit directive is found in an incremental input buffer, allow
546 // it to match any other incremental input buffer
548 StringRef CurrentBufferName =
549 SM.getBufferOrFake(SM.getFileID(Pos)).getBufferIdentifier();
550 if (CurrentBufferName.starts_with("input_line_"))
551 MatchAnyFileAndLine = true;
552 }
553 } else {
554 PH.Advance();
555 unsigned Line = 0;
556 bool FoundPlus = PH.Next("+");
557 if (FoundPlus || PH.Next("-")) {
558 // Relative to current line.
559 PH.Advance();
560 bool Invalid = false;
561 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
562 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
563 if (FoundPlus) ExpectedLine += Line;
564 else ExpectedLine -= Line;
565 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
566 }
567 } else if (PH.Next(Line)) {
568 // Absolute line number.
569 if (Line > 0)
570 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
571 } else if (PH.NextMarker()) {
572 Marker = PH.Match();
573 } else if (PP && PH.Search(":")) {
574 // Specific source file.
575 StringRef Filename(PH.C, PH.P-PH.C);
576 PH.Advance();
577
578 if (Filename == "*") {
579 if (DisableWildcardInDiagLoc) {
580 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
581 diag::err_verify_wildcard_loc);
582 State.WildcardsAreErroneouslyPresent = true;
583 }
584 MatchAnyFileAndLine = true;
585 if (!PH.Next("*")) {
586 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
587 diag::err_verify_missing_line)
588 << "'*'";
589 continue;
590 }
591 MatchAnyLine = true;
592 ExpectedLoc = SourceLocation();
593 } else {
596 Filename.starts_with("input_line_")) {
597 // Check if it came from the prompt
598 File = SM.getFileManager().getOptionalFileRef(Filename);
599 } else {
600 // Lookup file via Preprocessor, like a #include.
601 File =
602 PP->LookupFile(Pos, Filename, false, nullptr, nullptr, nullptr,
603 nullptr, nullptr, nullptr, nullptr, nullptr);
604 }
605 if (!File) {
606 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
607 diag::err_verify_missing_file)
608 << Filename << KindStr;
609 continue;
610 }
611
612 FileID FID = SM.translateFile(*File);
613 if (FID.isInvalid())
614 FID = SM.createFileID(*File, Pos, SrcMgr::C_User);
615
616 if (PH.Next(Line) && Line > 0)
617 ExpectedLoc = SM.translateLineCol(FID, Line, 1);
618 else if (PH.Next("*")) {
619 if (DisableWildcardInDiagLoc) {
620 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
621 diag::err_verify_wildcard_loc);
622 State.WildcardsAreErroneouslyPresent = true;
623 }
624 MatchAnyLine = true;
625 ExpectedLoc = SM.translateLineCol(FID, 1, 1);
626 }
627 }
628 } else if (PH.Next("*")) {
629 if (DisableWildcardInDiagLoc) {
630 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
631 diag::err_verify_wildcard_loc);
632 State.WildcardsAreErroneouslyPresent = true;
633 }
634 MatchAnyLine = true;
635 ExpectedLoc = SourceLocation();
636 }
637
638 if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) {
639 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
640 diag::err_verify_missing_line) << KindStr;
641 continue;
642 }
643 PH.Advance();
644 }
645
646 // Skip optional whitespace.
647 PH.SkipWhitespace();
648
649 std::optional<std::ptrdiff_t> NonSingularMatchDiagOffset;
650
651 // Next optional token: positive integer or a '+'.
652 if (PH.Next(D.Min)) {
653 if (OneDiagPerDirective && D.Min != 1) {
654 NonSingularMatchDiagOffset = PH.C - PH.Begin;
655 }
656 PH.Advance();
657 // A positive integer can be followed by a '+' meaning min
658 // or more, or by a '-' meaning a range from min to max.
659 if (PH.Next("+")) {
660 D.Max = Directive::MaxCount;
661 PH.Advance();
662 if (OneDiagPerDirective) {
663 NonSingularMatchDiagOffset = PH.C - PH.Begin;
664 }
665 } else if (PH.Next("-")) {
666 PH.Advance();
667 if (!PH.Next(D.Max) || D.Max < D.Min) {
668 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
669 diag::err_verify_invalid_range) << KindStr;
670 continue;
671 }
672 if (OneDiagPerDirective && D.Max != 1) {
673 NonSingularMatchDiagOffset = PH.C - PH.Begin;
674 }
675 PH.Advance();
676 } else {
677 D.Max = D.Min;
678 }
679 } else if (PH.Next("+")) {
680 // '+' on its own means "1 or more".
681 if (OneDiagPerDirective) {
682 NonSingularMatchDiagOffset = PH.C - PH.Begin;
683 }
684 D.Max = Directive::MaxCount;
685 PH.Advance();
686 }
687
688 if (NonSingularMatchDiagOffset) {
689 Diags.Report(Pos.getLocWithOffset(*NonSingularMatchDiagOffset),
690 diag::err_verify_non_singular_match);
691 State.AllDirectivesMatchExactlyOneDiag = false;
692 }
693
694 // Skip optional whitespace.
695 PH.SkipWhitespace();
696
697 // Next token: {{
698 if (!PH.Next("{{")) {
699 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
700 diag::err_verify_missing_start) << KindStr;
701 continue;
702 }
703 llvm::SmallString<8> CloseBrace("}}");
704 const char *const DelimBegin = PH.C;
705 PH.Advance();
706 // Count the number of opening braces for `string` kinds
707 for (; !D.RegexKind && PH.Next("{"); PH.Advance())
708 CloseBrace += '}';
709 const char* const ContentBegin = PH.C; // mark content begin
710 // Search for closing brace
711 StringRef OpenBrace(DelimBegin, ContentBegin - DelimBegin);
712 if (!PH.SearchClosingBrace(OpenBrace, CloseBrace)) {
713 Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
714 diag::err_verify_missing_end)
715 << KindStr << CloseBrace;
716 continue;
717 }
718 const char* const ContentEnd = PH.P; // mark content end
719 PH.Advance();
720
721 D.DirectivePos = Pos;
722 D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin);
723
724 // Build directive text; convert \n to newlines.
725 StringRef NewlineStr = "\\n";
726 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
727 size_t CPos = 0;
728 size_t FPos;
729 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
730 D.Text += Content.substr(CPos, FPos-CPos);
731 D.Text += '\n';
732 CPos = FPos + NewlineStr.size();
733 }
734 if (D.Text.empty())
735 D.Text.assign(ContentBegin, ContentEnd);
736
737 // Check that regex directives contain at least one regex.
738 if (D.RegexKind && D.Text.find("{{") == StringRef::npos) {
739 Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text;
740 return false;
741 }
742
743 if (Marker.empty())
744 attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine);
745 else
746 Markers.addDirective(Marker, D);
747 FoundDirective = true;
748 }
749
750 return FoundDirective;
751}
752
754 : Diags(Diags_), PrimaryClient(Diags.getClient()),
755 PrimaryClientOwner(Diags.takeClient()),
756 Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
757 State{HasNoDirectives, {}} {
758 if (Diags.hasSourceManager())
759 setSourceManager(Diags.getSourceManager());
760 CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDirectives;
761 OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDirectives;
762 DisableWildcardInDiagLoc = Diags.getDiagnosticOptions().VerifyDirectives;
763}
764
766 assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
767 assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
768 SrcManager = nullptr;
769 CheckDiagnostics();
770}
771
772// DiagnosticConsumer interface.
773
775 const Preprocessor *PP) {
776 // Attach comment handler on first invocation.
777 if (++ActiveSourceFiles == 1) {
778 if (PP) {
779 CurrentPreprocessor = PP;
780 this->LangOpts = &LangOpts;
781 setSourceManager(PP->getSourceManager());
782 const_cast<Preprocessor *>(PP)->addCommentHandler(this);
783#ifndef NDEBUG
784 // Debug build tracks parsed files.
785 const_cast<Preprocessor *>(PP)->addPPCallbacks(
786 std::make_unique<VerifyFileTracker>(*this, *SrcManager));
787#endif
788 }
789 }
790
791 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
792 PrimaryClient->BeginSourceFile(LangOpts, PP);
793}
794
796 assert(ActiveSourceFiles && "No active source files!");
797 PrimaryClient->EndSourceFile();
798
799 // Detach comment handler once last active source file completed.
800 if (--ActiveSourceFiles == 0) {
801 if (CurrentPreprocessor)
802 const_cast<Preprocessor *>(CurrentPreprocessor)->
803 removeCommentHandler(this);
804
805 // Diagnose any used-but-not-defined markers.
806 Markers->finalize();
807
808 // Check diagnostics once last file completed.
809 CheckDiagnostics();
810 CurrentPreprocessor = nullptr;
811 LangOpts = nullptr;
812 }
813}
814
816 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
817 if (Info.hasSourceManager()) {
818 // If this diagnostic is for a different source manager, ignore it.
819 if (SrcManager && &Info.getSourceManager() != SrcManager)
820 return;
821
822 setSourceManager(Info.getSourceManager());
823 }
824
825#ifndef NDEBUG
826 // Debug build tracks unparsed files for possible
827 // unparsed expected-* directives.
828 if (SrcManager) {
829 SourceLocation Loc = Info.getLocation();
830 if (Loc.isValid()) {
832
833 Loc = SrcManager->getExpansionLoc(Loc);
834 FileID FID = SrcManager->getFileID(Loc);
835
836 auto FE = SrcManager->getFileEntryRefForID(FID);
837 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
838 // If the file is a modules header file it shall not be parsed
839 // for expected-* directives.
840 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
841 if (HS.findModuleForHeader(*FE))
843 }
844
845 UpdateParsedFileStatus(*SrcManager, FID, PS);
846 }
847 }
848#endif
849
850 // Send the diagnostic to the buffer, we will check it once we reach the end
851 // of the source file (or are destructed).
852 Buffer->HandleDiagnostic(DiagLevel, Info);
853}
854
855/// HandleComment - Hook into the preprocessor and extract comments containing
856/// expected errors and warnings.
858 SourceRange Comment) {
860
861 // If this comment is for a different source manager, ignore it.
862 if (SrcManager && &SM != SrcManager)
863 return false;
864
865 SourceLocation CommentBegin = Comment.getBegin();
866
867 const char *CommentRaw = SM.getCharacterData(CommentBegin);
868 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
869
870 if (C.empty())
871 return false;
872
873 // Fold any "<EOL>" sequences
874 size_t loc = C.find('\\');
875 if (loc == StringRef::npos) {
876 ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers,
877 OneDiagPerDirective, DisableWildcardInDiagLoc);
878 return false;
879 }
880
881 std::string C2;
882 C2.reserve(C.size());
883
884 for (size_t last = 0;; loc = C.find('\\', last)) {
885 if (loc == StringRef::npos || loc == C.size()) {
886 C2 += C.substr(last);
887 break;
888 }
889 C2 += C.substr(last, loc-last);
890 last = loc + 1;
891
892 if (last < C.size() && (C[last] == '\n' || C[last] == '\r')) {
893 ++last;
894
895 // Escape \r\n or \n\r, but not \n\n.
896 if (last < C.size())
897 if (C[last] == '\n' || C[last] == '\r')
898 if (C[last] != C[last-1])
899 ++last;
900 } else {
901 // This was just a normal backslash.
902 C2 += '\\';
903 }
904 }
905
906 if (!C2.empty())
907 ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers,
908 OneDiagPerDirective, DisableWildcardInDiagLoc);
909 return false;
910}
911
912#ifndef NDEBUG
913/// Lex the specified source file to determine whether it contains
914/// any expected-* directives. As a Lexer is used rather than a full-blown
915/// Preprocessor, directives inside skipped #if blocks will still be found.
916///
917/// \return true if any directives were found.
919 const LangOptions &LangOpts) {
920 // Create a raw lexer to pull all the comments out of FID.
921 if (FID.isInvalid())
922 return false;
923
924 // Create a lexer to lex all the tokens of the main file in raw mode.
925 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
926 Lexer RawLex(FID, FromFile, SM, LangOpts);
927
928 // Return comments as tokens, this is how we find expected diagnostics.
929 RawLex.SetCommentRetentionState(true);
930
931 Token Tok;
932 Tok.setKind(tok::comment);
935 while (Tok.isNot(tok::eof)) {
936 RawLex.LexFromRawLexer(Tok);
937 if (!Tok.is(tok::comment)) continue;
938
939 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
940 if (Comment.empty()) continue;
941
942 // We don't care about tracking markers for this phase.
943 VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
944
945 // Find first directive.
946 if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
947 Markers, /*OneDiagPerDirective=*/false,
948 /*DisableWildcardInDiagLoc=*/false))
949 return true;
950 }
951 return false;
952}
953#endif // !NDEBUG
954
955/// Takes a list of diagnostics that have been generated but not matched
956/// by an expected-* directive and produces a diagnostic to the user from this.
957static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
958 const_diag_iterator diag_begin,
959 const_diag_iterator diag_end,
960 const char *Kind) {
961 if (diag_begin == diag_end) return 0;
962
964 llvm::raw_svector_ostream OS(Fmt);
965 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
966 if (I->first.isInvalid() || !SourceMgr)
967 OS << "\n (frontend)";
968 else {
969 OS << "\n ";
971 SourceMgr->getFileEntryRefForID(SourceMgr->getFileID(I->first)))
972 OS << " File " << File->getName();
973 OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
974 }
975 OS << ": " << I->second;
976 }
977
978 const bool IsSinglePrefix =
979 Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
980 std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
981 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
982 << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/true << OS.str();
983 return std::distance(diag_begin, diag_end);
984}
985
986/// Takes a list of diagnostics that were expected to have been generated
987/// but were not and produces a diagnostic to the user from this.
988static unsigned PrintExpected(DiagnosticsEngine &Diags,
989 SourceManager &SourceMgr,
990 std::vector<Directive *> &DL, const char *Kind) {
991 if (DL.empty())
992 return 0;
993
994 const bool IsSinglePrefix =
995 Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
996
998 llvm::raw_svector_ostream OS(Fmt);
999 for (const auto *D : DL) {
1000 if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
1001 OS << "\n File *";
1002 else
1003 OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc);
1004 if (D->MatchAnyLine)
1005 OS << " Line *";
1006 else
1007 OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
1008 if (D->DirectiveLoc != D->DiagnosticLoc)
1009 OS << " (directive at "
1010 << SourceMgr.getFilename(D->DirectiveLoc) << ':'
1011 << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
1012 if (!IsSinglePrefix)
1013 OS << " \'" << D->Spelling << '\'';
1014 OS << ": " << D->Text;
1015 }
1016
1017 std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
1018 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
1019 << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/false << OS.str();
1020 return DL.size();
1021}
1022
1023/// Determine whether two source locations come from the same file.
1025 SourceLocation DiagnosticLoc) {
1026 while (DiagnosticLoc.isMacroID())
1027 DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
1028
1029 if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
1030 return true;
1031
1032 const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
1033 if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
1034 return true;
1035
1036 return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
1037}
1038
1039/// Takes a list of diagnostics that were partially matched and prints them.
1040static unsigned
1042 llvm::SmallVector<std::pair<Directive *, std::string>> &DL,
1043 const char *Kind) {
1044 if (DL.empty())
1045 return 0;
1046
1047 SmallString<256> Fmt;
1048 llvm::raw_svector_ostream OS(Fmt);
1049 for (const auto &[D, DiagText] : DL) {
1050 OS << "\n '" << D->Spelling << "' at line "
1051 << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << " in "
1052 << SourceMgr.getFilename(D->DiagnosticLoc) << ": " << D->Text
1053 << "\n does not fully match diagnostic at line "
1054 << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
1055 if (!IsFromSameFile(SourceMgr, D->DirectiveLoc, D->DiagnosticLoc)) {
1056 OS << " in " << SourceMgr.getFilename(D->DiagnosticLoc);
1057 }
1058 OS << ": " << DiagText;
1059 }
1060
1061 Diags.Report(diag::err_verify_message_partial_match).setForceEmit()
1062 << Kind << OS.str();
1063 return DL.size();
1064}
1065
1066/// CheckLists - Compare expected to seen diagnostic lists and return the
1067/// the difference between them.
1068static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
1069 const char *Label,
1070 DirectiveList &Left,
1071 const_diag_iterator d2_begin,
1072 const_diag_iterator d2_end,
1073 bool IgnoreUnexpected) {
1074 std::vector<Directive *> LeftOnly;
1075 DiagList Right(d2_begin, d2_end);
1077
1078 for (auto &Owner : Left) {
1079 Directive &D = *Owner;
1080 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
1081
1082 for (unsigned i = 0; i < D.Max; ++i) {
1083 DiagList::iterator II, IE;
1084 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
1085 if (!D.MatchAnyLine) {
1086 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
1087 if (LineNo1 != LineNo2)
1088 continue;
1089 }
1090
1092 !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
1093 continue;
1094
1095 const std::string &RightText = II->second;
1096 DiagnosticMatchResult MatchResult = D.match(RightText);
1097 if (MatchResult == DiagnosticMatchResult::OnlyPartial) {
1098 assert(D.FullMatchRequired && "Partial match was checked for full "
1099 "match when it is not needed!");
1100 IncompleteMatches.push_back({&D, II->second});
1101 }
1102 if (MatchResult != DiagnosticMatchResult::None) {
1103 break;
1104 }
1105 }
1106 if (II == IE) {
1107 // Not found.
1108 if (i >= D.Min) break;
1109 LeftOnly.push_back(&D);
1110 } else {
1111 // Found. The same cannot be found twice.
1112 Right.erase(II);
1113 }
1114 }
1115 }
1116 // Now all that's left in Right are those that were not matched.
1117 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
1118 if (!IgnoreUnexpected)
1119 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
1120 num += PrintPartial(Diags, SourceMgr, IncompleteMatches, Label);
1121 return num;
1122}
1123
1124/// CheckResults - This compares the expected results to those that
1125/// were actually reported. It emits any discrepencies. Return "true" if there
1126/// were problems. Return "false" otherwise.
1127static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
1128 const TextDiagnosticBuffer &Buffer,
1129 ExpectedData &ED) {
1130 // We want to capture the delta between what was expected and what was
1131 // seen.
1132 //
1133 // Expected \ Seen - set expected but not seen
1134 // Seen \ Expected - set seen but not expected
1135 unsigned NumProblems = 0;
1136
1137 const DiagnosticLevelMask DiagMask =
1138 Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1139
1140 // See if there are error mismatches.
1141 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
1142 Buffer.err_begin(), Buffer.err_end(),
1143 bool(DiagnosticLevelMask::Error & DiagMask));
1144
1145 // See if there are warning mismatches.
1146 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
1147 Buffer.warn_begin(), Buffer.warn_end(),
1148 bool(DiagnosticLevelMask::Warning & DiagMask));
1149
1150 // See if there are remark mismatches.
1151 NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
1152 Buffer.remark_begin(), Buffer.remark_end(),
1153 bool(DiagnosticLevelMask::Remark & DiagMask));
1154
1155 // See if there are note mismatches.
1156 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
1157 Buffer.note_begin(), Buffer.note_end(),
1158 bool(DiagnosticLevelMask::Note & DiagMask));
1159
1160 return NumProblems;
1161}
1162
1163// Checks that directives are lexically in the same order as the emitted
1164// diagnostics. Assumes that:
1165// - every directive matches exactly one diagnostic,
1166// - there are no wildcards, and
1167// - CheckResults returned 0 problems, i.e. every diagnostic
1168// was matched by every directive without considering the order.
1170 SourceManager &SourceMgr,
1171 const TextDiagnosticBuffer &Buffer,
1172 const ExpectedData &ED) {
1173 // Building a set of all directives ordered by their location.
1174 auto directiveComparator = [](const Directive *LHS, const Directive *RHS) {
1175 return LHS->DirectiveLoc < RHS->DirectiveLoc;
1176 };
1177 auto sortDirectives = [&](const DirectiveList &Unordered) {
1178 std::vector<const Directive *> Ordered(Unordered.size());
1179 std::transform(Unordered.cbegin(), Unordered.cend(), Ordered.begin(),
1180 [](const std::unique_ptr<Directive> &D) { return &*D; });
1181 std::sort(Ordered.begin(), Ordered.end(), directiveComparator);
1182 return Ordered;
1183 };
1184 std::vector<const Directive *> OrderedErrors = sortDirectives(ED.Errors);
1185 std::vector<const Directive *> OrderedWarns = sortDirectives(ED.Warnings);
1186 std::vector<const Directive *> OrderedNotes = sortDirectives(ED.Notes);
1187 std::vector<const Directive *> OrderedRemarks = sortDirectives(ED.Remarks);
1188
1189 std::vector<const Directive *> OrderedDirectives = [&] {
1190 std::vector<const Directive *> OrderedEW(OrderedErrors.size() +
1191 OrderedWarns.size());
1192 std::merge(OrderedErrors.cbegin(), OrderedErrors.cend(),
1193 OrderedWarns.cbegin(), OrderedWarns.cend(), OrderedEW.begin(),
1194 directiveComparator);
1195
1196 std::vector<const Directive *> OrderedNR(OrderedNotes.size() +
1197 OrderedRemarks.size());
1198 std::merge(OrderedNotes.cbegin(), OrderedNotes.cend(),
1199 OrderedRemarks.cbegin(), OrderedRemarks.cend(),
1200 OrderedNR.begin(), directiveComparator);
1201
1202 std::vector<const Directive *> OrderedDirectives(OrderedEW.size() +
1203 OrderedNR.size());
1204 std::merge(OrderedEW.cbegin(), OrderedEW.cend(), OrderedNR.cbegin(),
1205 OrderedNR.cend(), OrderedDirectives.begin(),
1206 directiveComparator);
1207 return OrderedDirectives;
1208 }();
1209
1210 auto getLocDiagPair = [&](DiagnosticsEngine::Level DiagLevel, long DiagIndex)
1211 -> const std::pair<clang::SourceLocation, std::basic_string<char>> & {
1213 switch (DiagLevel) {
1216 assert(DiagIndex < Buffer.err_end() - Buffer.err_begin() &&
1217 "DiagIndex is out of bounds!");
1218 return Buffer.err_begin();
1220 assert(DiagIndex < Buffer.warn_end() - Buffer.warn_begin() &&
1221 "DiagIndex is out of bounds!");
1222 return Buffer.warn_begin();
1224 assert(DiagIndex < Buffer.note_end() - Buffer.note_begin() &&
1225 "DiagIndex is out of bounds!");
1226 return Buffer.note_begin();
1228 assert(DiagIndex < Buffer.remark_end() - Buffer.remark_begin() &&
1229 "DiagIndex is out of bounds!");
1230 return Buffer.remark_begin();
1232 llvm_unreachable("Unexpected diagnostic level!");
1233 }
1234 llvm_unreachable("Unknown DiagnosticsEngine::Level enum");
1235 }();
1236
1237 std::advance(It, DiagIndex);
1238 return *It;
1239 };
1240
1241 using LevelDiagPairT = std::pair<DiagnosticsEngine::Level, size_t>;
1242 static_assert(std::is_same_v<LevelDiagPairT,
1243 TextDiagnosticBuffer::AllDiagList::value_type>);
1244 int NumProblems = 0;
1245 SmallString<256> Fmt;
1246 llvm::raw_svector_ostream OS(Fmt);
1247 // zip_equal asserts that there're as many directives as emitted diagnostics.
1248 // CheckResults has already ensured that all diagnostics were matched.
1249 for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
1250 OrderedDirectives,
1251 llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
1252 assert(!Directive->MatchAnyFileAndLine && !Directive->MatchAnyLine &&
1253 "Cannot compare source locations when wildcards are present");
1254 const auto [DiagLevel, DiagIndex] = LevelDiagPair;
1255 const auto &[DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
1256 const SourceLocation DirLoc = Directive->DirectiveLoc;
1257
1258 bool LocsMatch =
1259 SourceMgr.getPresumedLineNumber(DiagLoc) ==
1260 SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
1261 IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
1262 bool TextMatch = Directive->match(DiagText) == DiagnosticMatchResult::Full;
1263 if (LocsMatch && TextMatch) {
1264 continue;
1265 }
1266 ++NumProblems;
1267
1268 auto printFileNameIfDifferent = [&](SourceLocation DirLoc,
1269 SourceLocation Loc) {
1270 if (!IsFromSameFile(SourceMgr, DirLoc, Loc)) {
1271 OS << " in " << SourceMgr.getFilename(Loc);
1272 }
1273 };
1274
1275 OS << "\n '" << Directive->Spelling << "' at line "
1276 << SourceMgr.getPresumedLineNumber(DirLoc) << " in "
1277 << SourceMgr.getFilename(DirLoc) << ": " << Directive->Text
1278 << "\n matches diagnostic at line "
1279 << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
1280 printFileNameIfDifferent(DirLoc, Directive->DiagnosticLoc);
1281 if (TextMatch) {
1282 OS << ", but diagnostic with the same message was first emitted at line "
1283 << SourceMgr.getPresumedLineNumber(DiagLoc);
1284 printFileNameIfDifferent(DirLoc, DiagLoc);
1285 } else {
1286 OS << ", but diagnostic at line "
1287 << SourceMgr.getPresumedLineNumber(DiagLoc);
1288 printFileNameIfDifferent(DirLoc, DiagLoc);
1289 OS << " was emitted first:"
1290 << "\n " << DiagText;
1291 }
1292 }
1293 if (NumProblems > 0) {
1294 Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
1295 }
1296 return NumProblems;
1297}
1298
1300 FileID FID,
1301 ParsedStatus PS) {
1302 // Check SourceManager hasn't changed.
1303 setSourceManager(SM);
1304
1305#ifndef NDEBUG
1306 if (FID.isInvalid())
1307 return;
1308
1309 OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID);
1310
1311 if (PS == IsParsed) {
1312 // Move the FileID from the unparsed set to the parsed set.
1313 UnparsedFiles.erase(FID);
1314 ParsedFiles.insert(std::make_pair(FID, FE ? &FE->getFileEntry() : nullptr));
1315 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
1316 // Add the FileID to the unparsed set if we haven't seen it before.
1317
1318 // Check for directives.
1319 bool FoundDirectives;
1320 if (PS == IsUnparsedNoDirectives)
1321 FoundDirectives = false;
1322 else
1323 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
1324
1325 // Add the FileID to the unparsed set.
1326 UnparsedFiles.insert(std::make_pair(FID,
1327 UnparsedFileStatus(FE, FoundDirectives)));
1328 }
1329#endif
1330}
1331
1332void VerifyDiagnosticConsumer::CheckDiagnostics() {
1333 // Ensure any diagnostics go to the primary client.
1334 DiagnosticConsumer *CurClient = Diags.getClient();
1335 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
1336 Diags.setClient(PrimaryClient, false);
1337
1338#ifndef NDEBUG
1339 // In a debug build, scan through any files that may have been missed
1340 // during parsing and issue a fatal error if directives are contained
1341 // within these files. If a fatal error occurs, this suggests that
1342 // this file is being parsed separately from the main file, in which
1343 // case consider moving the directives to the correct place, if this
1344 // is applicable.
1345 if (!UnparsedFiles.empty()) {
1346 // Generate a cache of parsed FileEntry pointers for alias lookups.
1348 for (const auto &I : ParsedFiles)
1349 if (const FileEntry *FE = I.second)
1350 ParsedFileCache.insert(FE);
1351
1352 // Iterate through list of unparsed files.
1353 for (const auto &I : UnparsedFiles) {
1354 const UnparsedFileStatus &Status = I.second;
1355 OptionalFileEntryRef FE = Status.getFile();
1356
1357 // Skip files that have been parsed via an alias.
1358 if (FE && ParsedFileCache.count(*FE))
1359 continue;
1360
1361 // Report a fatal error if this file contained directives.
1362 if (Status.foundDirectives()) {
1363 llvm::report_fatal_error("-verify directives found after rather"
1364 " than during normal parsing of " +
1365 (FE ? FE->getName() : "(unknown)"));
1366 }
1367 }
1368
1369 // UnparsedFiles has been processed now, so clear it.
1370 UnparsedFiles.clear();
1371 }
1372#endif // !NDEBUG
1373
1374 if (SrcManager) {
1375 // Produce an error if no expected-* directives could be found in the
1376 // source file(s) processed.
1377 if (State.Status == HasNoDirectives) {
1378 Diags.Report(diag::err_verify_no_directives).setForceEmit()
1379 << DetailedErrorString(Diags);
1380 ++NumErrors;
1381 State.Status = HasNoDirectivesReported;
1382 }
1383
1384 // Check that the expected diagnostics occurred.
1385 NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
1386
1387 // If either wildcards are present or there are directives that
1388 // do not match exactly one diagnostic, we already issued
1389 // errors about that. In such case we cannot check that directives
1390 // are in order.
1391 if (CheckOrderOfDirectives && NumErrors == 0 &&
1392 State.AllDirectivesMatchExactlyOneDiag &&
1393 !State.WildcardsAreErroneouslyPresent) {
1394 NumErrors += CheckResultsAreInOrder(Diags, *SrcManager, *Buffer, ED);
1395 }
1396 } else {
1397 const DiagnosticLevelMask DiagMask =
1398 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1399 if (bool(DiagnosticLevelMask::Error & DiagMask))
1400 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
1401 Buffer->err_end(), "error");
1402 if (bool(DiagnosticLevelMask::Warning & DiagMask))
1403 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
1404 Buffer->warn_end(), "warn");
1405 if (bool(DiagnosticLevelMask::Remark & DiagMask))
1406 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
1407 Buffer->remark_end(), "remark");
1408 if (bool(DiagnosticLevelMask::Note & DiagMask))
1409 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
1410 Buffer->note_end(), "note");
1411 }
1412
1413 Diags.setClient(CurClient, Owner.release() != nullptr);
1414
1415 // Reset the buffer, we have processed all the diagnostics in it.
1416 Buffer.reset(new TextDiagnosticBuffer());
1417 ED.Reset();
1418}
1419
1420std::unique_ptr<Directive>
1423 bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
1424 unsigned Min, unsigned Max, bool FullMatchRequired) {
1425 if (!RegexKind)
1426 return std::make_unique<StandardDirective>(
1429
1430 // Parse the directive into a regular expression.
1431 std::string RegexStr;
1432 StringRef S = Text;
1433 while (!S.empty()) {
1434 if (S.consume_front("{{")) {
1435 size_t RegexMatchLength = S.find("}}");
1436 assert(RegexMatchLength != StringRef::npos);
1437 // Append the regex, enclosed in parentheses.
1438 RegexStr += "(";
1439 RegexStr.append(S.data(), RegexMatchLength);
1440 RegexStr += ")";
1441 S = S.drop_front(RegexMatchLength + 2);
1442 } else {
1443 size_t VerbatimMatchLength = S.find("{{");
1444 if (VerbatimMatchLength == StringRef::npos)
1445 VerbatimMatchLength = S.size();
1446 // Escape and append the fixed string.
1447 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
1448 S = S.drop_front(VerbatimMatchLength);
1449 }
1450 }
1451
1452 return std::make_unique<RegexDirective>(
1454 Text, Min, Max, RegexStr, FullMatchRequired);
1455}
Defines the Diagnostic-related interfaces.
static ICEDiag NoDiag()
Token Tok
The Token.
FormatToken * Next
The next token in the unwrapped line.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
llvm::MachO::FileType FileType
Definition MachO.h:46
#define SM(sm)
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines the clang::TokenKind enum and support functions.
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.
static bool findDirectives(SourceManager &SM, FileID FID, const LangOptions &LangOpts)
Lex the specified source file to determine whether it contains any expected-* directives.
TextDiagnosticBuffer::const_iterator const_diag_iterator
TextDiagnosticBuffer::DiagList DiagList
VerifyDiagnosticConsumer::ExpectedData ExpectedData
static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc)
Determine whether two source locations come from the same file.
static unsigned PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr, llvm::SmallVector< std::pair< Directive *, std::string > > &DL, const char *Kind)
Takes a list of diagnostics that were partially matched and prints them.
VerifyDiagnosticConsumer::DirectiveList DirectiveList
static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const TextDiagnosticBuffer &Buffer, ExpectedData &ED)
CheckResults - This compares the expected results to those that were actually reported.
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...
VerifyDiagnosticConsumer::Directive Directive
static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const TextDiagnosticBuffer &Buffer, const ExpectedData &ED)
static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, Preprocessor *PP, SourceLocation Pos, VerifyDiagnosticConsumer::ParsingState &State, VerifyDiagnosticConsumer::MarkerTracker &Markers, bool OneDiagPerDirective, bool DisableWildcardInDiagLoc)
ParseDirective - Go through the comment and see if it indicates expected diagnostics.
static std::string DetailedErrorString(const DiagnosticsEngine &Diags)
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...
void addDirective(StringRef MarkerName, const UnattachedDirective &UD)
void addMarker(StringRef MarkerName, SourceLocation Pos)
const DiagnosticBuilder & setForceEmit() const
Forces the diagnostic to be emitted.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
unsigned NumErrors
Number of errors reported.
std::vector< std::string > VerifyPrefixes
The prefixes for comment directives sought by -verify ("expected" by default).
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine a...
const SourceLocation & getLocation() const
SourceManager & getSourceManager() const
bool hasSourceManager() const
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:232
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition Diagnostic.h:602
void setClient(DiagnosticConsumer *client, bool ShouldOwnClient=true)
Set the diagnostic client associated with this diagnostic object.
std::unique_ptr< DiagnosticConsumer > takeClient()
Return the current diagnostic client along with ownership of that client.
Definition Diagnostic.h:620
Level
The level of the diagnostic, after it has been through mapping.
Definition Diagnostic.h:237
DiagnosticConsumer * getClient()
Definition Diagnostic.h:612
const FileEntry & getFileEntry() const
Definition FileEntry.h:70
StringRef getName() const
The name of this FileEntry.
Definition FileEntry.h:61
Cached information about one file (either on disk or in the virtual file system).
Definition FileEntry.h:302
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isInvalid() const
Encapsulates the information needed to find the file referenced by a #include or #include_next,...
ModuleMap::KnownHeader findModuleForHeader(FileEntryRef File, bool AllowTextual=false, bool AllowExcluded=false) const
Retrieve the module that corresponds to the given file, if any.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
Definition Lexer.h:78
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition Lexer.h:236
void SetCommentRetentionState(bool Mode)
SetCommentRetentionMode - Change the comment retention mode of the lexer to the specified mode.
Definition Lexer.h:269
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,...
Definition Lexer.cpp:461
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition PPCallbacks.h:37
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
bool isIncrementalProcessingEnabled() const
Returns true if incremental processing is enabled.
SourceManager & getSourceManager() const
OptionalFileEntryRef LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled, ConstSearchDirIterator FromDir, const FileEntry *FromFile, ConstSearchDirIterator *CurDir, SmallVectorImpl< char > *SearchPath, SmallVectorImpl< char > *RelativePath, ModuleMap::KnownHeader *SuggestedModule, bool *IsMapped, bool *IsFrameworkFound, bool SkipCache=false, bool OpenFile=true, bool CacheFailures=true)
Given a "foo" or <foo> reference, look up the indicated file.
DiagnosticsEngine & getDiagnostics() const
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
DiagList::const_iterator const_iterator
AllDiagList::const_iterator all_begin() const
const_iterator warn_end() const
const_iterator note_begin() const
const_iterator err_begin() const
std::vector< std::pair< SourceLocation, std::string > > DiagList
const_iterator note_end() const
const_iterator warn_begin() const
const_iterator remark_begin() const
const_iterator remark_end() const
AllDiagList::const_iterator all_end() const
const_iterator err_end() const
Token - This structure provides full information about a lexed token.
Definition Token.h:36
Directive - Abstract class representing a parsed verify directive.
virtual bool isValid(std::string &Error)=0
virtual DiagnosticMatchResult match(StringRef S) const =0
static const unsigned MaxCount
Constant representing n or more matches.
static std::unique_ptr< Directive > create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, StringRef Spelling, bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, bool FullMatchRequired)
VerifyDiagnosticConsumer - Create a diagnostic client which will use markers in the input source to c...
void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS)
Update lists of parsed and unparsed files.
VerifyDiagnosticConsumer(DiagnosticsEngine &Diags)
Create a new verifying diagnostic client, which will issue errors to the currently-attached diagnosti...
@ IsUnparsed
File has diagnostics and may have directives.
@ IsUnparsedNoDirectives
File has diagnostics but guaranteed no directives.
@ IsParsed
File has been processed via HandleComment.
void EndSourceFile() override
Callback to inform the diagnostic client that processing of a source file has ended.
void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP) override
Callback to inform the diagnostic client that processing of a source file is beginning.
std::vector< std::unique_ptr< Directive > > DirectiveList
bool HandleComment(Preprocessor &PP, SourceRange Comment) override
HandleComment - Hook into the preprocessor and extract comments containing expected errors and warnin...
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.
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
The JSON file list parser is used to communicate input to InstallAPI.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
Definition Sema.h:830
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
DiagnosticLevelMask
A bitmask representing the diagnostic levels used by VerifyDiagnosticConsumer.
@ DType
'dtype' clause, an alias for 'device_type', stored separately for diagnostic purposes.
LLVM_READONLY bool isLetter(unsigned char c)
Return true if this character is an ASCII letter: [a-zA-Z].
Definition CharInfo.h:132
LLVM_READONLY bool isAlphanumeric(unsigned char c)
Return true if this character is an ASCII letter or digit: [a-zA-Z0-9].
Definition CharInfo.h:138
LLVM_READONLY bool isDigit(unsigned char c)
Return true if this character is an ASCII digit: [0-9].
Definition CharInfo.h:114
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition CharInfo.h:108
@ AtLeastPartial
Match, but not a full match.
@ Full
Match, but we didn't check for full match.
ExpectedData - owns directive objects and deletes on destructor.