clang 24.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 {
58 SourceManager &SM;
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.
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.
1024static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
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::stable_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
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
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:234
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition Diagnostic.h:615
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:633
Level
The level of the diagnostic, after it has been through mapping.
Definition Diagnostic.h:239
DiagnosticConsumer * getClient()
Definition Diagnostic.h:625
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:273
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isInvalid() const
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true, bool IsText=true)
Get a FileEntryRef if it exists, without doing anything on error.
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:79
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:462
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.
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
DiagnosticsEngine & getDiagnostics() const
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
FileID translateFile(const FileEntry *SourceFile) const
Get the FileID for the given file.
FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID=0, SourceLocation::UIntTy LoadedOffset=0)
Create a new FileID that represents the specified file being #included from the specified IncludePosi...
FileManager & getFileManager() const
SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const
Gets the location of the immediate macro caller, one level up the stack toward the initial macro type...
const char * getCharacterData(SourceLocation SL, bool *Invalid=nullptr) const
Return a pointer to the start of the specified location in the appropriate spelling MemoryBuffer.
llvm::MemoryBufferRef getBufferOrFake(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
bool isWrittenInSameFile(SourceLocation Loc1, SourceLocation Loc2) const
Returns true if the spelling locations for both SourceLocations are part of the same file buffer.
bool isWrittenInMainFile(SourceLocation Loc) const
Returns true if the spelling location for the given location is in the main file buffer.
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.
Top level wrappers for InstallAPI frontend operations.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
Definition Sema.h:825
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:196
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.