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
13#include "clang/Frontend/VerifyDiagnosticConsumer.h"
14#include "clang/Basic/CharInfo.h"
15#include "clang/Basic/Diagnostic.h"
16#include "clang/Basic/DiagnosticFrontend.h"
17#include "clang/Basic/DiagnosticOptions.h"
18#include "clang/Basic/LLVM.h"
19#include "clang/Basic/SourceLocation.h"
20#include "clang/Basic/SourceManager.h"
21#include "clang/Basic/TokenKinds.h"
22#include "clang/Frontend/TextDiagnosticBuffer.h"
23#include "clang/Lex/HeaderSearch.h"
24#include "clang/Lex/Lexer.h"
25#include "clang/Lex/PPCallbacks.h"
26#include "clang/Lex/Preprocessor.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
48using Directive = VerifyDiagnosticConsumer::Directive;
49using DirectiveList = VerifyDiagnosticConsumer::DirectiveList;
50using ExpectedData = VerifyDiagnosticConsumer::ExpectedData;
51
52#ifndef NDEBUG
53
54namespace {
55
56class VerifyFileTracker : public PPCallbacks {
57 VerifyDiagnosticConsumer &Verify;
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,
67 SrcMgr::CharacteristicKind FileType,
68 FileID PrevFID) override {
69 Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
70 VerifyDiagnosticConsumer::IsParsed);
71 }
72};
73
74} // namespace
75
76#endif
77
78//===----------------------------------------------------------------------===//
79// Checking diagnostics implementation.
80//===----------------------------------------------------------------------===//
81
82using DiagList = TextDiagnosticBuffer::DiagList;
83using const_diag_iterator = TextDiagnosticBuffer::const_iterator;
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)
94 : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
95 MatchAnyLine, Text, Min, Max) {}
96
97 bool isValid(std::string &Error) override {
98 // all strings are considered valid; even empty ones
99 return true;
100 }
101
102 bool match(StringRef S) override { return S.contains(Other: Text); }
103};
104
105/// RegexDirective - Directive with regular-expression matching.
106class RegexDirective : public Directive {
107public:
108 RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
109 StringRef Spelling, bool MatchAnyFileAndLine,
110 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
111 StringRef RegexStr)
112 : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
113 MatchAnyLine, Text, Min, Max),
114 Regex(RegexStr) {}
115
116 bool isValid(std::string &Error) override {
117 return Regex.isValid(Error);
118 }
119
120 bool match(StringRef S) override {
121 return Regex.match(String: S);
122 }
123
124private:
125 llvm::Regex Regex;
126};
127
128class ParseHelper
129{
130public:
131 ParseHelper(StringRef S)
132 : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
133
134 // Return true if string literal is next.
135 bool Next(StringRef S) {
136 P = C;
137 PEnd = C + S.size();
138 if (PEnd > End)
139 return false;
140 return memcmp(s1: P, s2: S.data(), n: S.size()) == 0;
141 }
142
143 // Return true if number is next.
144 // Output N only if number is next.
145 bool Next(unsigned &N) {
146 unsigned TMP = 0;
147 P = C;
148 PEnd = P;
149 for (; PEnd < End && *PEnd >= '0' && *PEnd <= '9'; ++PEnd) {
150 TMP *= 10;
151 TMP += *PEnd - '0';
152 }
153 if (PEnd == C)
154 return false;
155 N = TMP;
156 return true;
157 }
158
159 // Return true if a marker is next.
160 // A marker is the longest match for /#[A-Za-z0-9_-]+/.
161 bool NextMarker() {
162 P = C;
163 if (P == End || *P != '#')
164 return false;
165 PEnd = P;
166 ++PEnd;
167 while ((isAlphanumeric(c: *PEnd) || *PEnd == '-' || *PEnd == '_') &&
168 PEnd < End)
169 ++PEnd;
170 return PEnd > P + 1;
171 }
172
173 // Return true if string literal S is matched in content.
174 // When true, P marks begin-position of the match, and calling Advance sets C
175 // to end-position of the match.
176 // If S is the empty string, then search for any letter instead (makes sense
177 // with FinishDirectiveToken=true).
178 // If EnsureStartOfWord, then skip matches that don't start a new word.
179 // If FinishDirectiveToken, then assume the match is the start of a comment
180 // directive for -verify, and extend the match to include the entire first
181 // token of that directive.
182 bool Search(StringRef S, bool EnsureStartOfWord = false,
183 bool FinishDirectiveToken = false) {
184 do {
185 if (!S.empty()) {
186 P = std::search(first1: C, last1: End, first2: S.begin(), last2: S.end());
187 PEnd = P + S.size();
188 }
189 else {
190 P = C;
191 while (P != End && !isLetter(c: *P))
192 ++P;
193 PEnd = P + 1;
194 }
195 if (P == End)
196 break;
197 // If not start of word but required, skip and search again.
198 if (EnsureStartOfWord
199 // Check if string literal starts a new word.
200 && !(P == Begin || isWhitespace(c: P[-1])
201 // Or it could be preceded by the start of a comment.
202 || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
203 && P[-2] == '/')))
204 continue;
205 if (FinishDirectiveToken) {
206 while (PEnd != End && (isAlphanumeric(c: *PEnd)
207 || *PEnd == '-' || *PEnd == '_'))
208 ++PEnd;
209 // Put back trailing digits and hyphens to be parsed later as a count
210 // or count range. Because -verify prefixes must start with letters,
211 // we know the actual directive we found starts with a letter, so
212 // we won't put back the entire directive word and thus record an empty
213 // string.
214 assert(isLetter(*P) && "-verify prefix must start with a letter");
215 while (isDigit(c: PEnd[-1]) || PEnd[-1] == '-')
216 --PEnd;
217 }
218 return true;
219 } while (Advance());
220 return false;
221 }
222
223 // Return true if a CloseBrace that closes the OpenBrace at the current nest
224 // level is found. When true, P marks begin-position of CloseBrace.
225 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
226 unsigned Depth = 1;
227 P = C;
228 while (P < End) {
229 StringRef S(P, End - P);
230 if (S.starts_with(Prefix: OpenBrace)) {
231 ++Depth;
232 P += OpenBrace.size();
233 } else if (S.starts_with(Prefix: CloseBrace)) {
234 --Depth;
235 if (Depth == 0) {
236 PEnd = P + CloseBrace.size();
237 return true;
238 }
239 P += CloseBrace.size();
240 } else {
241 ++P;
242 }
243 }
244 return false;
245 }
246
247 // Advance 1-past previous next/search.
248 // Behavior is undefined if previous next/search failed.
249 bool Advance() {
250 C = PEnd;
251 return C < End;
252 }
253
254 // Return the text matched by the previous next/search.
255 // Behavior is undefined if previous next/search failed.
256 StringRef Match() { return StringRef(P, PEnd - P); }
257
258 // Skip zero or more whitespace.
259 void SkipWhitespace() {
260 for (; C < End && isWhitespace(c: *C); ++C)
261 ;
262 }
263
264 // Return true if EOF reached.
265 bool Done() {
266 return !(C < End);
267 }
268
269 // Beginning of expected content.
270 const char * const Begin;
271
272 // End of expected content (1-past).
273 const char * const End;
274
275 // Position of next char in content.
276 const char *C;
277
278 // Previous next/search subject start.
279 const char *P;
280
281private:
282 // Previous next/search subject end (1-past).
283 const char *PEnd = nullptr;
284};
285
286// The information necessary to create a directive.
287struct UnattachedDirective {
288 DirectiveList *DL = nullptr;
289 std::string Spelling;
290 bool RegexKind = false;
291 SourceLocation DirectivePos, ContentBegin;
292 std::string Text;
293 unsigned Min = 1, Max = 1;
294};
295
296// Attach the specified directive to the line of code indicated by
297// \p ExpectedLoc.
298void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
299 SourceLocation ExpectedLoc,
300 bool MatchAnyFileAndLine = false,
301 bool MatchAnyLine = false) {
302 // Construct new directive.
303 std::unique_ptr<Directive> D = Directive::create(
304 RegexKind: UD.RegexKind, DirectiveLoc: UD.DirectivePos, DiagnosticLoc: ExpectedLoc, Spelling: UD.Spelling,
305 MatchAnyFileAndLine, MatchAnyLine, Text: UD.Text, Min: UD.Min, Max: UD.Max);
306
307 std::string Error;
308 if (!D->isValid(Error)) {
309 Diags.Report(Loc: UD.ContentBegin, DiagID: diag::err_verify_invalid_content)
310 << (UD.RegexKind ? "regex" : "string") << Error;
311 }
312
313 UD.DL->push_back(x: std::move(D));
314}
315
316} // anonymous
317
318// Tracker for markers in the input files. A marker is a comment of the form
319//
320// n = 123; // #123
321//
322// ... that can be referred to by a later expected-* directive:
323//
324// // expected-error@#123 {{undeclared identifier 'n'}}
325//
326// Marker declarations must be at the start of a comment or preceded by
327// whitespace to distinguish them from uses of markers in directives.
328class VerifyDiagnosticConsumer::MarkerTracker {
329 DiagnosticsEngine &Diags;
330
331 struct Marker {
332 SourceLocation DefLoc;
333 SourceLocation RedefLoc;
334 SourceLocation UseLoc;
335 };
336 llvm::StringMap<Marker> Markers;
337
338 // Directives that couldn't be created yet because they name an unknown
339 // marker.
340 llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives;
341
342public:
343 MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {}
344
345 // Register a marker.
346 void addMarker(StringRef MarkerName, SourceLocation Pos) {
347 auto InsertResult = Markers.insert(
348 KV: {MarkerName, Marker{.DefLoc: Pos, .RedefLoc: SourceLocation(), .UseLoc: SourceLocation()}});
349
350 Marker &M = InsertResult.first->second;
351 if (!InsertResult.second) {
352 // Marker was redefined.
353 M.RedefLoc = Pos;
354 } else {
355 // First definition: build any deferred directives.
356 auto Deferred = DeferredDirectives.find(Key: MarkerName);
357 if (Deferred != DeferredDirectives.end()) {
358 for (auto &UD : Deferred->second) {
359 if (M.UseLoc.isInvalid())
360 M.UseLoc = UD.DirectivePos;
361 attachDirective(Diags, UD, ExpectedLoc: Pos);
362 }
363 DeferredDirectives.erase(I: Deferred);
364 }
365 }
366 }
367
368 // Register a directive at the specified marker.
369 void addDirective(StringRef MarkerName, const UnattachedDirective &UD) {
370 auto MarkerIt = Markers.find(Key: MarkerName);
371 if (MarkerIt != Markers.end()) {
372 Marker &M = MarkerIt->second;
373 if (M.UseLoc.isInvalid())
374 M.UseLoc = UD.DirectivePos;
375 return attachDirective(Diags, UD, ExpectedLoc: M.DefLoc);
376 }
377 DeferredDirectives[MarkerName].push_back(Elt: UD);
378 }
379
380 // Ensure we have no remaining deferred directives, and no
381 // multiply-defined-and-used markers.
382 void finalize() {
383 for (auto &MarkerInfo : Markers) {
384 StringRef Name = MarkerInfo.first();
385 Marker &M = MarkerInfo.second;
386 if (M.RedefLoc.isValid() && M.UseLoc.isValid()) {
387 Diags.Report(Loc: M.UseLoc, DiagID: diag::err_verify_ambiguous_marker) << Name;
388 Diags.Report(Loc: M.DefLoc, DiagID: diag::note_verify_ambiguous_marker) << Name;
389 Diags.Report(Loc: M.RedefLoc, DiagID: diag::note_verify_ambiguous_marker) << Name;
390 }
391 }
392
393 for (auto &DeferredPair : DeferredDirectives) {
394 Diags.Report(Loc: DeferredPair.second.front().DirectivePos,
395 DiagID: diag::err_verify_no_such_marker)
396 << DeferredPair.first();
397 }
398 }
399};
400
401static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
402 if (Diags.getDiagnosticOptions().VerifyPrefixes.empty())
403 return "expected";
404 return *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
405}
406
407/// ParseDirective - Go through the comment and see if it indicates expected
408/// diagnostics. If so, then put them in the appropriate directive list.
409///
410/// Returns true if any valid directives were found.
411static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
412 Preprocessor *PP, SourceLocation Pos,
413 VerifyDiagnosticConsumer::ParsingState &State,
414 VerifyDiagnosticConsumer::MarkerTracker &Markers) {
415 DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
416
417 // First, scan the comment looking for markers.
418 for (ParseHelper PH(S); !PH.Done();) {
419 if (!PH.Search(S: "#", EnsureStartOfWord: true))
420 break;
421 PH.C = PH.P;
422 if (!PH.NextMarker()) {
423 PH.Next(S: "#");
424 PH.Advance();
425 continue;
426 }
427 PH.Advance();
428 Markers.addMarker(MarkerName: PH.Match(), Pos);
429 }
430
431 // A single comment may contain multiple directives.
432 bool FoundDirective = false;
433 for (ParseHelper PH(S); !PH.Done();) {
434 // Search for the initial directive token.
435 // If one prefix, save time by searching only for its directives.
436 // Otherwise, search for any potential directive token and check it later.
437 const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes;
438 if (!(Prefixes.size() == 1 ? PH.Search(S: *Prefixes.begin(), EnsureStartOfWord: true, FinishDirectiveToken: true)
439 : PH.Search(S: "", EnsureStartOfWord: true, FinishDirectiveToken: true)))
440 break;
441
442 StringRef DToken = PH.Match();
443 PH.Advance();
444
445 UnattachedDirective D;
446 D.Spelling = DToken;
447 // Default directive kind.
448 const char *KindStr = "string";
449
450 // Parse the initial directive token in reverse so we can easily determine
451 // its exact actual prefix. If we were to parse it from the front instead,
452 // it would be harder to determine where the prefix ends because there
453 // might be multiple matching -verify prefixes because some might prefix
454 // others.
455
456 // Regex in initial directive token: -re
457 if (DToken.consume_back(Suffix: "-re")) {
458 D.RegexKind = true;
459 KindStr = "regex";
460 }
461
462 // Type in initial directive token: -{error|warning|note|no-diagnostics}
463 bool NoDiag = false;
464 StringRef DType;
465 if (DToken.ends_with(Suffix: DType = "-error"))
466 D.DL = ED ? &ED->Errors : nullptr;
467 else if (DToken.ends_with(Suffix: DType = "-warning"))
468 D.DL = ED ? &ED->Warnings : nullptr;
469 else if (DToken.ends_with(Suffix: DType = "-remark"))
470 D.DL = ED ? &ED->Remarks : nullptr;
471 else if (DToken.ends_with(Suffix: DType = "-note"))
472 D.DL = ED ? &ED->Notes : nullptr;
473 else if (DToken.ends_with(Suffix: DType = "-no-diagnostics")) {
474 NoDiag = true;
475 if (D.RegexKind)
476 continue;
477 } else
478 continue;
479 DToken = DToken.substr(Start: 0, N: DToken.size()-DType.size());
480
481 // What's left in DToken is the actual prefix. That might not be a -verify
482 // prefix even if there is only one -verify prefix (for example, the full
483 // DToken is foo-bar-warning, but foo is the only -verify prefix).
484 if (!llvm::binary_search(Range: Prefixes, Value&: DToken))
485 continue;
486
487 if (NoDiag) {
488 if (State.Status ==
489 VerifyDiagnosticConsumer::HasOtherExpectedDirectives) {
490 Diags.Report(Loc: Pos, DiagID: diag::err_verify_invalid_no_diags)
491 << D.Spelling << /*IsExpectedNoDiagnostics=*/true;
492 } else if (State.Status !=
493 VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
494 State.Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
495 State.FirstNoDiagnosticsDirective = D.Spelling;
496 }
497 continue;
498 }
499 if (State.Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
500 Diags.Report(Loc: Pos, DiagID: diag::err_verify_invalid_no_diags)
501 << D.Spelling << /*IsExpectedNoDiagnostics=*/false
502 << State.FirstNoDiagnosticsDirective;
503 continue;
504 }
505 State.Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
506
507 // If a directive has been found but we're not interested
508 // in storing the directive information, return now.
509 if (!D.DL)
510 return true;
511
512 // Next optional token: @
513 SourceLocation ExpectedLoc;
514 StringRef Marker;
515 bool MatchAnyFileAndLine = false;
516 bool MatchAnyLine = false;
517 if (!PH.Next(S: "@")) {
518 ExpectedLoc = Pos;
519
520 // If an implicit directive is found in an incremental input buffer, allow
521 // it to match any other incremental input buffer
522 if (PP->isIncrementalProcessingEnabled()) {
523 StringRef CurrentBufferName =
524 SM.getBufferOrFake(FID: SM.getFileID(SpellingLoc: Pos)).getBufferIdentifier();
525 if (CurrentBufferName.starts_with(Prefix: "input_line_"))
526 MatchAnyFileAndLine = true;
527 }
528 } else {
529 PH.Advance();
530 unsigned Line = 0;
531 bool FoundPlus = PH.Next(S: "+");
532 if (FoundPlus || PH.Next(S: "-")) {
533 // Relative to current line.
534 PH.Advance();
535 bool Invalid = false;
536 unsigned ExpectedLine = SM.getSpellingLineNumber(Loc: Pos, Invalid: &Invalid);
537 if (!Invalid && PH.Next(N&: Line) && (FoundPlus || Line < ExpectedLine)) {
538 if (FoundPlus) ExpectedLine += Line;
539 else ExpectedLine -= Line;
540 ExpectedLoc = SM.translateLineCol(FID: SM.getFileID(SpellingLoc: Pos), Line: ExpectedLine, Col: 1);
541 }
542 } else if (PH.Next(N&: Line)) {
543 // Absolute line number.
544 if (Line > 0)
545 ExpectedLoc = SM.translateLineCol(FID: SM.getFileID(SpellingLoc: Pos), Line, Col: 1);
546 } else if (PH.NextMarker()) {
547 Marker = PH.Match();
548 } else if (PP && PH.Search(S: ":")) {
549 // Specific source file.
550 StringRef Filename(PH.C, PH.P-PH.C);
551 PH.Advance();
552
553 if (Filename == "*") {
554 MatchAnyFileAndLine = true;
555 if (!PH.Next(S: "*")) {
556 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C - PH.Begin),
557 DiagID: diag::err_verify_missing_line)
558 << "'*'";
559 continue;
560 }
561 MatchAnyLine = true;
562 ExpectedLoc = SourceLocation();
563 } else {
564 OptionalFileEntryRef File;
565 if (PP->isIncrementalProcessingEnabled() &&
566 Filename.starts_with(Prefix: "input_line_")) {
567 // Check if it came from the prompt
568 File = SM.getFileManager().getOptionalFileRef(Filename);
569 } else {
570 // Lookup file via Preprocessor, like a #include.
571 File =
572 PP->LookupFile(FilenameLoc: Pos, Filename, isAngled: false, FromDir: nullptr, FromFile: nullptr, CurDir: nullptr,
573 SearchPath: nullptr, RelativePath: nullptr, SuggestedModule: nullptr, IsMapped: nullptr, IsFrameworkFound: nullptr);
574 }
575 if (!File) {
576 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C - PH.Begin),
577 DiagID: diag::err_verify_missing_file)
578 << Filename << KindStr;
579 continue;
580 }
581
582 FileID FID = SM.translateFile(SourceFile: *File);
583 if (FID.isInvalid())
584 FID = SM.createFileID(SourceFile: *File, IncludePos: Pos, FileCharacter: SrcMgr::C_User);
585
586 if (PH.Next(N&: Line) && Line > 0)
587 ExpectedLoc = SM.translateLineCol(FID, Line, Col: 1);
588 else if (PH.Next(S: "*")) {
589 MatchAnyLine = true;
590 ExpectedLoc = SM.translateLineCol(FID, Line: 1, Col: 1);
591 }
592 }
593 } else if (PH.Next(S: "*")) {
594 MatchAnyLine = true;
595 ExpectedLoc = SourceLocation();
596 }
597
598 if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) {
599 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C-PH.Begin),
600 DiagID: diag::err_verify_missing_line) << KindStr;
601 continue;
602 }
603 PH.Advance();
604 }
605
606 // Skip optional whitespace.
607 PH.SkipWhitespace();
608
609 // Next optional token: positive integer or a '+'.
610 if (PH.Next(N&: D.Min)) {
611 PH.Advance();
612 // A positive integer can be followed by a '+' meaning min
613 // or more, or by a '-' meaning a range from min to max.
614 if (PH.Next(S: "+")) {
615 D.Max = Directive::MaxCount;
616 PH.Advance();
617 } else if (PH.Next(S: "-")) {
618 PH.Advance();
619 if (!PH.Next(N&: D.Max) || D.Max < D.Min) {
620 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C-PH.Begin),
621 DiagID: diag::err_verify_invalid_range) << KindStr;
622 continue;
623 }
624 PH.Advance();
625 } else {
626 D.Max = D.Min;
627 }
628 } else if (PH.Next(S: "+")) {
629 // '+' on its own means "1 or more".
630 D.Max = Directive::MaxCount;
631 PH.Advance();
632 }
633
634 // Skip optional whitespace.
635 PH.SkipWhitespace();
636
637 // Next token: {{
638 if (!PH.Next(S: "{{")) {
639 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C-PH.Begin),
640 DiagID: diag::err_verify_missing_start) << KindStr;
641 continue;
642 }
643 llvm::SmallString<8> CloseBrace("}}");
644 const char *const DelimBegin = PH.C;
645 PH.Advance();
646 // Count the number of opening braces for `string` kinds
647 for (; !D.RegexKind && PH.Next(S: "{"); PH.Advance())
648 CloseBrace += '}';
649 const char* const ContentBegin = PH.C; // mark content begin
650 // Search for closing brace
651 StringRef OpenBrace(DelimBegin, ContentBegin - DelimBegin);
652 if (!PH.SearchClosingBrace(OpenBrace, CloseBrace)) {
653 Diags.Report(Loc: Pos.getLocWithOffset(Offset: PH.C - PH.Begin),
654 DiagID: diag::err_verify_missing_end)
655 << KindStr << CloseBrace;
656 continue;
657 }
658 const char* const ContentEnd = PH.P; // mark content end
659 PH.Advance();
660
661 D.DirectivePos = Pos;
662 D.ContentBegin = Pos.getLocWithOffset(Offset: ContentBegin - PH.Begin);
663
664 // Build directive text; convert \n to newlines.
665 StringRef NewlineStr = "\\n";
666 StringRef Content(ContentBegin, ContentEnd-ContentBegin);
667 size_t CPos = 0;
668 size_t FPos;
669 while ((FPos = Content.find(Str: NewlineStr, From: CPos)) != StringRef::npos) {
670 D.Text += Content.substr(Start: CPos, N: FPos-CPos);
671 D.Text += '\n';
672 CPos = FPos + NewlineStr.size();
673 }
674 if (D.Text.empty())
675 D.Text.assign(first: ContentBegin, last: ContentEnd);
676
677 // Check that regex directives contain at least one regex.
678 if (D.RegexKind && D.Text.find(s: "{{") == StringRef::npos) {
679 Diags.Report(Loc: D.ContentBegin, DiagID: diag::err_verify_missing_regex) << D.Text;
680 return false;
681 }
682
683 if (Marker.empty())
684 attachDirective(Diags, UD: D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine);
685 else
686 Markers.addDirective(MarkerName: Marker, UD: D);
687 FoundDirective = true;
688 }
689
690 return FoundDirective;
691}
692
693VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
694 : Diags(Diags_), PrimaryClient(Diags.getClient()),
695 PrimaryClientOwner(Diags.takeClient()),
696 Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
697 State{.Status: HasNoDirectives, .FirstNoDiagnosticsDirective: {}} {
698 if (Diags.hasSourceManager())
699 setSourceManager(Diags.getSourceManager());
700}
701
702VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
703 assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
704 assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
705 SrcManager = nullptr;
706 CheckDiagnostics();
707}
708
709// DiagnosticConsumer interface.
710
711void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
712 const Preprocessor *PP) {
713 // Attach comment handler on first invocation.
714 if (++ActiveSourceFiles == 1) {
715 if (PP) {
716 CurrentPreprocessor = PP;
717 this->LangOpts = &LangOpts;
718 setSourceManager(PP->getSourceManager());
719 const_cast<Preprocessor *>(PP)->addCommentHandler(Handler: this);
720#ifndef NDEBUG
721 // Debug build tracks parsed files.
722 const_cast<Preprocessor *>(PP)->addPPCallbacks(
723 std::make_unique<VerifyFileTracker>(*this, *SrcManager));
724#endif
725 }
726 }
727
728 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
729 PrimaryClient->BeginSourceFile(LangOpts, PP);
730}
731
732void VerifyDiagnosticConsumer::EndSourceFile() {
733 assert(ActiveSourceFiles && "No active source files!");
734 PrimaryClient->EndSourceFile();
735
736 // Detach comment handler once last active source file completed.
737 if (--ActiveSourceFiles == 0) {
738 if (CurrentPreprocessor)
739 const_cast<Preprocessor *>(CurrentPreprocessor)->
740 removeCommentHandler(Handler: this);
741
742 // Diagnose any used-but-not-defined markers.
743 Markers->finalize();
744
745 // Check diagnostics once last file completed.
746 CheckDiagnostics();
747 CurrentPreprocessor = nullptr;
748 LangOpts = nullptr;
749 }
750}
751
752void VerifyDiagnosticConsumer::HandleDiagnostic(
753 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
754 if (Info.hasSourceManager()) {
755 // If this diagnostic is for a different source manager, ignore it.
756 if (SrcManager && &Info.getSourceManager() != SrcManager)
757 return;
758
759 setSourceManager(Info.getSourceManager());
760 }
761
762#ifndef NDEBUG
763 // Debug build tracks unparsed files for possible
764 // unparsed expected-* directives.
765 if (SrcManager) {
766 SourceLocation Loc = Info.getLocation();
767 if (Loc.isValid()) {
768 ParsedStatus PS = IsUnparsed;
769
770 Loc = SrcManager->getExpansionLoc(Loc);
771 FileID FID = SrcManager->getFileID(Loc);
772
773 auto FE = SrcManager->getFileEntryRefForID(FID);
774 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
775 // If the file is a modules header file it shall not be parsed
776 // for expected-* directives.
777 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
778 if (HS.findModuleForHeader(*FE))
779 PS = IsUnparsedNoDirectives;
780 }
781
782 UpdateParsedFileStatus(*SrcManager, FID, PS);
783 }
784 }
785#endif
786
787 // Send the diagnostic to the buffer, we will check it once we reach the end
788 // of the source file (or are destructed).
789 Buffer->HandleDiagnostic(DiagLevel, Info);
790}
791
792/// HandleComment - Hook into the preprocessor and extract comments containing
793/// expected errors and warnings.
794bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
795 SourceRange Comment) {
796 SourceManager &SM = PP.getSourceManager();
797
798 // If this comment is for a different source manager, ignore it.
799 if (SrcManager && &SM != SrcManager)
800 return false;
801
802 SourceLocation CommentBegin = Comment.getBegin();
803
804 const char *CommentRaw = SM.getCharacterData(SL: CommentBegin);
805 StringRef C(CommentRaw, SM.getCharacterData(SL: Comment.getEnd()) - CommentRaw);
806
807 if (C.empty())
808 return false;
809
810 // Fold any "\<EOL>" sequences
811 size_t loc = C.find(C: '\\');
812 if (loc == StringRef::npos) {
813 ParseDirective(S: C, ED: &ED, SM, PP: &PP, Pos: CommentBegin, State, Markers&: *Markers);
814 return false;
815 }
816
817 std::string C2;
818 C2.reserve(res_arg: C.size());
819
820 for (size_t last = 0;; loc = C.find(C: '\\', From: last)) {
821 if (loc == StringRef::npos || loc == C.size()) {
822 C2 += C.substr(Start: last);
823 break;
824 }
825 C2 += C.substr(Start: last, N: loc-last);
826 last = loc + 1;
827
828 if (last < C.size() && (C[last] == '\n' || C[last] == '\r')) {
829 ++last;
830
831 // Escape \r\n or \n\r, but not \n\n.
832 if (last < C.size())
833 if (C[last] == '\n' || C[last] == '\r')
834 if (C[last] != C[last-1])
835 ++last;
836 } else {
837 // This was just a normal backslash.
838 C2 += '\\';
839 }
840 }
841
842 if (!C2.empty())
843 ParseDirective(S: C2, ED: &ED, SM, PP: &PP, Pos: CommentBegin, State, Markers&: *Markers);
844 return false;
845}
846
847#ifndef NDEBUG
848/// Lex the specified source file to determine whether it contains
849/// any expected-* directives. As a Lexer is used rather than a full-blown
850/// Preprocessor, directives inside skipped #if blocks will still be found.
851///
852/// \return true if any directives were found.
853static bool findDirectives(SourceManager &SM, FileID FID,
854 const LangOptions &LangOpts) {
855 // Create a raw lexer to pull all the comments out of FID.
856 if (FID.isInvalid())
857 return false;
858
859 // Create a lexer to lex all the tokens of the main file in raw mode.
860 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
861 Lexer RawLex(FID, FromFile, SM, LangOpts);
862
863 // Return comments as tokens, this is how we find expected diagnostics.
864 RawLex.SetCommentRetentionState(true);
865
866 Token Tok;
867 Tok.setKind(tok::comment);
868 VerifyDiagnosticConsumer::ParsingState State = {
869 VerifyDiagnosticConsumer::HasNoDirectives, {}};
870 while (Tok.isNot(tok::eof)) {
871 RawLex.LexFromRawLexer(Tok);
872 if (!Tok.is(tok::comment)) continue;
873
874 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
875 if (Comment.empty()) continue;
876
877 // We don't care about tracking markers for this phase.
878 VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
879
880 // Find first directive.
881 if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
882 Markers))
883 return true;
884 }
885 return false;
886}
887#endif // !NDEBUG
888
889/// Takes a list of diagnostics that have been generated but not matched
890/// by an expected-* directive and produces a diagnostic to the user from this.
891static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
892 const_diag_iterator diag_begin,
893 const_diag_iterator diag_end,
894 const char *Kind) {
895 if (diag_begin == diag_end) return 0;
896
897 SmallString<256> Fmt;
898 llvm::raw_svector_ostream OS(Fmt);
899 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
900 if (I->first.isInvalid() || !SourceMgr)
901 OS << "\n (frontend)";
902 else {
903 OS << "\n ";
904 if (OptionalFileEntryRef File =
905 SourceMgr->getFileEntryRefForID(FID: SourceMgr->getFileID(SpellingLoc: I->first)))
906 OS << " File " << File->getName();
907 OS << " Line " << SourceMgr->getPresumedLineNumber(Loc: I->first);
908 }
909 OS << ": " << I->second;
910 }
911
912 const bool IsSinglePrefix =
913 Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
914 std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
915 Diags.Report(DiagID: diag::err_verify_inconsistent_diags).setForceEmit()
916 << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/true << OS.str();
917 return std::distance(first: diag_begin, last: diag_end);
918}
919
920/// Takes a list of diagnostics that were expected to have been generated
921/// but were not and produces a diagnostic to the user from this.
922static unsigned PrintExpected(DiagnosticsEngine &Diags,
923 SourceManager &SourceMgr,
924 std::vector<Directive *> &DL, const char *Kind) {
925 if (DL.empty())
926 return 0;
927
928 const bool IsSinglePrefix =
929 Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
930
931 SmallString<256> Fmt;
932 llvm::raw_svector_ostream OS(Fmt);
933 for (const auto *D : DL) {
934 if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
935 OS << "\n File *";
936 else
937 OS << "\n File " << SourceMgr.getFilename(SpellingLoc: D->DiagnosticLoc);
938 if (D->MatchAnyLine)
939 OS << " Line *";
940 else
941 OS << " Line " << SourceMgr.getPresumedLineNumber(Loc: D->DiagnosticLoc);
942 if (D->DirectiveLoc != D->DiagnosticLoc)
943 OS << " (directive at "
944 << SourceMgr.getFilename(SpellingLoc: D->DirectiveLoc) << ':'
945 << SourceMgr.getPresumedLineNumber(Loc: D->DirectiveLoc) << ')';
946 if (!IsSinglePrefix)
947 OS << " \'" << D->Spelling << '\'';
948 OS << ": " << D->Text;
949 }
950
951 std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
952 Diags.Report(DiagID: diag::err_verify_inconsistent_diags).setForceEmit()
953 << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/false << OS.str();
954 return DL.size();
955}
956
957/// Determine whether two source locations come from the same file.
958static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
959 SourceLocation DiagnosticLoc) {
960 while (DiagnosticLoc.isMacroID())
961 DiagnosticLoc = SM.getImmediateMacroCallerLoc(Loc: DiagnosticLoc);
962
963 if (SM.isWrittenInSameFile(Loc1: DirectiveLoc, Loc2: DiagnosticLoc))
964 return true;
965
966 const FileEntry *DiagFile = SM.getFileEntryForID(FID: SM.getFileID(SpellingLoc: DiagnosticLoc));
967 if (!DiagFile && SM.isWrittenInMainFile(Loc: DirectiveLoc))
968 return true;
969
970 return (DiagFile == SM.getFileEntryForID(FID: SM.getFileID(SpellingLoc: DirectiveLoc)));
971}
972
973/// CheckLists - Compare expected to seen diagnostic lists and return the
974/// the difference between them.
975static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
976 const char *Label,
977 DirectiveList &Left,
978 const_diag_iterator d2_begin,
979 const_diag_iterator d2_end,
980 bool IgnoreUnexpected) {
981 std::vector<Directive *> LeftOnly;
982 DiagList Right(d2_begin, d2_end);
983
984 for (auto &Owner : Left) {
985 Directive &D = *Owner;
986 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(Loc: D.DiagnosticLoc);
987
988 for (unsigned i = 0; i < D.Max; ++i) {
989 DiagList::iterator II, IE;
990 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
991 if (!D.MatchAnyLine) {
992 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(Loc: II->first);
993 if (LineNo1 != LineNo2)
994 continue;
995 }
996
997 if (!D.DiagnosticLoc.isInvalid() && !D.MatchAnyFileAndLine &&
998 !IsFromSameFile(SM&: SourceMgr, DirectiveLoc: D.DiagnosticLoc, DiagnosticLoc: II->first))
999 continue;
1000
1001 const std::string &RightText = II->second;
1002 if (D.match(S: RightText))
1003 break;
1004 }
1005 if (II == IE) {
1006 // Not found.
1007 if (i >= D.Min) break;
1008 LeftOnly.push_back(x: &D);
1009 } else {
1010 // Found. The same cannot be found twice.
1011 Right.erase(position: II);
1012 }
1013 }
1014 }
1015 // Now all that's left in Right are those that were not matched.
1016 unsigned num = PrintExpected(Diags, SourceMgr, DL&: LeftOnly, Kind: Label);
1017 if (!IgnoreUnexpected)
1018 num += PrintUnexpected(Diags, SourceMgr: &SourceMgr, diag_begin: Right.begin(), diag_end: Right.end(), Kind: Label);
1019 return num;
1020}
1021
1022/// CheckResults - This compares the expected results to those that
1023/// were actually reported. It emits any discrepencies. Return "true" if there
1024/// were problems. Return "false" otherwise.
1025static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
1026 const TextDiagnosticBuffer &Buffer,
1027 ExpectedData &ED) {
1028 // We want to capture the delta between what was expected and what was
1029 // seen.
1030 //
1031 // Expected \ Seen - set expected but not seen
1032 // Seen \ Expected - set seen but not expected
1033 unsigned NumProblems = 0;
1034
1035 const DiagnosticLevelMask DiagMask =
1036 Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1037
1038 // See if there are error mismatches.
1039 NumProblems += CheckLists(Diags, SourceMgr, Label: "error", Left&: ED.Errors,
1040 d2_begin: Buffer.err_begin(), d2_end: Buffer.err_end(),
1041 IgnoreUnexpected: bool(DiagnosticLevelMask::Error & DiagMask));
1042
1043 // See if there are warning mismatches.
1044 NumProblems += CheckLists(Diags, SourceMgr, Label: "warning", Left&: ED.Warnings,
1045 d2_begin: Buffer.warn_begin(), d2_end: Buffer.warn_end(),
1046 IgnoreUnexpected: bool(DiagnosticLevelMask::Warning & DiagMask));
1047
1048 // See if there are remark mismatches.
1049 NumProblems += CheckLists(Diags, SourceMgr, Label: "remark", Left&: ED.Remarks,
1050 d2_begin: Buffer.remark_begin(), d2_end: Buffer.remark_end(),
1051 IgnoreUnexpected: bool(DiagnosticLevelMask::Remark & DiagMask));
1052
1053 // See if there are note mismatches.
1054 NumProblems += CheckLists(Diags, SourceMgr, Label: "note", Left&: ED.Notes,
1055 d2_begin: Buffer.note_begin(), d2_end: Buffer.note_end(),
1056 IgnoreUnexpected: bool(DiagnosticLevelMask::Note & DiagMask));
1057
1058 return NumProblems;
1059}
1060
1061void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
1062 FileID FID,
1063 ParsedStatus PS) {
1064 // Check SourceManager hasn't changed.
1065 setSourceManager(SM);
1066
1067#ifndef NDEBUG
1068 if (FID.isInvalid())
1069 return;
1070
1071 OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID);
1072
1073 if (PS == IsParsed) {
1074 // Move the FileID from the unparsed set to the parsed set.
1075 UnparsedFiles.erase(FID);
1076 ParsedFiles.insert(std::make_pair(FID, FE ? &FE->getFileEntry() : nullptr));
1077 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
1078 // Add the FileID to the unparsed set if we haven't seen it before.
1079
1080 // Check for directives.
1081 bool FoundDirectives;
1082 if (PS == IsUnparsedNoDirectives)
1083 FoundDirectives = false;
1084 else
1085 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
1086
1087 // Add the FileID to the unparsed set.
1088 UnparsedFiles.insert(std::make_pair(FID,
1089 UnparsedFileStatus(FE, FoundDirectives)));
1090 }
1091#endif
1092}
1093
1094void VerifyDiagnosticConsumer::CheckDiagnostics() {
1095 // Ensure any diagnostics go to the primary client.
1096 DiagnosticConsumer *CurClient = Diags.getClient();
1097 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
1098 Diags.setClient(client: PrimaryClient, ShouldOwnClient: false);
1099
1100#ifndef NDEBUG
1101 // In a debug build, scan through any files that may have been missed
1102 // during parsing and issue a fatal error if directives are contained
1103 // within these files. If a fatal error occurs, this suggests that
1104 // this file is being parsed separately from the main file, in which
1105 // case consider moving the directives to the correct place, if this
1106 // is applicable.
1107 if (!UnparsedFiles.empty()) {
1108 // Generate a cache of parsed FileEntry pointers for alias lookups.
1109 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
1110 for (const auto &I : ParsedFiles)
1111 if (const FileEntry *FE = I.second)
1112 ParsedFileCache.insert(FE);
1113
1114 // Iterate through list of unparsed files.
1115 for (const auto &I : UnparsedFiles) {
1116 const UnparsedFileStatus &Status = I.second;
1117 OptionalFileEntryRef FE = Status.getFile();
1118
1119 // Skip files that have been parsed via an alias.
1120 if (FE && ParsedFileCache.count(*FE))
1121 continue;
1122
1123 // Report a fatal error if this file contained directives.
1124 if (Status.foundDirectives()) {
1125 llvm::report_fatal_error("-verify directives found after rather"
1126 " than during normal parsing of " +
1127 (FE ? FE->getName() : "(unknown)"));
1128 }
1129 }
1130
1131 // UnparsedFiles has been processed now, so clear it.
1132 UnparsedFiles.clear();
1133 }
1134#endif // !NDEBUG
1135
1136 if (SrcManager) {
1137 // Produce an error if no expected-* directives could be found in the
1138 // source file(s) processed.
1139 if (State.Status == HasNoDirectives) {
1140 Diags.Report(DiagID: diag::err_verify_no_directives).setForceEmit()
1141 << DetailedErrorString(Diags);
1142 ++NumErrors;
1143 State.Status = HasNoDirectivesReported;
1144 }
1145
1146 // Check that the expected diagnostics occurred.
1147 NumErrors += CheckResults(Diags, SourceMgr&: *SrcManager, Buffer: *Buffer, ED);
1148 } else {
1149 const DiagnosticLevelMask DiagMask =
1150 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1151 if (bool(DiagnosticLevelMask::Error & DiagMask))
1152 NumErrors += PrintUnexpected(Diags, SourceMgr: nullptr, diag_begin: Buffer->err_begin(),
1153 diag_end: Buffer->err_end(), Kind: "error");
1154 if (bool(DiagnosticLevelMask::Warning & DiagMask))
1155 NumErrors += PrintUnexpected(Diags, SourceMgr: nullptr, diag_begin: Buffer->warn_begin(),
1156 diag_end: Buffer->warn_end(), Kind: "warn");
1157 if (bool(DiagnosticLevelMask::Remark & DiagMask))
1158 NumErrors += PrintUnexpected(Diags, SourceMgr: nullptr, diag_begin: Buffer->remark_begin(),
1159 diag_end: Buffer->remark_end(), Kind: "remark");
1160 if (bool(DiagnosticLevelMask::Note & DiagMask))
1161 NumErrors += PrintUnexpected(Diags, SourceMgr: nullptr, diag_begin: Buffer->note_begin(),
1162 diag_end: Buffer->note_end(), Kind: "note");
1163 }
1164
1165 Diags.setClient(client: CurClient, ShouldOwnClient: Owner.release() != nullptr);
1166
1167 // Reset the buffer, we have processed all the diagnostics in it.
1168 Buffer.reset(p: new TextDiagnosticBuffer());
1169 ED.Reset();
1170}
1171
1172std::unique_ptr<Directive>
1173Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
1174 SourceLocation DiagnosticLoc, StringRef Spelling,
1175 bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
1176 unsigned Min, unsigned Max) {
1177 if (!RegexKind)
1178 return std::make_unique<StandardDirective>(args&: DirectiveLoc, args&: DiagnosticLoc,
1179 args&: Spelling, args&: MatchAnyFileAndLine,
1180 args&: MatchAnyLine, args&: Text, args&: Min, args&: Max);
1181
1182 // Parse the directive into a regular expression.
1183 std::string RegexStr;
1184 StringRef S = Text;
1185 while (!S.empty()) {
1186 if (S.consume_front(Prefix: "{{")) {
1187 size_t RegexMatchLength = S.find(Str: "}}");
1188 assert(RegexMatchLength != StringRef::npos);
1189 // Append the regex, enclosed in parentheses.
1190 RegexStr += "(";
1191 RegexStr.append(s: S.data(), n: RegexMatchLength);
1192 RegexStr += ")";
1193 S = S.drop_front(N: RegexMatchLength + 2);
1194 } else {
1195 size_t VerbatimMatchLength = S.find(Str: "{{");
1196 if (VerbatimMatchLength == StringRef::npos)
1197 VerbatimMatchLength = S.size();
1198 // Escape and append the fixed string.
1199 RegexStr += llvm::Regex::escape(String: S.substr(Start: 0, N: VerbatimMatchLength));
1200 S = S.drop_front(N: VerbatimMatchLength);
1201 }
1202 }
1203
1204 return std::make_unique<RegexDirective>(args&: DirectiveLoc, args&: DiagnosticLoc, args&: Spelling,
1205 args&: MatchAnyFileAndLine, args&: MatchAnyLine,
1206 args&: Text, args&: Min, args&: Max, args&: RegexStr);
1207}
1208