xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/DiagnosticOptions.h"
17 #include "clang/Basic/LLVM.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Basic/TokenKinds.h"
21 #include "clang/Frontend/FrontendDiagnostic.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 
46 using namespace clang;
47 
48 using Directive = VerifyDiagnosticConsumer::Directive;
49 using DirectiveList = VerifyDiagnosticConsumer::DirectiveList;
50 using ExpectedData = VerifyDiagnosticConsumer::ExpectedData;
51 
52 #ifndef NDEBUG
53 
54 namespace {
55 
56 class VerifyFileTracker : public PPCallbacks {
57   VerifyDiagnosticConsumer &Verify;
58   SourceManager &SM;
59 
60 public:
VerifyFileTracker(VerifyDiagnosticConsumer & Verify,SourceManager & SM)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.
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID)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 
82 using DiagList = TextDiagnosticBuffer::DiagList;
83 using const_diag_iterator = TextDiagnosticBuffer::const_iterator;
84 
85 namespace {
86 
87 /// StandardDirective - Directive with string matching.
88 class StandardDirective : public Directive {
89 public:
StandardDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,StringRef Spelling,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)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 
isValid(std::string & Error)97   bool isValid(std::string &Error) override {
98     // all strings are considered valid; even empty ones
99     return true;
100   }
101 
match(StringRef S)102   bool match(StringRef S) override { return S.contains(Text); }
103 };
104 
105 /// RegexDirective - Directive with regular-expression matching.
106 class RegexDirective : public Directive {
107 public:
RegexDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,StringRef Spelling,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max,StringRef RegexStr)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 
isValid(std::string & Error)116   bool isValid(std::string &Error) override {
117     return Regex.isValid(Error);
118   }
119 
match(StringRef S)120   bool match(StringRef S) override {
121     return Regex.match(S);
122   }
123 
124 private:
125   llvm::Regex Regex;
126 };
127 
128 class ParseHelper
129 {
130 public:
ParseHelper(StringRef S)131   ParseHelper(StringRef S)
132       : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
133 
134   // Return true if string literal is next.
Next(StringRef S)135   bool Next(StringRef S) {
136     P = C;
137     PEnd = C + S.size();
138     if (PEnd > End)
139       return false;
140     return memcmp(P, S.data(), S.size()) == 0;
141   }
142 
143   // Return true if number is next.
144   // Output N only if number is next.
Next(unsigned & N)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_-]+/.
NextMarker()161   bool NextMarker() {
162     P = C;
163     if (P == End || *P != '#')
164       return false;
165     PEnd = P;
166     ++PEnd;
167     while ((isAlphanumeric(*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.
Search(StringRef S,bool EnsureStartOfWord=false,bool FinishDirectiveToken=false)182   bool Search(StringRef S, bool EnsureStartOfWord = false,
183               bool FinishDirectiveToken = false) {
184     do {
185       if (!S.empty()) {
186         P = std::search(C, End, S.begin(), S.end());
187         PEnd = P + S.size();
188       }
189       else {
190         P = C;
191         while (P != End && !isLetter(*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(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(*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(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.
SearchClosingBrace(StringRef OpenBrace,StringRef 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(OpenBrace)) {
231         ++Depth;
232         P += OpenBrace.size();
233       } else if (S.starts_with(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.
Advance()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.
Match()256   StringRef Match() { return StringRef(P, PEnd - P); }
257 
258   // Skip zero or more whitespace.
SkipWhitespace()259   void SkipWhitespace() {
260     for (; C < End && isWhitespace(*C); ++C)
261       ;
262   }
263 
264   // Return true if EOF reached.
Done()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 
281 private:
282   // Previous next/search subject end (1-past).
283   const char *PEnd = nullptr;
284 };
285 
286 // The information necessary to create a directive.
287 struct 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.
attachDirective(DiagnosticsEngine & Diags,const UnattachedDirective & UD,SourceLocation ExpectedLoc,bool MatchAnyFileAndLine=false,bool MatchAnyLine=false)298 void 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       UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
305       MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max);
306 
307   std::string Error;
308   if (!D->isValid(Error)) {
309     Diags.Report(UD.ContentBegin, diag::err_verify_invalid_content)
310       << (UD.RegexKind ? "regex" : "string") << Error;
311   }
312 
313   UD.DL->push_back(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.
328 class 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 
342 public:
MarkerTracker(DiagnosticsEngine & Diags)343   MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {}
344 
345   // Register a marker.
addMarker(StringRef MarkerName,SourceLocation Pos)346   void addMarker(StringRef MarkerName, SourceLocation Pos) {
347     auto InsertResult = Markers.insert(
348         {MarkerName, Marker{Pos, SourceLocation(), 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(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, Pos);
362         }
363         DeferredDirectives.erase(Deferred);
364       }
365     }
366   }
367 
368   // Register a directive at the specified marker.
addDirective(StringRef MarkerName,const UnattachedDirective & UD)369   void addDirective(StringRef MarkerName, const UnattachedDirective &UD) {
370     auto MarkerIt = Markers.find(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, M.DefLoc);
376     }
377     DeferredDirectives[MarkerName].push_back(UD);
378   }
379 
380   // Ensure we have no remaining deferred directives, and no
381   // multiply-defined-and-used markers.
finalize()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(M.UseLoc, diag::err_verify_ambiguous_marker) << Name;
388         Diags.Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name;
389         Diags.Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name;
390       }
391     }
392 
393     for (auto &DeferredPair : DeferredDirectives) {
394       Diags.Report(DeferredPair.second.front().DirectivePos,
395                    diag::err_verify_no_such_marker)
396           << DeferredPair.first();
397     }
398   }
399 };
400 
DetailedErrorString(const DiagnosticsEngine & Diags)401 static 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.
ParseDirective(StringRef S,ExpectedData * ED,SourceManager & SM,Preprocessor * PP,SourceLocation Pos,VerifyDiagnosticConsumer::ParsingState & State,VerifyDiagnosticConsumer::MarkerTracker & Markers)411 static 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("#", true))
420       break;
421     PH.C = PH.P;
422     if (!PH.NextMarker()) {
423       PH.Next("#");
424       PH.Advance();
425       continue;
426     }
427     PH.Advance();
428     Markers.addMarker(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(*Prefixes.begin(), true, true)
439                                : PH.Search("", true, 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("-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(DType = "-error"))
466       D.DL = ED ? &ED->Errors : nullptr;
467     else if (DToken.ends_with(DType = "-warning"))
468       D.DL = ED ? &ED->Warnings : nullptr;
469     else if (DToken.ends_with(DType = "-remark"))
470       D.DL = ED ? &ED->Remarks : nullptr;
471     else if (DToken.ends_with(DType = "-note"))
472       D.DL = ED ? &ED->Notes : nullptr;
473     else if (DToken.ends_with(DType = "-no-diagnostics")) {
474       NoDiag = true;
475       if (D.RegexKind)
476         continue;
477     } else
478       continue;
479     DToken = DToken.substr(0, 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(Prefixes, DToken))
485       continue;
486 
487     if (NoDiag) {
488       if (State.Status ==
489           VerifyDiagnosticConsumer::HasOtherExpectedDirectives) {
490         Diags.Report(Pos, 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(Pos, 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("@")) {
518       ExpectedLoc = Pos;
519     } else {
520       PH.Advance();
521       unsigned Line = 0;
522       bool FoundPlus = PH.Next("+");
523       if (FoundPlus || PH.Next("-")) {
524         // Relative to current line.
525         PH.Advance();
526         bool Invalid = false;
527         unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
528         if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
529           if (FoundPlus) ExpectedLine += Line;
530           else ExpectedLine -= Line;
531           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
532         }
533       } else if (PH.Next(Line)) {
534         // Absolute line number.
535         if (Line > 0)
536           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
537       } else if (PH.NextMarker()) {
538         Marker = PH.Match();
539       } else if (PP && PH.Search(":")) {
540         // Specific source file.
541         StringRef Filename(PH.C, PH.P-PH.C);
542         PH.Advance();
543 
544         if (Filename == "*") {
545           MatchAnyFileAndLine = true;
546           if (!PH.Next("*")) {
547             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
548                          diag::err_verify_missing_line)
549                 << "'*'";
550             continue;
551           }
552           MatchAnyLine = true;
553           ExpectedLoc = SourceLocation();
554         } else {
555           // Lookup file via Preprocessor, like a #include.
556           OptionalFileEntryRef File =
557               PP->LookupFile(Pos, Filename, false, nullptr, nullptr, nullptr,
558                              nullptr, nullptr, nullptr, nullptr, nullptr);
559           if (!File) {
560             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
561                          diag::err_verify_missing_file)
562                 << Filename << KindStr;
563             continue;
564           }
565 
566           FileID FID = SM.translateFile(*File);
567           if (FID.isInvalid())
568             FID = SM.createFileID(*File, Pos, SrcMgr::C_User);
569 
570           if (PH.Next(Line) && Line > 0)
571             ExpectedLoc = SM.translateLineCol(FID, Line, 1);
572           else if (PH.Next("*")) {
573             MatchAnyLine = true;
574             ExpectedLoc = SM.translateLineCol(FID, 1, 1);
575           }
576         }
577       } else if (PH.Next("*")) {
578         MatchAnyLine = true;
579         ExpectedLoc = SourceLocation();
580       }
581 
582       if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) {
583         Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
584                      diag::err_verify_missing_line) << KindStr;
585         continue;
586       }
587       PH.Advance();
588     }
589 
590     // Skip optional whitespace.
591     PH.SkipWhitespace();
592 
593     // Next optional token: positive integer or a '+'.
594     if (PH.Next(D.Min)) {
595       PH.Advance();
596       // A positive integer can be followed by a '+' meaning min
597       // or more, or by a '-' meaning a range from min to max.
598       if (PH.Next("+")) {
599         D.Max = Directive::MaxCount;
600         PH.Advance();
601       } else if (PH.Next("-")) {
602         PH.Advance();
603         if (!PH.Next(D.Max) || D.Max < D.Min) {
604           Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
605                        diag::err_verify_invalid_range) << KindStr;
606           continue;
607         }
608         PH.Advance();
609       } else {
610         D.Max = D.Min;
611       }
612     } else if (PH.Next("+")) {
613       // '+' on its own means "1 or more".
614       D.Max = Directive::MaxCount;
615       PH.Advance();
616     }
617 
618     // Skip optional whitespace.
619     PH.SkipWhitespace();
620 
621     // Next token: {{
622     if (!PH.Next("{{")) {
623       Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
624                    diag::err_verify_missing_start) << KindStr;
625       continue;
626     }
627     llvm::SmallString<8> CloseBrace("}}");
628     const char *const DelimBegin = PH.C;
629     PH.Advance();
630     // Count the number of opening braces for `string` kinds
631     for (; !D.RegexKind && PH.Next("{"); PH.Advance())
632       CloseBrace += '}';
633     const char* const ContentBegin = PH.C; // mark content begin
634     // Search for closing brace
635     StringRef OpenBrace(DelimBegin, ContentBegin - DelimBegin);
636     if (!PH.SearchClosingBrace(OpenBrace, CloseBrace)) {
637       Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
638                    diag::err_verify_missing_end)
639           << KindStr << CloseBrace;
640       continue;
641     }
642     const char* const ContentEnd = PH.P; // mark content end
643     PH.Advance();
644 
645     D.DirectivePos = Pos;
646     D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin);
647 
648     // Build directive text; convert \n to newlines.
649     StringRef NewlineStr = "\\n";
650     StringRef Content(ContentBegin, ContentEnd-ContentBegin);
651     size_t CPos = 0;
652     size_t FPos;
653     while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
654       D.Text += Content.substr(CPos, FPos-CPos);
655       D.Text += '\n';
656       CPos = FPos + NewlineStr.size();
657     }
658     if (D.Text.empty())
659       D.Text.assign(ContentBegin, ContentEnd);
660 
661     // Check that regex directives contain at least one regex.
662     if (D.RegexKind && D.Text.find("{{") == StringRef::npos) {
663       Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text;
664       return false;
665     }
666 
667     if (Marker.empty())
668       attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine);
669     else
670       Markers.addDirective(Marker, D);
671     FoundDirective = true;
672   }
673 
674   return FoundDirective;
675 }
676 
VerifyDiagnosticConsumer(DiagnosticsEngine & Diags_)677 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
678     : Diags(Diags_), PrimaryClient(Diags.getClient()),
679       PrimaryClientOwner(Diags.takeClient()),
680       Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
681       State{HasNoDirectives, {}} {
682   if (Diags.hasSourceManager())
683     setSourceManager(Diags.getSourceManager());
684 }
685 
~VerifyDiagnosticConsumer()686 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
687   assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
688   assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
689   SrcManager = nullptr;
690   CheckDiagnostics();
691   assert(!Diags.ownsClient() &&
692          "The VerifyDiagnosticConsumer takes over ownership of the client!");
693 }
694 
695 // DiagnosticConsumer interface.
696 
BeginSourceFile(const LangOptions & LangOpts,const Preprocessor * PP)697 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
698                                                const Preprocessor *PP) {
699   // Attach comment handler on first invocation.
700   if (++ActiveSourceFiles == 1) {
701     if (PP) {
702       CurrentPreprocessor = PP;
703       this->LangOpts = &LangOpts;
704       setSourceManager(PP->getSourceManager());
705       const_cast<Preprocessor *>(PP)->addCommentHandler(this);
706 #ifndef NDEBUG
707       // Debug build tracks parsed files.
708       const_cast<Preprocessor *>(PP)->addPPCallbacks(
709                       std::make_unique<VerifyFileTracker>(*this, *SrcManager));
710 #endif
711     }
712   }
713 
714   assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
715   PrimaryClient->BeginSourceFile(LangOpts, PP);
716 }
717 
EndSourceFile()718 void VerifyDiagnosticConsumer::EndSourceFile() {
719   assert(ActiveSourceFiles && "No active source files!");
720   PrimaryClient->EndSourceFile();
721 
722   // Detach comment handler once last active source file completed.
723   if (--ActiveSourceFiles == 0) {
724     if (CurrentPreprocessor)
725       const_cast<Preprocessor *>(CurrentPreprocessor)->
726           removeCommentHandler(this);
727 
728     // Diagnose any used-but-not-defined markers.
729     Markers->finalize();
730 
731     // Check diagnostics once last file completed.
732     CheckDiagnostics();
733     CurrentPreprocessor = nullptr;
734     LangOpts = nullptr;
735   }
736 }
737 
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)738 void VerifyDiagnosticConsumer::HandleDiagnostic(
739       DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
740   if (Info.hasSourceManager()) {
741     // If this diagnostic is for a different source manager, ignore it.
742     if (SrcManager && &Info.getSourceManager() != SrcManager)
743       return;
744 
745     setSourceManager(Info.getSourceManager());
746   }
747 
748 #ifndef NDEBUG
749   // Debug build tracks unparsed files for possible
750   // unparsed expected-* directives.
751   if (SrcManager) {
752     SourceLocation Loc = Info.getLocation();
753     if (Loc.isValid()) {
754       ParsedStatus PS = IsUnparsed;
755 
756       Loc = SrcManager->getExpansionLoc(Loc);
757       FileID FID = SrcManager->getFileID(Loc);
758 
759       auto FE = SrcManager->getFileEntryRefForID(FID);
760       if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
761         // If the file is a modules header file it shall not be parsed
762         // for expected-* directives.
763         HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
764         if (HS.findModuleForHeader(*FE))
765           PS = IsUnparsedNoDirectives;
766       }
767 
768       UpdateParsedFileStatus(*SrcManager, FID, PS);
769     }
770   }
771 #endif
772 
773   // Send the diagnostic to the buffer, we will check it once we reach the end
774   // of the source file (or are destructed).
775   Buffer->HandleDiagnostic(DiagLevel, Info);
776 }
777 
778 /// HandleComment - Hook into the preprocessor and extract comments containing
779 ///  expected errors and warnings.
HandleComment(Preprocessor & PP,SourceRange Comment)780 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
781                                              SourceRange Comment) {
782   SourceManager &SM = PP.getSourceManager();
783 
784   // If this comment is for a different source manager, ignore it.
785   if (SrcManager && &SM != SrcManager)
786     return false;
787 
788   SourceLocation CommentBegin = Comment.getBegin();
789 
790   const char *CommentRaw = SM.getCharacterData(CommentBegin);
791   StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
792 
793   if (C.empty())
794     return false;
795 
796   // Fold any "\<EOL>" sequences
797   size_t loc = C.find('\\');
798   if (loc == StringRef::npos) {
799     ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers);
800     return false;
801   }
802 
803   std::string C2;
804   C2.reserve(C.size());
805 
806   for (size_t last = 0;; loc = C.find('\\', last)) {
807     if (loc == StringRef::npos || loc == C.size()) {
808       C2 += C.substr(last);
809       break;
810     }
811     C2 += C.substr(last, loc-last);
812     last = loc + 1;
813 
814     if (last < C.size() && (C[last] == '\n' || C[last] == '\r')) {
815       ++last;
816 
817       // Escape \r\n  or \n\r, but not \n\n.
818       if (last < C.size())
819         if (C[last] == '\n' || C[last] == '\r')
820           if (C[last] != C[last-1])
821             ++last;
822     } else {
823       // This was just a normal backslash.
824       C2 += '\\';
825     }
826   }
827 
828   if (!C2.empty())
829     ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers);
830   return false;
831 }
832 
833 #ifndef NDEBUG
834 /// Lex the specified source file to determine whether it contains
835 /// any expected-* directives.  As a Lexer is used rather than a full-blown
836 /// Preprocessor, directives inside skipped #if blocks will still be found.
837 ///
838 /// \return true if any directives were found.
findDirectives(SourceManager & SM,FileID FID,const LangOptions & LangOpts)839 static bool findDirectives(SourceManager &SM, FileID FID,
840                            const LangOptions &LangOpts) {
841   // Create a raw lexer to pull all the comments out of FID.
842   if (FID.isInvalid())
843     return false;
844 
845   // Create a lexer to lex all the tokens of the main file in raw mode.
846   llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
847   Lexer RawLex(FID, FromFile, SM, LangOpts);
848 
849   // Return comments as tokens, this is how we find expected diagnostics.
850   RawLex.SetCommentRetentionState(true);
851 
852   Token Tok;
853   Tok.setKind(tok::comment);
854   VerifyDiagnosticConsumer::ParsingState State = {
855       VerifyDiagnosticConsumer::HasNoDirectives, {}};
856   while (Tok.isNot(tok::eof)) {
857     RawLex.LexFromRawLexer(Tok);
858     if (!Tok.is(tok::comment)) continue;
859 
860     std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
861     if (Comment.empty()) continue;
862 
863     // We don't care about tracking markers for this phase.
864     VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
865 
866     // Find first directive.
867     if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
868                        Markers))
869       return true;
870   }
871   return false;
872 }
873 #endif // !NDEBUG
874 
875 /// Takes a list of diagnostics that have been generated but not matched
876 /// by an expected-* directive and produces a diagnostic to the user from this.
PrintUnexpected(DiagnosticsEngine & Diags,SourceManager * SourceMgr,const_diag_iterator diag_begin,const_diag_iterator diag_end,const char * Kind)877 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
878                                 const_diag_iterator diag_begin,
879                                 const_diag_iterator diag_end,
880                                 const char *Kind) {
881   if (diag_begin == diag_end) return 0;
882 
883   SmallString<256> Fmt;
884   llvm::raw_svector_ostream OS(Fmt);
885   for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
886     if (I->first.isInvalid() || !SourceMgr)
887       OS << "\n  (frontend)";
888     else {
889       OS << "\n ";
890       if (OptionalFileEntryRef File =
891               SourceMgr->getFileEntryRefForID(SourceMgr->getFileID(I->first)))
892         OS << " File " << File->getName();
893       OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
894     }
895     OS << ": " << I->second;
896   }
897 
898   const bool IsSinglePrefix =
899       Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
900   std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
901   Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
902       << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/true << OS.str();
903   return std::distance(diag_begin, diag_end);
904 }
905 
906 /// Takes a list of diagnostics that were expected to have been generated
907 /// but were not and produces a diagnostic to the user from this.
PrintExpected(DiagnosticsEngine & Diags,SourceManager & SourceMgr,std::vector<Directive * > & DL,const char * Kind)908 static unsigned PrintExpected(DiagnosticsEngine &Diags,
909                               SourceManager &SourceMgr,
910                               std::vector<Directive *> &DL, const char *Kind) {
911   if (DL.empty())
912     return 0;
913 
914   const bool IsSinglePrefix =
915       Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
916 
917   SmallString<256> Fmt;
918   llvm::raw_svector_ostream OS(Fmt);
919   for (const auto *D : DL) {
920     if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
921       OS << "\n  File *";
922     else
923       OS << "\n  File " << SourceMgr.getFilename(D->DiagnosticLoc);
924     if (D->MatchAnyLine)
925       OS << " Line *";
926     else
927       OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
928     if (D->DirectiveLoc != D->DiagnosticLoc)
929       OS << " (directive at "
930          << SourceMgr.getFilename(D->DirectiveLoc) << ':'
931          << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
932     if (!IsSinglePrefix)
933       OS << " \'" << D->Spelling << '\'';
934     OS << ": " << D->Text;
935   }
936 
937   std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
938   Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
939       << IsSinglePrefix << Prefix << Kind << /*Unexpected=*/false << OS.str();
940   return DL.size();
941 }
942 
943 /// Determine whether two source locations come from the same file.
IsFromSameFile(SourceManager & SM,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc)944 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
945                            SourceLocation DiagnosticLoc) {
946   while (DiagnosticLoc.isMacroID())
947     DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
948 
949   if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
950     return true;
951 
952   const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
953   if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
954     return true;
955 
956   return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
957 }
958 
959 /// CheckLists - Compare expected to seen diagnostic lists and return the
960 /// the difference between them.
CheckLists(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const char * Label,DirectiveList & Left,const_diag_iterator d2_begin,const_diag_iterator d2_end,bool IgnoreUnexpected)961 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
962                            const char *Label,
963                            DirectiveList &Left,
964                            const_diag_iterator d2_begin,
965                            const_diag_iterator d2_end,
966                            bool IgnoreUnexpected) {
967   std::vector<Directive *> LeftOnly;
968   DiagList Right(d2_begin, d2_end);
969 
970   for (auto &Owner : Left) {
971     Directive &D = *Owner;
972     unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
973 
974     for (unsigned i = 0; i < D.Max; ++i) {
975       DiagList::iterator II, IE;
976       for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
977         if (!D.MatchAnyLine) {
978           unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
979           if (LineNo1 != LineNo2)
980             continue;
981         }
982 
983         if (!D.DiagnosticLoc.isInvalid() && !D.MatchAnyFileAndLine &&
984             !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
985           continue;
986 
987         const std::string &RightText = II->second;
988         if (D.match(RightText))
989           break;
990       }
991       if (II == IE) {
992         // Not found.
993         if (i >= D.Min) break;
994         LeftOnly.push_back(&D);
995       } else {
996         // Found. The same cannot be found twice.
997         Right.erase(II);
998       }
999     }
1000   }
1001   // Now all that's left in Right are those that were not matched.
1002   unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
1003   if (!IgnoreUnexpected)
1004     num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
1005   return num;
1006 }
1007 
1008 /// CheckResults - This compares the expected results to those that
1009 /// were actually reported. It emits any discrepencies. Return "true" if there
1010 /// were problems. Return "false" otherwise.
CheckResults(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const TextDiagnosticBuffer & Buffer,ExpectedData & ED)1011 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
1012                              const TextDiagnosticBuffer &Buffer,
1013                              ExpectedData &ED) {
1014   // We want to capture the delta between what was expected and what was
1015   // seen.
1016   //
1017   //   Expected \ Seen - set expected but not seen
1018   //   Seen \ Expected - set seen but not expected
1019   unsigned NumProblems = 0;
1020 
1021   const DiagnosticLevelMask DiagMask =
1022     Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1023 
1024   // See if there are error mismatches.
1025   NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
1026                             Buffer.err_begin(), Buffer.err_end(),
1027                             bool(DiagnosticLevelMask::Error & DiagMask));
1028 
1029   // See if there are warning mismatches.
1030   NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
1031                             Buffer.warn_begin(), Buffer.warn_end(),
1032                             bool(DiagnosticLevelMask::Warning & DiagMask));
1033 
1034   // See if there are remark mismatches.
1035   NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
1036                             Buffer.remark_begin(), Buffer.remark_end(),
1037                             bool(DiagnosticLevelMask::Remark & DiagMask));
1038 
1039   // See if there are note mismatches.
1040   NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
1041                             Buffer.note_begin(), Buffer.note_end(),
1042                             bool(DiagnosticLevelMask::Note & DiagMask));
1043 
1044   return NumProblems;
1045 }
1046 
UpdateParsedFileStatus(SourceManager & SM,FileID FID,ParsedStatus PS)1047 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
1048                                                       FileID FID,
1049                                                       ParsedStatus PS) {
1050   // Check SourceManager hasn't changed.
1051   setSourceManager(SM);
1052 
1053 #ifndef NDEBUG
1054   if (FID.isInvalid())
1055     return;
1056 
1057   OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID);
1058 
1059   if (PS == IsParsed) {
1060     // Move the FileID from the unparsed set to the parsed set.
1061     UnparsedFiles.erase(FID);
1062     ParsedFiles.insert(std::make_pair(FID, FE ? &FE->getFileEntry() : nullptr));
1063   } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
1064     // Add the FileID to the unparsed set if we haven't seen it before.
1065 
1066     // Check for directives.
1067     bool FoundDirectives;
1068     if (PS == IsUnparsedNoDirectives)
1069       FoundDirectives = false;
1070     else
1071       FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
1072 
1073     // Add the FileID to the unparsed set.
1074     UnparsedFiles.insert(std::make_pair(FID,
1075                                       UnparsedFileStatus(FE, FoundDirectives)));
1076   }
1077 #endif
1078 }
1079 
CheckDiagnostics()1080 void VerifyDiagnosticConsumer::CheckDiagnostics() {
1081   // Ensure any diagnostics go to the primary client.
1082   DiagnosticConsumer *CurClient = Diags.getClient();
1083   std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
1084   Diags.setClient(PrimaryClient, false);
1085 
1086 #ifndef NDEBUG
1087   // In a debug build, scan through any files that may have been missed
1088   // during parsing and issue a fatal error if directives are contained
1089   // within these files.  If a fatal error occurs, this suggests that
1090   // this file is being parsed separately from the main file, in which
1091   // case consider moving the directives to the correct place, if this
1092   // is applicable.
1093   if (!UnparsedFiles.empty()) {
1094     // Generate a cache of parsed FileEntry pointers for alias lookups.
1095     llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
1096     for (const auto &I : ParsedFiles)
1097       if (const FileEntry *FE = I.second)
1098         ParsedFileCache.insert(FE);
1099 
1100     // Iterate through list of unparsed files.
1101     for (const auto &I : UnparsedFiles) {
1102       const UnparsedFileStatus &Status = I.second;
1103       OptionalFileEntryRef FE = Status.getFile();
1104 
1105       // Skip files that have been parsed via an alias.
1106       if (FE && ParsedFileCache.count(*FE))
1107         continue;
1108 
1109       // Report a fatal error if this file contained directives.
1110       if (Status.foundDirectives()) {
1111         llvm::report_fatal_error("-verify directives found after rather"
1112                                  " than during normal parsing of " +
1113                                  (FE ? FE->getName() : "(unknown)"));
1114       }
1115     }
1116 
1117     // UnparsedFiles has been processed now, so clear it.
1118     UnparsedFiles.clear();
1119   }
1120 #endif // !NDEBUG
1121 
1122   if (SrcManager) {
1123     // Produce an error if no expected-* directives could be found in the
1124     // source file(s) processed.
1125     if (State.Status == HasNoDirectives) {
1126       Diags.Report(diag::err_verify_no_directives).setForceEmit()
1127           << DetailedErrorString(Diags);
1128       ++NumErrors;
1129       State.Status = HasNoDirectivesReported;
1130     }
1131 
1132     // Check that the expected diagnostics occurred.
1133     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
1134   } else {
1135     const DiagnosticLevelMask DiagMask =
1136         ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1137     if (bool(DiagnosticLevelMask::Error & DiagMask))
1138       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
1139                                    Buffer->err_end(), "error");
1140     if (bool(DiagnosticLevelMask::Warning & DiagMask))
1141       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
1142                                    Buffer->warn_end(), "warn");
1143     if (bool(DiagnosticLevelMask::Remark & DiagMask))
1144       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
1145                                    Buffer->remark_end(), "remark");
1146     if (bool(DiagnosticLevelMask::Note & DiagMask))
1147       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
1148                                    Buffer->note_end(), "note");
1149   }
1150 
1151   Diags.setClient(CurClient, Owner.release() != nullptr);
1152 
1153   // Reset the buffer, we have processed all the diagnostics in it.
1154   Buffer.reset(new TextDiagnosticBuffer());
1155   ED.Reset();
1156 }
1157 
1158 std::unique_ptr<Directive>
create(bool RegexKind,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,StringRef Spelling,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)1159 Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
1160                   SourceLocation DiagnosticLoc, StringRef Spelling,
1161                   bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
1162                   unsigned Min, unsigned Max) {
1163   if (!RegexKind)
1164     return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
1165                                                Spelling, MatchAnyFileAndLine,
1166                                                MatchAnyLine, Text, Min, Max);
1167 
1168   // Parse the directive into a regular expression.
1169   std::string RegexStr;
1170   StringRef S = Text;
1171   while (!S.empty()) {
1172     if (S.consume_front("{{")) {
1173       size_t RegexMatchLength = S.find("}}");
1174       assert(RegexMatchLength != StringRef::npos);
1175       // Append the regex, enclosed in parentheses.
1176       RegexStr += "(";
1177       RegexStr.append(S.data(), RegexMatchLength);
1178       RegexStr += ")";
1179       S = S.drop_front(RegexMatchLength + 2);
1180     } else {
1181       size_t VerbatimMatchLength = S.find("{{");
1182       if (VerbatimMatchLength == StringRef::npos)
1183         VerbatimMatchLength = S.size();
1184       // Escape and append the fixed string.
1185       RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
1186       S = S.drop_front(VerbatimMatchLength);
1187     }
1188   }
1189 
1190   return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc, Spelling,
1191                                           MatchAnyFileAndLine, MatchAnyLine,
1192                                           Text, Min, Max, RegexStr);
1193 }
1194