1341825Sdim//===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===//
2226586Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6226586Sdim//
7226586Sdim//===----------------------------------------------------------------------===//
8226586Sdim//
9226586Sdim// This is a concrete diagnostic client, which buffers the diagnostic messages.
10226586Sdim//
11226586Sdim//===----------------------------------------------------------------------===//
12226586Sdim
13249423Sdim#include "clang/Frontend/VerifyDiagnosticConsumer.h"
14249423Sdim#include "clang/Basic/CharInfo.h"
15341825Sdim#include "clang/Basic/Diagnostic.h"
16341825Sdim#include "clang/Basic/DiagnosticOptions.h"
17239462Sdim#include "clang/Basic/FileManager.h"
18341825Sdim#include "clang/Basic/LLVM.h"
19341825Sdim#include "clang/Basic/SourceLocation.h"
20341825Sdim#include "clang/Basic/SourceManager.h"
21341825Sdim#include "clang/Basic/TokenKinds.h"
22226586Sdim#include "clang/Frontend/FrontendDiagnostic.h"
23226586Sdim#include "clang/Frontend/TextDiagnosticBuffer.h"
24239462Sdim#include "clang/Lex/HeaderSearch.h"
25341825Sdim#include "clang/Lex/Lexer.h"
26341825Sdim#include "clang/Lex/PPCallbacks.h"
27226586Sdim#include "clang/Lex/Preprocessor.h"
28341825Sdim#include "clang/Lex/Token.h"
29341825Sdim#include "llvm/ADT/STLExtras.h"
30341825Sdim#include "llvm/ADT/SmallPtrSet.h"
31226586Sdim#include "llvm/ADT/SmallString.h"
32341825Sdim#include "llvm/ADT/StringRef.h"
33341825Sdim#include "llvm/ADT/Twine.h"
34341825Sdim#include "llvm/Support/ErrorHandling.h"
35226586Sdim#include "llvm/Support/Regex.h"
36226586Sdim#include "llvm/Support/raw_ostream.h"
37341825Sdim#include <algorithm>
38341825Sdim#include <cassert>
39341825Sdim#include <cstddef>
40341825Sdim#include <cstring>
41341825Sdim#include <iterator>
42341825Sdim#include <memory>
43341825Sdim#include <string>
44341825Sdim#include <utility>
45341825Sdim#include <vector>
46234353Sdim
47226586Sdimusing namespace clang;
48226586Sdim
49341825Sdimusing Directive = VerifyDiagnosticConsumer::Directive;
50341825Sdimusing DirectiveList = VerifyDiagnosticConsumer::DirectiveList;
51341825Sdimusing ExpectedData = VerifyDiagnosticConsumer::ExpectedData;
52341825Sdim
53239462Sdim#ifndef NDEBUG
54341825Sdim
55239462Sdimnamespace {
56341825Sdim
57239462Sdimclass VerifyFileTracker : public PPCallbacks {
58243830Sdim  VerifyDiagnosticConsumer &Verify;
59239462Sdim  SourceManager &SM;
60239462Sdim
61239462Sdimpublic:
62243830Sdim  VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
63341825Sdim      : Verify(Verify), SM(SM) {}
64239462Sdim
65341825Sdim  /// Hook into the preprocessor and update the list of parsed
66239462Sdim  /// files when the preprocessor indicates a new file is entered.
67288943Sdim  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
68288943Sdim                   SrcMgr::CharacteristicKind FileType,
69288943Sdim                   FileID PrevFID) override {
70243830Sdim    Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
71243830Sdim                                  VerifyDiagnosticConsumer::IsParsed);
72239462Sdim  }
73239462Sdim};
74341825Sdim
75341825Sdim} // namespace
76341825Sdim
77239462Sdim#endif
78239462Sdim
79226586Sdim//===----------------------------------------------------------------------===//
80226586Sdim// Checking diagnostics implementation.
81226586Sdim//===----------------------------------------------------------------------===//
82226586Sdim
83341825Sdimusing DiagList = TextDiagnosticBuffer::DiagList;
84341825Sdimusing const_diag_iterator = TextDiagnosticBuffer::const_iterator;
85226586Sdim
86226586Sdimnamespace {
87226586Sdim
88226586Sdim/// StandardDirective - Directive with string matching.
89226586Sdimclass StandardDirective : public Directive {
90226586Sdimpublic:
91239462Sdim  StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
92276479Sdim                    bool MatchAnyLine, StringRef Text, unsigned Min,
93276479Sdim                    unsigned Max)
94341825Sdim      : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {}
95226586Sdim
96276479Sdim  bool isValid(std::string &Error) override {
97226586Sdim    // all strings are considered valid; even empty ones
98226586Sdim    return true;
99226586Sdim  }
100226586Sdim
101276479Sdim  bool match(StringRef S) override {
102239462Sdim    return S.find(Text) != StringRef::npos;
103226586Sdim  }
104226586Sdim};
105226586Sdim
106226586Sdim/// RegexDirective - Directive with regular-expression matching.
107226586Sdimclass RegexDirective : public Directive {
108226586Sdimpublic:
109239462Sdim  RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
110276479Sdim                 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
111276479Sdim                 StringRef RegexStr)
112341825Sdim      : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
113341825Sdim        Regex(RegexStr) {}
114226586Sdim
115276479Sdim  bool isValid(std::string &Error) override {
116296417Sdim    return Regex.isValid(Error);
117226586Sdim  }
118226586Sdim
119276479Sdim  bool match(StringRef S) override {
120226586Sdim    return Regex.match(S);
121226586Sdim  }
122226586Sdim
123226586Sdimprivate:
124226586Sdim  llvm::Regex Regex;
125226586Sdim};
126226586Sdim
127226586Sdimclass ParseHelper
128226586Sdim{
129226586Sdimpublic:
130239462Sdim  ParseHelper(StringRef S)
131341825Sdim      : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
132226586Sdim
133226586Sdim  // Return true if string literal is next.
134226586Sdim  bool Next(StringRef S) {
135226586Sdim    P = C;
136226586Sdim    PEnd = C + S.size();
137226586Sdim    if (PEnd > End)
138226586Sdim      return false;
139341825Sdim    return memcmp(P, S.data(), S.size()) == 0;
140226586Sdim  }
141226586Sdim
142226586Sdim  // Return true if number is next.
143226586Sdim  // Output N only if number is next.
144226586Sdim  bool Next(unsigned &N) {
145226586Sdim    unsigned TMP = 0;
146226586Sdim    P = C;
147353358Sdim    PEnd = P;
148353358Sdim    for (; PEnd < End && *PEnd >= '0' && *PEnd <= '9'; ++PEnd) {
149226586Sdim      TMP *= 10;
150353358Sdim      TMP += *PEnd - '0';
151226586Sdim    }
152353358Sdim    if (PEnd == C)
153226586Sdim      return false;
154226586Sdim    N = TMP;
155226586Sdim    return true;
156226586Sdim  }
157226586Sdim
158353358Sdim  // Return true if a marker is next.
159353358Sdim  // A marker is the longest match for /#[A-Za-z0-9_-]+/.
160353358Sdim  bool NextMarker() {
161353358Sdim    P = C;
162353358Sdim    if (P == End || *P != '#')
163353358Sdim      return false;
164353358Sdim    PEnd = P;
165353358Sdim    ++PEnd;
166353358Sdim    while ((isAlphanumeric(*PEnd) || *PEnd == '-' || *PEnd == '_') &&
167353358Sdim           PEnd < End)
168353358Sdim      ++PEnd;
169353358Sdim    return PEnd > P + 1;
170353358Sdim  }
171353358Sdim
172327952Sdim  // Return true if string literal S is matched in content.
173327952Sdim  // When true, P marks begin-position of the match, and calling Advance sets C
174327952Sdim  // to end-position of the match.
175327952Sdim  // If S is the empty string, then search for any letter instead (makes sense
176327952Sdim  // with FinishDirectiveToken=true).
177327952Sdim  // If EnsureStartOfWord, then skip matches that don't start a new word.
178327952Sdim  // If FinishDirectiveToken, then assume the match is the start of a comment
179327952Sdim  // directive for -verify, and extend the match to include the entire first
180327952Sdim  // token of that directive.
181327952Sdim  bool Search(StringRef S, bool EnsureStartOfWord = false,
182327952Sdim              bool FinishDirectiveToken = false) {
183243830Sdim    do {
184327952Sdim      if (!S.empty()) {
185327952Sdim        P = std::search(C, End, S.begin(), S.end());
186327952Sdim        PEnd = P + S.size();
187327952Sdim      }
188327952Sdim      else {
189327952Sdim        P = C;
190327952Sdim        while (P != End && !isLetter(*P))
191327952Sdim          ++P;
192327952Sdim        PEnd = P + 1;
193327952Sdim      }
194243830Sdim      if (P == End)
195243830Sdim        break;
196327952Sdim      // If not start of word but required, skip and search again.
197327952Sdim      if (EnsureStartOfWord
198327952Sdim               // Check if string literal starts a new word.
199327952Sdim          && !(P == Begin || isWhitespace(P[-1])
200327952Sdim               // Or it could be preceded by the start of a comment.
201327952Sdim               || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
202327952Sdim                                   &&  P[-2] == '/')))
203327952Sdim        continue;
204327952Sdim      if (FinishDirectiveToken) {
205327952Sdim        while (PEnd != End && (isAlphanumeric(*PEnd)
206327952Sdim                               || *PEnd == '-' || *PEnd == '_'))
207327952Sdim          ++PEnd;
208327952Sdim        // Put back trailing digits and hyphens to be parsed later as a count
209327952Sdim        // or count range.  Because -verify prefixes must start with letters,
210327952Sdim        // we know the actual directive we found starts with a letter, so
211327952Sdim        // we won't put back the entire directive word and thus record an empty
212327952Sdim        // string.
213327952Sdim        assert(isLetter(*P) && "-verify prefix must start with a letter");
214327952Sdim        while (isDigit(PEnd[-1]) || PEnd[-1] == '-')
215327952Sdim          --PEnd;
216327952Sdim      }
217327952Sdim      return true;
218243830Sdim    } while (Advance());
219243830Sdim    return false;
220226586Sdim  }
221226586Sdim
222276479Sdim  // Return true if a CloseBrace that closes the OpenBrace at the current nest
223276479Sdim  // level is found. When true, P marks begin-position of CloseBrace.
224276479Sdim  bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
225276479Sdim    unsigned Depth = 1;
226276479Sdim    P = C;
227276479Sdim    while (P < End) {
228276479Sdim      StringRef S(P, End - P);
229276479Sdim      if (S.startswith(OpenBrace)) {
230276479Sdim        ++Depth;
231276479Sdim        P += OpenBrace.size();
232276479Sdim      } else if (S.startswith(CloseBrace)) {
233276479Sdim        --Depth;
234276479Sdim        if (Depth == 0) {
235276479Sdim          PEnd = P + CloseBrace.size();
236276479Sdim          return true;
237276479Sdim        }
238276479Sdim        P += CloseBrace.size();
239276479Sdim      } else {
240276479Sdim        ++P;
241276479Sdim      }
242276479Sdim    }
243276479Sdim    return false;
244276479Sdim  }
245276479Sdim
246226586Sdim  // Advance 1-past previous next/search.
247226586Sdim  // Behavior is undefined if previous next/search failed.
248226586Sdim  bool Advance() {
249226586Sdim    C = PEnd;
250226586Sdim    return C < End;
251226586Sdim  }
252226586Sdim
253353358Sdim  // Return the text matched by the previous next/search.
254353358Sdim  // Behavior is undefined if previous next/search failed.
255353358Sdim  StringRef Match() { return StringRef(P, PEnd - P); }
256353358Sdim
257226586Sdim  // Skip zero or more whitespace.
258226586Sdim  void SkipWhitespace() {
259249423Sdim    for (; C < End && isWhitespace(*C); ++C)
260226586Sdim      ;
261226586Sdim  }
262226586Sdim
263226586Sdim  // Return true if EOF reached.
264226586Sdim  bool Done() {
265226586Sdim    return !(C < End);
266226586Sdim  }
267226586Sdim
268341825Sdim  // Beginning of expected content.
269341825Sdim  const char * const Begin;
270341825Sdim
271341825Sdim  // End of expected content (1-past).
272341825Sdim  const char * const End;
273341825Sdim
274341825Sdim  // Position of next char in content.
275341825Sdim  const char *C;
276341825Sdim
277353358Sdim  // Previous next/search subject start.
278226586Sdim  const char *P;
279226586Sdim
280226586Sdimprivate:
281341825Sdim  // Previous next/search subject end (1-past).
282341825Sdim  const char *PEnd = nullptr;
283226586Sdim};
284226586Sdim
285353358Sdim// The information necessary to create a directive.
286353358Sdimstruct UnattachedDirective {
287353358Sdim  DirectiveList *DL = nullptr;
288353358Sdim  bool RegexKind = false;
289353358Sdim  SourceLocation DirectivePos, ContentBegin;
290353358Sdim  std::string Text;
291353358Sdim  unsigned Min = 1, Max = 1;
292353358Sdim};
293353358Sdim
294353358Sdim// Attach the specified directive to the line of code indicated by
295353358Sdim// \p ExpectedLoc.
296353358Sdimvoid attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
297353358Sdim                     SourceLocation ExpectedLoc, bool MatchAnyLine = false) {
298353358Sdim  // Construct new directive.
299353358Sdim  std::unique_ptr<Directive> D =
300353358Sdim      Directive::create(UD.RegexKind, UD.DirectivePos, ExpectedLoc,
301353358Sdim                        MatchAnyLine, UD.Text, UD.Min, UD.Max);
302353358Sdim
303353358Sdim  std::string Error;
304353358Sdim  if (!D->isValid(Error)) {
305353358Sdim    Diags.Report(UD.ContentBegin, diag::err_verify_invalid_content)
306353358Sdim      << (UD.RegexKind ? "regex" : "string") << Error;
307353358Sdim  }
308353358Sdim
309353358Sdim  UD.DL->push_back(std::move(D));
310353358Sdim}
311353358Sdim
312341825Sdim} // anonymous
313226586Sdim
314353358Sdim// Tracker for markers in the input files. A marker is a comment of the form
315353358Sdim//
316353358Sdim//   n = 123; // #123
317353358Sdim//
318353358Sdim// ... that can be referred to by a later expected-* directive:
319353358Sdim//
320353358Sdim//   // expected-error@#123 {{undeclared identifier 'n'}}
321353358Sdim//
322353358Sdim// Marker declarations must be at the start of a comment or preceded by
323353358Sdim// whitespace to distinguish them from uses of markers in directives.
324353358Sdimclass VerifyDiagnosticConsumer::MarkerTracker {
325353358Sdim  DiagnosticsEngine &Diags;
326353358Sdim
327353358Sdim  struct Marker {
328353358Sdim    SourceLocation DefLoc;
329353358Sdim    SourceLocation RedefLoc;
330353358Sdim    SourceLocation UseLoc;
331353358Sdim  };
332353358Sdim  llvm::StringMap<Marker> Markers;
333353358Sdim
334353358Sdim  // Directives that couldn't be created yet because they name an unknown
335353358Sdim  // marker.
336353358Sdim  llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives;
337353358Sdim
338353358Sdimpublic:
339353358Sdim  MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {}
340353358Sdim
341353358Sdim  // Register a marker.
342353358Sdim  void addMarker(StringRef MarkerName, SourceLocation Pos) {
343353358Sdim    auto InsertResult = Markers.insert(
344353358Sdim        {MarkerName, Marker{Pos, SourceLocation(), SourceLocation()}});
345353358Sdim
346353358Sdim    Marker &M = InsertResult.first->second;
347353358Sdim    if (!InsertResult.second) {
348353358Sdim      // Marker was redefined.
349353358Sdim      M.RedefLoc = Pos;
350353358Sdim    } else {
351353358Sdim      // First definition: build any deferred directives.
352353358Sdim      auto Deferred = DeferredDirectives.find(MarkerName);
353353358Sdim      if (Deferred != DeferredDirectives.end()) {
354353358Sdim        for (auto &UD : Deferred->second) {
355353358Sdim          if (M.UseLoc.isInvalid())
356353358Sdim            M.UseLoc = UD.DirectivePos;
357353358Sdim          attachDirective(Diags, UD, Pos);
358353358Sdim        }
359353358Sdim        DeferredDirectives.erase(Deferred);
360353358Sdim      }
361353358Sdim    }
362353358Sdim  }
363353358Sdim
364353358Sdim  // Register a directive at the specified marker.
365353358Sdim  void addDirective(StringRef MarkerName, const UnattachedDirective &UD) {
366353358Sdim    auto MarkerIt = Markers.find(MarkerName);
367353358Sdim    if (MarkerIt != Markers.end()) {
368353358Sdim      Marker &M = MarkerIt->second;
369353358Sdim      if (M.UseLoc.isInvalid())
370353358Sdim        M.UseLoc = UD.DirectivePos;
371353358Sdim      return attachDirective(Diags, UD, M.DefLoc);
372353358Sdim    }
373353358Sdim    DeferredDirectives[MarkerName].push_back(UD);
374353358Sdim  }
375353358Sdim
376353358Sdim  // Ensure we have no remaining deferred directives, and no
377353358Sdim  // multiply-defined-and-used markers.
378353358Sdim  void finalize() {
379353358Sdim    for (auto &MarkerInfo : Markers) {
380353358Sdim      StringRef Name = MarkerInfo.first();
381353358Sdim      Marker &M = MarkerInfo.second;
382353358Sdim      if (M.RedefLoc.isValid() && M.UseLoc.isValid()) {
383353358Sdim        Diags.Report(M.UseLoc, diag::err_verify_ambiguous_marker) << Name;
384353358Sdim        Diags.Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name;
385353358Sdim        Diags.Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name;
386353358Sdim      }
387353358Sdim    }
388353358Sdim
389353358Sdim    for (auto &DeferredPair : DeferredDirectives) {
390353358Sdim      Diags.Report(DeferredPair.second.front().DirectivePos,
391353358Sdim                   diag::err_verify_no_such_marker)
392353358Sdim          << DeferredPair.first();
393353358Sdim    }
394353358Sdim  }
395353358Sdim};
396353358Sdim
397226586Sdim/// ParseDirective - Go through the comment and see if it indicates expected
398226586Sdim/// diagnostics. If so, then put them in the appropriate directive list.
399226586Sdim///
400239462Sdim/// Returns true if any valid directives were found.
401239462Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
402251662Sdim                           Preprocessor *PP, SourceLocation Pos,
403353358Sdim                           VerifyDiagnosticConsumer::DirectiveStatus &Status,
404353358Sdim                           VerifyDiagnosticConsumer::MarkerTracker &Markers) {
405251662Sdim  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
406251662Sdim
407353358Sdim  // First, scan the comment looking for markers.
408353358Sdim  for (ParseHelper PH(S); !PH.Done();) {
409353358Sdim    if (!PH.Search("#", true))
410353358Sdim      break;
411353358Sdim    PH.C = PH.P;
412353358Sdim    if (!PH.NextMarker()) {
413353358Sdim      PH.Next("#");
414353358Sdim      PH.Advance();
415353358Sdim      continue;
416353358Sdim    }
417353358Sdim    PH.Advance();
418353358Sdim    Markers.addMarker(PH.Match(), Pos);
419353358Sdim  }
420353358Sdim
421226586Sdim  // A single comment may contain multiple directives.
422239462Sdim  bool FoundDirective = false;
423239462Sdim  for (ParseHelper PH(S); !PH.Done();) {
424327952Sdim    // Search for the initial directive token.
425327952Sdim    // If one prefix, save time by searching only for its directives.
426327952Sdim    // Otherwise, search for any potential directive token and check it later.
427327952Sdim    const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes;
428327952Sdim    if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true)
429327952Sdim                               : PH.Search("", true, true)))
430226586Sdim      break;
431353358Sdim
432353358Sdim    StringRef DToken = PH.Match();
433226586Sdim    PH.Advance();
434226586Sdim
435327952Sdim    // Default directive kind.
436353358Sdim    UnattachedDirective D;
437353358Sdim    const char *KindStr = "string";
438226586Sdim
439327952Sdim    // Parse the initial directive token in reverse so we can easily determine
440327952Sdim    // its exact actual prefix.  If we were to parse it from the front instead,
441327952Sdim    // it would be harder to determine where the prefix ends because there
442327952Sdim    // might be multiple matching -verify prefixes because some might prefix
443327952Sdim    // others.
444327952Sdim
445327952Sdim    // Regex in initial directive token: -re
446327952Sdim    if (DToken.endswith("-re")) {
447353358Sdim      D.RegexKind = true;
448327952Sdim      KindStr = "regex";
449327952Sdim      DToken = DToken.substr(0, DToken.size()-3);
450327952Sdim    }
451327952Sdim
452327952Sdim    // Type in initial directive token: -{error|warning|note|no-diagnostics}
453327952Sdim    bool NoDiag = false;
454327952Sdim    StringRef DType;
455327952Sdim    if (DToken.endswith(DType="-error"))
456353358Sdim      D.DL = ED ? &ED->Errors : nullptr;
457327952Sdim    else if (DToken.endswith(DType="-warning"))
458353358Sdim      D.DL = ED ? &ED->Warnings : nullptr;
459327952Sdim    else if (DToken.endswith(DType="-remark"))
460353358Sdim      D.DL = ED ? &ED->Remarks : nullptr;
461327952Sdim    else if (DToken.endswith(DType="-note"))
462353358Sdim      D.DL = ED ? &ED->Notes : nullptr;
463327952Sdim    else if (DToken.endswith(DType="-no-diagnostics")) {
464327952Sdim      NoDiag = true;
465353358Sdim      if (D.RegexKind)
466327952Sdim        continue;
467327952Sdim    }
468327952Sdim    else
469327952Sdim      continue;
470327952Sdim    DToken = DToken.substr(0, DToken.size()-DType.size());
471327952Sdim
472327952Sdim    // What's left in DToken is the actual prefix.  That might not be a -verify
473327952Sdim    // prefix even if there is only one -verify prefix (for example, the full
474327952Sdim    // DToken is foo-bar-warning, but foo is the only -verify prefix).
475327952Sdim    if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
476327952Sdim      continue;
477327952Sdim
478327952Sdim    if (NoDiag) {
479243830Sdim      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
480243830Sdim        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
481243830Sdim          << /*IsExpectedNoDiagnostics=*/true;
482243830Sdim      else
483243830Sdim        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
484226586Sdim      continue;
485327952Sdim    }
486243830Sdim    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
487243830Sdim      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
488243830Sdim        << /*IsExpectedNoDiagnostics=*/false;
489243830Sdim      continue;
490243830Sdim    }
491243830Sdim    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
492243830Sdim
493239462Sdim    // If a directive has been found but we're not interested
494239462Sdim    // in storing the directive information, return now.
495353358Sdim    if (!D.DL)
496239462Sdim      return true;
497239462Sdim
498239462Sdim    // Next optional token: @
499239462Sdim    SourceLocation ExpectedLoc;
500353358Sdim    StringRef Marker;
501276479Sdim    bool MatchAnyLine = false;
502239462Sdim    if (!PH.Next("@")) {
503239462Sdim      ExpectedLoc = Pos;
504239462Sdim    } else {
505239462Sdim      PH.Advance();
506239462Sdim      unsigned Line = 0;
507239462Sdim      bool FoundPlus = PH.Next("+");
508239462Sdim      if (FoundPlus || PH.Next("-")) {
509239462Sdim        // Relative to current line.
510239462Sdim        PH.Advance();
511239462Sdim        bool Invalid = false;
512239462Sdim        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
513239462Sdim        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
514239462Sdim          if (FoundPlus) ExpectedLine += Line;
515239462Sdim          else ExpectedLine -= Line;
516239462Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
517239462Sdim        }
518251662Sdim      } else if (PH.Next(Line)) {
519239462Sdim        // Absolute line number.
520251662Sdim        if (Line > 0)
521251662Sdim          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
522353358Sdim      } else if (PH.NextMarker()) {
523353358Sdim        Marker = PH.Match();
524251662Sdim      } else if (PP && PH.Search(":")) {
525251662Sdim        // Specific source file.
526251662Sdim        StringRef Filename(PH.C, PH.P-PH.C);
527251662Sdim        PH.Advance();
528251662Sdim
529251662Sdim        // Lookup file via Preprocessor, like a #include.
530251662Sdim        const DirectoryLookup *CurDir;
531360784Sdim        Optional<FileEntryRef> File =
532280031Sdim            PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
533353358Sdim                           nullptr, nullptr, nullptr, nullptr, nullptr);
534360784Sdim        if (!File) {
535251662Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
536251662Sdim                       diag::err_verify_missing_file) << Filename << KindStr;
537251662Sdim          continue;
538251662Sdim        }
539251662Sdim
540360784Sdim        const FileEntry *FE = &File->getFileEntry();
541251662Sdim        if (SM.translateFile(FE).isInvalid())
542251662Sdim          SM.createFileID(FE, Pos, SrcMgr::C_User);
543251662Sdim
544239462Sdim        if (PH.Next(Line) && Line > 0)
545251662Sdim          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
546276479Sdim        else if (PH.Next("*")) {
547276479Sdim          MatchAnyLine = true;
548276479Sdim          ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
549276479Sdim        }
550327952Sdim      } else if (PH.Next("*")) {
551327952Sdim        MatchAnyLine = true;
552327952Sdim        ExpectedLoc = SourceLocation();
553239462Sdim      }
554239462Sdim
555353358Sdim      if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) {
556239462Sdim        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
557239462Sdim                     diag::err_verify_missing_line) << KindStr;
558239462Sdim        continue;
559239462Sdim      }
560239462Sdim      PH.Advance();
561239462Sdim    }
562239462Sdim
563239462Sdim    // Skip optional whitespace.
564226586Sdim    PH.SkipWhitespace();
565226586Sdim
566239462Sdim    // Next optional token: positive integer or a '+'.
567353358Sdim    if (PH.Next(D.Min)) {
568226586Sdim      PH.Advance();
569239462Sdim      // A positive integer can be followed by a '+' meaning min
570239462Sdim      // or more, or by a '-' meaning a range from min to max.
571239462Sdim      if (PH.Next("+")) {
572353358Sdim        D.Max = Directive::MaxCount;
573239462Sdim        PH.Advance();
574239462Sdim      } else if (PH.Next("-")) {
575239462Sdim        PH.Advance();
576353358Sdim        if (!PH.Next(D.Max) || D.Max < D.Min) {
577239462Sdim          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
578239462Sdim                       diag::err_verify_invalid_range) << KindStr;
579239462Sdim          continue;
580239462Sdim        }
581239462Sdim        PH.Advance();
582239462Sdim      } else {
583353358Sdim        D.Max = D.Min;
584239462Sdim      }
585239462Sdim    } else if (PH.Next("+")) {
586239462Sdim      // '+' on its own means "1 or more".
587353358Sdim      D.Max = Directive::MaxCount;
588234353Sdim      PH.Advance();
589234353Sdim    }
590226586Sdim
591239462Sdim    // Skip optional whitespace.
592226586Sdim    PH.SkipWhitespace();
593226586Sdim
594239462Sdim    // Next token: {{
595226586Sdim    if (!PH.Next("{{")) {
596239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
597239462Sdim                   diag::err_verify_missing_start) << KindStr;
598226586Sdim      continue;
599226586Sdim    }
600226586Sdim    PH.Advance();
601226586Sdim    const char* const ContentBegin = PH.C; // mark content begin
602239462Sdim    // Search for token: }}
603276479Sdim    if (!PH.SearchClosingBrace("{{", "}}")) {
604239462Sdim      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
605239462Sdim                   diag::err_verify_missing_end) << KindStr;
606226586Sdim      continue;
607226586Sdim    }
608226586Sdim    const char* const ContentEnd = PH.P; // mark content end
609226586Sdim    PH.Advance();
610226586Sdim
611353358Sdim    D.DirectivePos = Pos;
612353358Sdim    D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin);
613353358Sdim
614239462Sdim    // Build directive text; convert \n to newlines.
615226586Sdim    StringRef NewlineStr = "\\n";
616226586Sdim    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
617226586Sdim    size_t CPos = 0;
618226586Sdim    size_t FPos;
619226586Sdim    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
620353358Sdim      D.Text += Content.substr(CPos, FPos-CPos);
621353358Sdim      D.Text += '\n';
622226586Sdim      CPos = FPos + NewlineStr.size();
623226586Sdim    }
624353358Sdim    if (D.Text.empty())
625353358Sdim      D.Text.assign(ContentBegin, ContentEnd);
626226586Sdim
627276479Sdim    // Check that regex directives contain at least one regex.
628353358Sdim    if (D.RegexKind && D.Text.find("{{") == StringRef::npos) {
629353358Sdim      Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text;
630276479Sdim      return false;
631276479Sdim    }
632276479Sdim
633353358Sdim    if (Marker.empty())
634353358Sdim      attachDirective(Diags, D, ExpectedLoc, MatchAnyLine);
635353358Sdim    else
636353358Sdim      Markers.addDirective(Marker, D);
637353358Sdim    FoundDirective = true;
638353358Sdim  }
639276479Sdim
640353358Sdim  return FoundDirective;
641353358Sdim}
642353358Sdim
643353358SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
644353358Sdim    : Diags(Diags_), PrimaryClient(Diags.getClient()),
645353358Sdim      PrimaryClientOwner(Diags.takeClient()),
646353358Sdim      Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
647353358Sdim      Status(HasNoDirectives) {
648353358Sdim  if (Diags.hasSourceManager())
649353358Sdim    setSourceManager(Diags.getSourceManager());
650353358Sdim}
651353358Sdim
652353358SdimVerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
653353358Sdim  assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
654353358Sdim  assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
655353358Sdim  SrcManager = nullptr;
656353358Sdim  CheckDiagnostics();
657353358Sdim  assert(!Diags.ownsClient() &&
658353358Sdim         "The VerifyDiagnosticConsumer takes over ownership of the client!");
659353358Sdim}
660353358Sdim
661353358Sdim// DiagnosticConsumer interface.
662353358Sdim
663353358Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
664353358Sdim                                               const Preprocessor *PP) {
665353358Sdim  // Attach comment handler on first invocation.
666353358Sdim  if (++ActiveSourceFiles == 1) {
667353358Sdim    if (PP) {
668353358Sdim      CurrentPreprocessor = PP;
669353358Sdim      this->LangOpts = &LangOpts;
670353358Sdim      setSourceManager(PP->getSourceManager());
671353358Sdim      const_cast<Preprocessor *>(PP)->addCommentHandler(this);
672353358Sdim#ifndef NDEBUG
673353358Sdim      // Debug build tracks parsed files.
674353358Sdim      const_cast<Preprocessor *>(PP)->addPPCallbacks(
675360784Sdim                      std::make_unique<VerifyFileTracker>(*this, *SrcManager));
676353358Sdim#endif
677226586Sdim    }
678226586Sdim  }
679239462Sdim
680353358Sdim  assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
681353358Sdim  PrimaryClient->BeginSourceFile(LangOpts, PP);
682226586Sdim}
683226586Sdim
684353358Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() {
685353358Sdim  assert(ActiveSourceFiles && "No active source files!");
686353358Sdim  PrimaryClient->EndSourceFile();
687353358Sdim
688353358Sdim  // Detach comment handler once last active source file completed.
689353358Sdim  if (--ActiveSourceFiles == 0) {
690353358Sdim    if (CurrentPreprocessor)
691353358Sdim      const_cast<Preprocessor *>(CurrentPreprocessor)->
692353358Sdim          removeCommentHandler(this);
693353358Sdim
694353358Sdim    // Diagnose any used-but-not-defined markers.
695353358Sdim    Markers->finalize();
696353358Sdim
697353358Sdim    // Check diagnostics once last file completed.
698353358Sdim    CheckDiagnostics();
699353358Sdim    CurrentPreprocessor = nullptr;
700353358Sdim    LangOpts = nullptr;
701353358Sdim  }
702353358Sdim}
703353358Sdim
704353358Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic(
705353358Sdim      DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
706353358Sdim  if (Info.hasSourceManager()) {
707353358Sdim    // If this diagnostic is for a different source manager, ignore it.
708353358Sdim    if (SrcManager && &Info.getSourceManager() != SrcManager)
709353358Sdim      return;
710353358Sdim
711353358Sdim    setSourceManager(Info.getSourceManager());
712353358Sdim  }
713353358Sdim
714353358Sdim#ifndef NDEBUG
715353358Sdim  // Debug build tracks unparsed files for possible
716353358Sdim  // unparsed expected-* directives.
717353358Sdim  if (SrcManager) {
718353358Sdim    SourceLocation Loc = Info.getLocation();
719353358Sdim    if (Loc.isValid()) {
720353358Sdim      ParsedStatus PS = IsUnparsed;
721353358Sdim
722353358Sdim      Loc = SrcManager->getExpansionLoc(Loc);
723353358Sdim      FileID FID = SrcManager->getFileID(Loc);
724353358Sdim
725353358Sdim      const FileEntry *FE = SrcManager->getFileEntryForID(FID);
726353358Sdim      if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
727353358Sdim        // If the file is a modules header file it shall not be parsed
728353358Sdim        // for expected-* directives.
729353358Sdim        HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
730353358Sdim        if (HS.findModuleForHeader(FE))
731353358Sdim          PS = IsUnparsedNoDirectives;
732353358Sdim      }
733353358Sdim
734353358Sdim      UpdateParsedFileStatus(*SrcManager, FID, PS);
735353358Sdim    }
736353358Sdim  }
737353358Sdim#endif
738353358Sdim
739353358Sdim  // Send the diagnostic to the buffer, we will check it once we reach the end
740353358Sdim  // of the source file (or are destructed).
741353358Sdim  Buffer->HandleDiagnostic(DiagLevel, Info);
742353358Sdim}
743353358Sdim
744239462Sdim/// HandleComment - Hook into the preprocessor and extract comments containing
745239462Sdim///  expected errors and warnings.
746239462Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
747239462Sdim                                             SourceRange Comment) {
748239462Sdim  SourceManager &SM = PP.getSourceManager();
749251662Sdim
750251662Sdim  // If this comment is for a different source manager, ignore it.
751251662Sdim  if (SrcManager && &SM != SrcManager)
752251662Sdim    return false;
753251662Sdim
754239462Sdim  SourceLocation CommentBegin = Comment.getBegin();
755239462Sdim
756239462Sdim  const char *CommentRaw = SM.getCharacterData(CommentBegin);
757239462Sdim  StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
758239462Sdim
759239462Sdim  if (C.empty())
760239462Sdim    return false;
761239462Sdim
762239462Sdim  // Fold any "\<EOL>" sequences
763239462Sdim  size_t loc = C.find('\\');
764239462Sdim  if (loc == StringRef::npos) {
765353358Sdim    ParseDirective(C, &ED, SM, &PP, CommentBegin, Status, *Markers);
766239462Sdim    return false;
767239462Sdim  }
768239462Sdim
769239462Sdim  std::string C2;
770239462Sdim  C2.reserve(C.size());
771239462Sdim
772239462Sdim  for (size_t last = 0;; loc = C.find('\\', last)) {
773239462Sdim    if (loc == StringRef::npos || loc == C.size()) {
774239462Sdim      C2 += C.substr(last);
775239462Sdim      break;
776239462Sdim    }
777239462Sdim    C2 += C.substr(last, loc-last);
778239462Sdim    last = loc + 1;
779239462Sdim
780239462Sdim    if (C[last] == '\n' || C[last] == '\r') {
781239462Sdim      ++last;
782239462Sdim
783239462Sdim      // Escape \r\n  or \n\r, but not \n\n.
784239462Sdim      if (last < C.size())
785239462Sdim        if (C[last] == '\n' || C[last] == '\r')
786239462Sdim          if (C[last] != C[last-1])
787239462Sdim            ++last;
788239462Sdim    } else {
789239462Sdim      // This was just a normal backslash.
790239462Sdim      C2 += '\\';
791239462Sdim    }
792239462Sdim  }
793239462Sdim
794239462Sdim  if (!C2.empty())
795353358Sdim    ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status, *Markers);
796239462Sdim  return false;
797239462Sdim}
798239462Sdim
799239462Sdim#ifndef NDEBUG
800341825Sdim/// Lex the specified source file to determine whether it contains
801239462Sdim/// any expected-* directives.  As a Lexer is used rather than a full-blown
802239462Sdim/// Preprocessor, directives inside skipped #if blocks will still be found.
803239462Sdim///
804239462Sdim/// \return true if any directives were found.
805243830Sdimstatic bool findDirectives(SourceManager &SM, FileID FID,
806243830Sdim                           const LangOptions &LangOpts) {
807226586Sdim  // Create a raw lexer to pull all the comments out of FID.
808226586Sdim  if (FID.isInvalid())
809239462Sdim    return false;
810226586Sdim
811226586Sdim  // Create a lexer to lex all the tokens of the main file in raw mode.
812226586Sdim  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
813243830Sdim  Lexer RawLex(FID, FromFile, SM, LangOpts);
814226586Sdim
815226586Sdim  // Return comments as tokens, this is how we find expected diagnostics.
816226586Sdim  RawLex.SetCommentRetentionState(true);
817226586Sdim
818226586Sdim  Token Tok;
819226586Sdim  Tok.setKind(tok::comment);
820243830Sdim  VerifyDiagnosticConsumer::DirectiveStatus Status =
821243830Sdim    VerifyDiagnosticConsumer::HasNoDirectives;
822226586Sdim  while (Tok.isNot(tok::eof)) {
823261991Sdim    RawLex.LexFromRawLexer(Tok);
824226586Sdim    if (!Tok.is(tok::comment)) continue;
825226586Sdim
826243830Sdim    std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
827226586Sdim    if (Comment.empty()) continue;
828226586Sdim
829353358Sdim    // We don't care about tracking markers for this phase.
830353358Sdim    VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
831353358Sdim
832243830Sdim    // Find first directive.
833276479Sdim    if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
834353358Sdim                       Status, Markers))
835243830Sdim      return true;
836239462Sdim  }
837243830Sdim  return false;
838226586Sdim}
839239462Sdim#endif // !NDEBUG
840226586Sdim
841341825Sdim/// Takes a list of diagnostics that have been generated but not matched
842239462Sdim/// by an expected-* directive and produces a diagnostic to the user from this.
843239462Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
844239462Sdim                                const_diag_iterator diag_begin,
845239462Sdim                                const_diag_iterator diag_end,
846239462Sdim                                const char *Kind) {
847226586Sdim  if (diag_begin == diag_end) return 0;
848226586Sdim
849234353Sdim  SmallString<256> Fmt;
850226586Sdim  llvm::raw_svector_ostream OS(Fmt);
851226586Sdim  for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
852226586Sdim    if (I->first.isInvalid() || !SourceMgr)
853226586Sdim      OS << "\n  (frontend)";
854251662Sdim    else {
855251662Sdim      OS << "\n ";
856251662Sdim      if (const FileEntry *File = SourceMgr->getFileEntryForID(
857251662Sdim                                                SourceMgr->getFileID(I->first)))
858251662Sdim        OS << " File " << File->getName();
859251662Sdim      OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
860251662Sdim    }
861226586Sdim    OS << ": " << I->second;
862226586Sdim  }
863226586Sdim
864239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
865239462Sdim    << Kind << /*Unexpected=*/true << OS.str();
866226586Sdim  return std::distance(diag_begin, diag_end);
867226586Sdim}
868226586Sdim
869341825Sdim/// Takes a list of diagnostics that were expected to have been generated
870239462Sdim/// but were not and produces a diagnostic to the user from this.
871280031Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags,
872280031Sdim                              SourceManager &SourceMgr,
873280031Sdim                              std::vector<Directive *> &DL, const char *Kind) {
874226586Sdim  if (DL.empty())
875226586Sdim    return 0;
876226586Sdim
877234353Sdim  SmallString<256> Fmt;
878226586Sdim  llvm::raw_svector_ostream OS(Fmt);
879341825Sdim  for (const auto *D : DL) {
880341825Sdim    if (D->DiagnosticLoc.isInvalid())
881327952Sdim      OS << "\n  File *";
882327952Sdim    else
883341825Sdim      OS << "\n  File " << SourceMgr.getFilename(D->DiagnosticLoc);
884341825Sdim    if (D->MatchAnyLine)
885276479Sdim      OS << " Line *";
886276479Sdim    else
887341825Sdim      OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
888341825Sdim    if (D->DirectiveLoc != D->DiagnosticLoc)
889239462Sdim      OS << " (directive at "
890341825Sdim         << SourceMgr.getFilename(D->DirectiveLoc) << ':'
891341825Sdim         << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
892341825Sdim    OS << ": " << D->Text;
893226586Sdim  }
894226586Sdim
895239462Sdim  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
896239462Sdim    << Kind << /*Unexpected=*/false << OS.str();
897226586Sdim  return DL.size();
898226586Sdim}
899226586Sdim
900341825Sdim/// Determine whether two source locations come from the same file.
901251662Sdimstatic bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
902251662Sdim                           SourceLocation DiagnosticLoc) {
903251662Sdim  while (DiagnosticLoc.isMacroID())
904251662Sdim    DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
905251662Sdim
906261991Sdim  if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
907251662Sdim    return true;
908251662Sdim
909251662Sdim  const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
910261991Sdim  if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
911251662Sdim    return true;
912251662Sdim
913251662Sdim  return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
914251662Sdim}
915251662Sdim
916226586Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the
917226586Sdim/// the difference between them.
918226586Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
919226586Sdim                           const char *Label,
920226586Sdim                           DirectiveList &Left,
921226586Sdim                           const_diag_iterator d2_begin,
922288943Sdim                           const_diag_iterator d2_end,
923288943Sdim                           bool IgnoreUnexpected) {
924280031Sdim  std::vector<Directive *> LeftOnly;
925226586Sdim  DiagList Right(d2_begin, d2_end);
926226586Sdim
927280031Sdim  for (auto &Owner : Left) {
928280031Sdim    Directive &D = *Owner;
929239462Sdim    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
930226586Sdim
931239462Sdim    for (unsigned i = 0; i < D.Max; ++i) {
932226586Sdim      DiagList::iterator II, IE;
933226586Sdim      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
934276479Sdim        if (!D.MatchAnyLine) {
935276479Sdim          unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
936276479Sdim          if (LineNo1 != LineNo2)
937276479Sdim            continue;
938276479Sdim        }
939226586Sdim
940327952Sdim        if (!D.DiagnosticLoc.isInvalid() &&
941327952Sdim            !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
942251662Sdim          continue;
943251662Sdim
944226586Sdim        const std::string &RightText = II->second;
945239462Sdim        if (D.match(RightText))
946226586Sdim          break;
947226586Sdim      }
948226586Sdim      if (II == IE) {
949226586Sdim        // Not found.
950239462Sdim        if (i >= D.Min) break;
951280031Sdim        LeftOnly.push_back(&D);
952226586Sdim      } else {
953226586Sdim        // Found. The same cannot be found twice.
954226586Sdim        Right.erase(II);
955226586Sdim      }
956226586Sdim    }
957226586Sdim  }
958226586Sdim  // Now all that's left in Right are those that were not matched.
959239462Sdim  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
960288943Sdim  if (!IgnoreUnexpected)
961288943Sdim    num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
962234353Sdim  return num;
963226586Sdim}
964226586Sdim
965226586Sdim/// CheckResults - This compares the expected results to those that
966226586Sdim/// were actually reported. It emits any discrepencies. Return "true" if there
967226586Sdim/// were problems. Return "false" otherwise.
968226586Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
969226586Sdim                             const TextDiagnosticBuffer &Buffer,
970226586Sdim                             ExpectedData &ED) {
971226586Sdim  // We want to capture the delta between what was expected and what was
972226586Sdim  // seen.
973226586Sdim  //
974226586Sdim  //   Expected \ Seen - set expected but not seen
975226586Sdim  //   Seen \ Expected - set seen but not expected
976226586Sdim  unsigned NumProblems = 0;
977226586Sdim
978288943Sdim  const DiagnosticLevelMask DiagMask =
979288943Sdim    Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
980288943Sdim
981226586Sdim  // See if there are error mismatches.
982226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
983288943Sdim                            Buffer.err_begin(), Buffer.err_end(),
984288943Sdim                            bool(DiagnosticLevelMask::Error & DiagMask));
985226586Sdim
986226586Sdim  // See if there are warning mismatches.
987226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
988288943Sdim                            Buffer.warn_begin(), Buffer.warn_end(),
989288943Sdim                            bool(DiagnosticLevelMask::Warning & DiagMask));
990226586Sdim
991276479Sdim  // See if there are remark mismatches.
992276479Sdim  NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
993288943Sdim                            Buffer.remark_begin(), Buffer.remark_end(),
994288943Sdim                            bool(DiagnosticLevelMask::Remark & DiagMask));
995276479Sdim
996226586Sdim  // See if there are note mismatches.
997226586Sdim  NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
998288943Sdim                            Buffer.note_begin(), Buffer.note_end(),
999288943Sdim                            bool(DiagnosticLevelMask::Note & DiagMask));
1000226586Sdim
1001226586Sdim  return NumProblems;
1002226586Sdim}
1003226586Sdim
1004243830Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
1005243830Sdim                                                      FileID FID,
1006243830Sdim                                                      ParsedStatus PS) {
1007243830Sdim  // Check SourceManager hasn't changed.
1008243830Sdim  setSourceManager(SM);
1009243830Sdim
1010243830Sdim#ifndef NDEBUG
1011243830Sdim  if (FID.isInvalid())
1012243830Sdim    return;
1013243830Sdim
1014243830Sdim  const FileEntry *FE = SM.getFileEntryForID(FID);
1015243830Sdim
1016243830Sdim  if (PS == IsParsed) {
1017243830Sdim    // Move the FileID from the unparsed set to the parsed set.
1018243830Sdim    UnparsedFiles.erase(FID);
1019243830Sdim    ParsedFiles.insert(std::make_pair(FID, FE));
1020243830Sdim  } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
1021243830Sdim    // Add the FileID to the unparsed set if we haven't seen it before.
1022243830Sdim
1023243830Sdim    // Check for directives.
1024243830Sdim    bool FoundDirectives;
1025243830Sdim    if (PS == IsUnparsedNoDirectives)
1026243830Sdim      FoundDirectives = false;
1027243830Sdim    else
1028243830Sdim      FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
1029243830Sdim
1030243830Sdim    // Add the FileID to the unparsed set.
1031243830Sdim    UnparsedFiles.insert(std::make_pair(FID,
1032243830Sdim                                      UnparsedFileStatus(FE, FoundDirectives)));
1033243830Sdim  }
1034243830Sdim#endif
1035243830Sdim}
1036243830Sdim
1037226586Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() {
1038226586Sdim  // Ensure any diagnostics go to the primary client.
1039280031Sdim  DiagnosticConsumer *CurClient = Diags.getClient();
1040280031Sdim  std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
1041226586Sdim  Diags.setClient(PrimaryClient, false);
1042226586Sdim
1043243830Sdim#ifndef NDEBUG
1044243830Sdim  // In a debug build, scan through any files that may have been missed
1045243830Sdim  // during parsing and issue a fatal error if directives are contained
1046243830Sdim  // within these files.  If a fatal error occurs, this suggests that
1047243830Sdim  // this file is being parsed separately from the main file, in which
1048243830Sdim  // case consider moving the directives to the correct place, if this
1049243830Sdim  // is applicable.
1050341825Sdim  if (!UnparsedFiles.empty()) {
1051243830Sdim    // Generate a cache of parsed FileEntry pointers for alias lookups.
1052243830Sdim    llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
1053341825Sdim    for (const auto &I : ParsedFiles)
1054341825Sdim      if (const FileEntry *FE = I.second)
1055243830Sdim        ParsedFileCache.insert(FE);
1056239462Sdim
1057243830Sdim    // Iterate through list of unparsed files.
1058341825Sdim    for (const auto &I : UnparsedFiles) {
1059341825Sdim      const UnparsedFileStatus &Status = I.second;
1060243830Sdim      const FileEntry *FE = Status.getFile();
1061243830Sdim
1062243830Sdim      // Skip files that have been parsed via an alias.
1063243830Sdim      if (FE && ParsedFileCache.count(FE))
1064239462Sdim        continue;
1065239462Sdim
1066243830Sdim      // Report a fatal error if this file contained directives.
1067243830Sdim      if (Status.foundDirectives()) {
1068239462Sdim        llvm::report_fatal_error(Twine("-verify directives found after rather"
1069239462Sdim                                       " than during normal parsing of ",
1070243830Sdim                                 StringRef(FE ? FE->getName() : "(unknown)")));
1071243830Sdim      }
1072226586Sdim    }
1073226586Sdim
1074243830Sdim    // UnparsedFiles has been processed now, so clear it.
1075243830Sdim    UnparsedFiles.clear();
1076243830Sdim  }
1077243830Sdim#endif // !NDEBUG
1078243830Sdim
1079243830Sdim  if (SrcManager) {
1080243830Sdim    // Produce an error if no expected-* directives could be found in the
1081243830Sdim    // source file(s) processed.
1082243830Sdim    if (Status == HasNoDirectives) {
1083243830Sdim      Diags.Report(diag::err_verify_no_directives).setForceEmit();
1084243830Sdim      ++NumErrors;
1085243830Sdim      Status = HasNoDirectivesReported;
1086243830Sdim    }
1087243830Sdim
1088226586Sdim    // Check that the expected diagnostics occurred.
1089243830Sdim    NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
1090226586Sdim  } else {
1091288943Sdim    const DiagnosticLevelMask DiagMask =
1092288943Sdim        ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
1093288943Sdim    if (bool(DiagnosticLevelMask::Error & DiagMask))
1094288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
1095288943Sdim                                   Buffer->err_end(), "error");
1096288943Sdim    if (bool(DiagnosticLevelMask::Warning & DiagMask))
1097288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
1098288943Sdim                                   Buffer->warn_end(), "warn");
1099288943Sdim    if (bool(DiagnosticLevelMask::Remark & DiagMask))
1100288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
1101288943Sdim                                   Buffer->remark_end(), "remark");
1102288943Sdim    if (bool(DiagnosticLevelMask::Note & DiagMask))
1103288943Sdim      NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
1104288943Sdim                                   Buffer->note_end(), "note");
1105226586Sdim  }
1106226586Sdim
1107280031Sdim  Diags.setClient(CurClient, Owner.release() != nullptr);
1108226586Sdim
1109226586Sdim  // Reset the buffer, we have processed all the diagnostics in it.
1110226586Sdim  Buffer.reset(new TextDiagnosticBuffer());
1111276479Sdim  ED.Reset();
1112226586Sdim}
1113226586Sdim
1114280031Sdimstd::unique_ptr<Directive> Directive::create(bool RegexKind,
1115280031Sdim                                             SourceLocation DirectiveLoc,
1116280031Sdim                                             SourceLocation DiagnosticLoc,
1117280031Sdim                                             bool MatchAnyLine, StringRef Text,
1118280031Sdim                                             unsigned Min, unsigned Max) {
1119276479Sdim  if (!RegexKind)
1120360784Sdim    return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
1121280031Sdim                                                MatchAnyLine, Text, Min, Max);
1122276479Sdim
1123276479Sdim  // Parse the directive into a regular expression.
1124276479Sdim  std::string RegexStr;
1125276479Sdim  StringRef S = Text;
1126276479Sdim  while (!S.empty()) {
1127276479Sdim    if (S.startswith("{{")) {
1128276479Sdim      S = S.drop_front(2);
1129276479Sdim      size_t RegexMatchLength = S.find("}}");
1130276479Sdim      assert(RegexMatchLength != StringRef::npos);
1131276479Sdim      // Append the regex, enclosed in parentheses.
1132276479Sdim      RegexStr += "(";
1133276479Sdim      RegexStr.append(S.data(), RegexMatchLength);
1134276479Sdim      RegexStr += ")";
1135276479Sdim      S = S.drop_front(RegexMatchLength + 2);
1136276479Sdim    } else {
1137276479Sdim      size_t VerbatimMatchLength = S.find("{{");
1138276479Sdim      if (VerbatimMatchLength == StringRef::npos)
1139276479Sdim        VerbatimMatchLength = S.size();
1140276479Sdim      // Escape and append the fixed string.
1141276479Sdim      RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
1142276479Sdim      S = S.drop_front(VerbatimMatchLength);
1143276479Sdim    }
1144276479Sdim  }
1145276479Sdim
1146360784Sdim  return std::make_unique<RegexDirective>(
1147280031Sdim      DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr);
1148226586Sdim}
1149