xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This is a concrete diagnostic client, which buffers the diagnostic messages.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/Frontend/VerifyDiagnosticConsumer.h"
140b57cec5SDimitry Andric #include "clang/Basic/CharInfo.h"
150b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h"
160b57cec5SDimitry Andric #include "clang/Basic/DiagnosticOptions.h"
170b57cec5SDimitry Andric #include "clang/Basic/FileManager.h"
180b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
190b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h"
200b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
210b57cec5SDimitry Andric #include "clang/Basic/TokenKinds.h"
220b57cec5SDimitry Andric #include "clang/Frontend/FrontendDiagnostic.h"
230b57cec5SDimitry Andric #include "clang/Frontend/TextDiagnosticBuffer.h"
240b57cec5SDimitry Andric #include "clang/Lex/HeaderSearch.h"
250b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
260b57cec5SDimitry Andric #include "clang/Lex/PPCallbacks.h"
270b57cec5SDimitry Andric #include "clang/Lex/Preprocessor.h"
280b57cec5SDimitry Andric #include "clang/Lex/Token.h"
290b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
300b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
310b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
320b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
330b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
340b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
350b57cec5SDimitry Andric #include "llvm/Support/Regex.h"
360b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
370b57cec5SDimitry Andric #include <algorithm>
380b57cec5SDimitry Andric #include <cassert>
390b57cec5SDimitry Andric #include <cstddef>
400b57cec5SDimitry Andric #include <cstring>
410b57cec5SDimitry Andric #include <iterator>
420b57cec5SDimitry Andric #include <memory>
430b57cec5SDimitry Andric #include <string>
440b57cec5SDimitry Andric #include <utility>
450b57cec5SDimitry Andric #include <vector>
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric using namespace clang;
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric using Directive = VerifyDiagnosticConsumer::Directive;
500b57cec5SDimitry Andric using DirectiveList = VerifyDiagnosticConsumer::DirectiveList;
510b57cec5SDimitry Andric using ExpectedData = VerifyDiagnosticConsumer::ExpectedData;
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric #ifndef NDEBUG
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric namespace {
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric class VerifyFileTracker : public PPCallbacks {
580b57cec5SDimitry Andric   VerifyDiagnosticConsumer &Verify;
590b57cec5SDimitry Andric   SourceManager &SM;
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric public:
VerifyFileTracker(VerifyDiagnosticConsumer & Verify,SourceManager & SM)620b57cec5SDimitry Andric   VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
630b57cec5SDimitry Andric       : Verify(Verify), SM(SM) {}
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric   /// Hook into the preprocessor and update the list of parsed
660b57cec5SDimitry Andric   /// files when the preprocessor indicates a new file is entered.
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID)670b57cec5SDimitry Andric   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
680b57cec5SDimitry Andric                    SrcMgr::CharacteristicKind FileType,
690b57cec5SDimitry Andric                    FileID PrevFID) override {
700b57cec5SDimitry Andric     Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
710b57cec5SDimitry Andric                                   VerifyDiagnosticConsumer::IsParsed);
720b57cec5SDimitry Andric   }
730b57cec5SDimitry Andric };
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric } // namespace
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric #endif
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
800b57cec5SDimitry Andric // Checking diagnostics implementation.
810b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric using DiagList = TextDiagnosticBuffer::DiagList;
840b57cec5SDimitry Andric using const_diag_iterator = TextDiagnosticBuffer::const_iterator;
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric namespace {
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric /// StandardDirective - Directive with string matching.
890b57cec5SDimitry Andric class StandardDirective : public Directive {
900b57cec5SDimitry Andric public:
StandardDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)910b57cec5SDimitry Andric   StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
925ffd83dbSDimitry Andric                     bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
935ffd83dbSDimitry Andric                     unsigned Min, unsigned Max)
945ffd83dbSDimitry Andric       : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
955ffd83dbSDimitry Andric                   MatchAnyLine, Text, Min, Max) {}
960b57cec5SDimitry Andric 
isValid(std::string & Error)970b57cec5SDimitry Andric   bool isValid(std::string &Error) override {
980b57cec5SDimitry Andric     // all strings are considered valid; even empty ones
990b57cec5SDimitry Andric     return true;
1000b57cec5SDimitry Andric   }
1010b57cec5SDimitry Andric 
match(StringRef S)102349cc55cSDimitry Andric   bool match(StringRef S) override { return S.contains(Text); }
1030b57cec5SDimitry Andric };
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric /// RegexDirective - Directive with regular-expression matching.
1060b57cec5SDimitry Andric class RegexDirective : public Directive {
1070b57cec5SDimitry Andric public:
RegexDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max,StringRef RegexStr)1080b57cec5SDimitry Andric   RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
1095ffd83dbSDimitry Andric                  bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
1105ffd83dbSDimitry Andric                  unsigned Min, unsigned Max, StringRef RegexStr)
1115ffd83dbSDimitry Andric       : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
1125ffd83dbSDimitry Andric                   MatchAnyLine, Text, Min, Max),
1130b57cec5SDimitry Andric         Regex(RegexStr) {}
1140b57cec5SDimitry Andric 
isValid(std::string & Error)1150b57cec5SDimitry Andric   bool isValid(std::string &Error) override {
1160b57cec5SDimitry Andric     return Regex.isValid(Error);
1170b57cec5SDimitry Andric   }
1180b57cec5SDimitry Andric 
match(StringRef S)1190b57cec5SDimitry Andric   bool match(StringRef S) override {
1200b57cec5SDimitry Andric     return Regex.match(S);
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric private:
1240b57cec5SDimitry Andric   llvm::Regex Regex;
1250b57cec5SDimitry Andric };
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric class ParseHelper
1280b57cec5SDimitry Andric {
1290b57cec5SDimitry Andric public:
ParseHelper(StringRef S)1300b57cec5SDimitry Andric   ParseHelper(StringRef S)
1310b57cec5SDimitry Andric       : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {}
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // Return true if string literal is next.
Next(StringRef S)1340b57cec5SDimitry Andric   bool Next(StringRef S) {
1350b57cec5SDimitry Andric     P = C;
1360b57cec5SDimitry Andric     PEnd = C + S.size();
1370b57cec5SDimitry Andric     if (PEnd > End)
1380b57cec5SDimitry Andric       return false;
1390b57cec5SDimitry Andric     return memcmp(P, S.data(), S.size()) == 0;
1400b57cec5SDimitry Andric   }
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   // Return true if number is next.
1430b57cec5SDimitry Andric   // Output N only if number is next.
Next(unsigned & N)1440b57cec5SDimitry Andric   bool Next(unsigned &N) {
1450b57cec5SDimitry Andric     unsigned TMP = 0;
1460b57cec5SDimitry Andric     P = C;
1470b57cec5SDimitry Andric     PEnd = P;
1480b57cec5SDimitry Andric     for (; PEnd < End && *PEnd >= '0' && *PEnd <= '9'; ++PEnd) {
1490b57cec5SDimitry Andric       TMP *= 10;
1500b57cec5SDimitry Andric       TMP += *PEnd - '0';
1510b57cec5SDimitry Andric     }
1520b57cec5SDimitry Andric     if (PEnd == C)
1530b57cec5SDimitry Andric       return false;
1540b57cec5SDimitry Andric     N = TMP;
1550b57cec5SDimitry Andric     return true;
1560b57cec5SDimitry Andric   }
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   // Return true if a marker is next.
1590b57cec5SDimitry Andric   // A marker is the longest match for /#[A-Za-z0-9_-]+/.
NextMarker()1600b57cec5SDimitry Andric   bool NextMarker() {
1610b57cec5SDimitry Andric     P = C;
1620b57cec5SDimitry Andric     if (P == End || *P != '#')
1630b57cec5SDimitry Andric       return false;
1640b57cec5SDimitry Andric     PEnd = P;
1650b57cec5SDimitry Andric     ++PEnd;
1660b57cec5SDimitry Andric     while ((isAlphanumeric(*PEnd) || *PEnd == '-' || *PEnd == '_') &&
1670b57cec5SDimitry Andric            PEnd < End)
1680b57cec5SDimitry Andric       ++PEnd;
1690b57cec5SDimitry Andric     return PEnd > P + 1;
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // Return true if string literal S is matched in content.
1730b57cec5SDimitry Andric   // When true, P marks begin-position of the match, and calling Advance sets C
1740b57cec5SDimitry Andric   // to end-position of the match.
1750b57cec5SDimitry Andric   // If S is the empty string, then search for any letter instead (makes sense
1760b57cec5SDimitry Andric   // with FinishDirectiveToken=true).
1770b57cec5SDimitry Andric   // If EnsureStartOfWord, then skip matches that don't start a new word.
1780b57cec5SDimitry Andric   // If FinishDirectiveToken, then assume the match is the start of a comment
1790b57cec5SDimitry Andric   // directive for -verify, and extend the match to include the entire first
1800b57cec5SDimitry Andric   // token of that directive.
Search(StringRef S,bool EnsureStartOfWord=false,bool FinishDirectiveToken=false)1810b57cec5SDimitry Andric   bool Search(StringRef S, bool EnsureStartOfWord = false,
1820b57cec5SDimitry Andric               bool FinishDirectiveToken = false) {
1830b57cec5SDimitry Andric     do {
1840b57cec5SDimitry Andric       if (!S.empty()) {
1850b57cec5SDimitry Andric         P = std::search(C, End, S.begin(), S.end());
1860b57cec5SDimitry Andric         PEnd = P + S.size();
1870b57cec5SDimitry Andric       }
1880b57cec5SDimitry Andric       else {
1890b57cec5SDimitry Andric         P = C;
1900b57cec5SDimitry Andric         while (P != End && !isLetter(*P))
1910b57cec5SDimitry Andric           ++P;
1920b57cec5SDimitry Andric         PEnd = P + 1;
1930b57cec5SDimitry Andric       }
1940b57cec5SDimitry Andric       if (P == End)
1950b57cec5SDimitry Andric         break;
1960b57cec5SDimitry Andric       // If not start of word but required, skip and search again.
1970b57cec5SDimitry Andric       if (EnsureStartOfWord
1980b57cec5SDimitry Andric                // Check if string literal starts a new word.
1990b57cec5SDimitry Andric           && !(P == Begin || isWhitespace(P[-1])
2000b57cec5SDimitry Andric                // Or it could be preceded by the start of a comment.
2010b57cec5SDimitry Andric                || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
2020b57cec5SDimitry Andric                                    &&  P[-2] == '/')))
2030b57cec5SDimitry Andric         continue;
2040b57cec5SDimitry Andric       if (FinishDirectiveToken) {
2050b57cec5SDimitry Andric         while (PEnd != End && (isAlphanumeric(*PEnd)
2060b57cec5SDimitry Andric                                || *PEnd == '-' || *PEnd == '_'))
2070b57cec5SDimitry Andric           ++PEnd;
2080b57cec5SDimitry Andric         // Put back trailing digits and hyphens to be parsed later as a count
2090b57cec5SDimitry Andric         // or count range.  Because -verify prefixes must start with letters,
2100b57cec5SDimitry Andric         // we know the actual directive we found starts with a letter, so
2110b57cec5SDimitry Andric         // we won't put back the entire directive word and thus record an empty
2120b57cec5SDimitry Andric         // string.
2130b57cec5SDimitry Andric         assert(isLetter(*P) && "-verify prefix must start with a letter");
2140b57cec5SDimitry Andric         while (isDigit(PEnd[-1]) || PEnd[-1] == '-')
2150b57cec5SDimitry Andric           --PEnd;
2160b57cec5SDimitry Andric       }
2170b57cec5SDimitry Andric       return true;
2180b57cec5SDimitry Andric     } while (Advance());
2190b57cec5SDimitry Andric     return false;
2200b57cec5SDimitry Andric   }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   // Return true if a CloseBrace that closes the OpenBrace at the current nest
2230b57cec5SDimitry Andric   // level is found. When true, P marks begin-position of CloseBrace.
SearchClosingBrace(StringRef OpenBrace,StringRef CloseBrace)2240b57cec5SDimitry Andric   bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
2250b57cec5SDimitry Andric     unsigned Depth = 1;
2260b57cec5SDimitry Andric     P = C;
2270b57cec5SDimitry Andric     while (P < End) {
2280b57cec5SDimitry Andric       StringRef S(P, End - P);
2295f757f3fSDimitry Andric       if (S.starts_with(OpenBrace)) {
2300b57cec5SDimitry Andric         ++Depth;
2310b57cec5SDimitry Andric         P += OpenBrace.size();
2325f757f3fSDimitry Andric       } else if (S.starts_with(CloseBrace)) {
2330b57cec5SDimitry Andric         --Depth;
2340b57cec5SDimitry Andric         if (Depth == 0) {
2350b57cec5SDimitry Andric           PEnd = P + CloseBrace.size();
2360b57cec5SDimitry Andric           return true;
2370b57cec5SDimitry Andric         }
2380b57cec5SDimitry Andric         P += CloseBrace.size();
2390b57cec5SDimitry Andric       } else {
2400b57cec5SDimitry Andric         ++P;
2410b57cec5SDimitry Andric       }
2420b57cec5SDimitry Andric     }
2430b57cec5SDimitry Andric     return false;
2440b57cec5SDimitry Andric   }
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric   // Advance 1-past previous next/search.
2470b57cec5SDimitry Andric   // Behavior is undefined if previous next/search failed.
Advance()2480b57cec5SDimitry Andric   bool Advance() {
2490b57cec5SDimitry Andric     C = PEnd;
2500b57cec5SDimitry Andric     return C < End;
2510b57cec5SDimitry Andric   }
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric   // Return the text matched by the previous next/search.
2540b57cec5SDimitry Andric   // Behavior is undefined if previous next/search failed.
Match()2550b57cec5SDimitry Andric   StringRef Match() { return StringRef(P, PEnd - P); }
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   // Skip zero or more whitespace.
SkipWhitespace()2580b57cec5SDimitry Andric   void SkipWhitespace() {
2590b57cec5SDimitry Andric     for (; C < End && isWhitespace(*C); ++C)
2600b57cec5SDimitry Andric       ;
2610b57cec5SDimitry Andric   }
2620b57cec5SDimitry Andric 
2630b57cec5SDimitry Andric   // Return true if EOF reached.
Done()2640b57cec5SDimitry Andric   bool Done() {
2650b57cec5SDimitry Andric     return !(C < End);
2660b57cec5SDimitry Andric   }
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric   // Beginning of expected content.
2690b57cec5SDimitry Andric   const char * const Begin;
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   // End of expected content (1-past).
2720b57cec5SDimitry Andric   const char * const End;
2730b57cec5SDimitry Andric 
2740b57cec5SDimitry Andric   // Position of next char in content.
2750b57cec5SDimitry Andric   const char *C;
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric   // Previous next/search subject start.
2780b57cec5SDimitry Andric   const char *P;
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric private:
2810b57cec5SDimitry Andric   // Previous next/search subject end (1-past).
2820b57cec5SDimitry Andric   const char *PEnd = nullptr;
2830b57cec5SDimitry Andric };
2840b57cec5SDimitry Andric 
2850b57cec5SDimitry Andric // The information necessary to create a directive.
2860b57cec5SDimitry Andric struct UnattachedDirective {
2870b57cec5SDimitry Andric   DirectiveList *DL = nullptr;
2880b57cec5SDimitry Andric   bool RegexKind = false;
2890b57cec5SDimitry Andric   SourceLocation DirectivePos, ContentBegin;
2900b57cec5SDimitry Andric   std::string Text;
2910b57cec5SDimitry Andric   unsigned Min = 1, Max = 1;
2920b57cec5SDimitry Andric };
2930b57cec5SDimitry Andric 
2940b57cec5SDimitry Andric // Attach the specified directive to the line of code indicated by
2950b57cec5SDimitry Andric // \p ExpectedLoc.
attachDirective(DiagnosticsEngine & Diags,const UnattachedDirective & UD,SourceLocation ExpectedLoc,bool MatchAnyFileAndLine=false,bool MatchAnyLine=false)2960b57cec5SDimitry Andric void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
2975ffd83dbSDimitry Andric                      SourceLocation ExpectedLoc,
2985ffd83dbSDimitry Andric                      bool MatchAnyFileAndLine = false,
2995ffd83dbSDimitry Andric                      bool MatchAnyLine = false) {
3000b57cec5SDimitry Andric   // Construct new directive.
3015ffd83dbSDimitry Andric   std::unique_ptr<Directive> D = Directive::create(
3025ffd83dbSDimitry Andric       UD.RegexKind, UD.DirectivePos, ExpectedLoc, MatchAnyFileAndLine,
3030b57cec5SDimitry Andric       MatchAnyLine, UD.Text, UD.Min, UD.Max);
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric   std::string Error;
3060b57cec5SDimitry Andric   if (!D->isValid(Error)) {
3070b57cec5SDimitry Andric     Diags.Report(UD.ContentBegin, diag::err_verify_invalid_content)
3080b57cec5SDimitry Andric       << (UD.RegexKind ? "regex" : "string") << Error;
3090b57cec5SDimitry Andric   }
3100b57cec5SDimitry Andric 
3110b57cec5SDimitry Andric   UD.DL->push_back(std::move(D));
3120b57cec5SDimitry Andric }
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric } // anonymous
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric // Tracker for markers in the input files. A marker is a comment of the form
3170b57cec5SDimitry Andric //
3180b57cec5SDimitry Andric //   n = 123; // #123
3190b57cec5SDimitry Andric //
3200b57cec5SDimitry Andric // ... that can be referred to by a later expected-* directive:
3210b57cec5SDimitry Andric //
3220b57cec5SDimitry Andric //   // expected-error@#123 {{undeclared identifier 'n'}}
3230b57cec5SDimitry Andric //
3240b57cec5SDimitry Andric // Marker declarations must be at the start of a comment or preceded by
3250b57cec5SDimitry Andric // whitespace to distinguish them from uses of markers in directives.
3260b57cec5SDimitry Andric class VerifyDiagnosticConsumer::MarkerTracker {
3270b57cec5SDimitry Andric   DiagnosticsEngine &Diags;
3280b57cec5SDimitry Andric 
3290b57cec5SDimitry Andric   struct Marker {
3300b57cec5SDimitry Andric     SourceLocation DefLoc;
3310b57cec5SDimitry Andric     SourceLocation RedefLoc;
3320b57cec5SDimitry Andric     SourceLocation UseLoc;
3330b57cec5SDimitry Andric   };
3340b57cec5SDimitry Andric   llvm::StringMap<Marker> Markers;
3350b57cec5SDimitry Andric 
3360b57cec5SDimitry Andric   // Directives that couldn't be created yet because they name an unknown
3370b57cec5SDimitry Andric   // marker.
3380b57cec5SDimitry Andric   llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives;
3390b57cec5SDimitry Andric 
3400b57cec5SDimitry Andric public:
MarkerTracker(DiagnosticsEngine & Diags)3410b57cec5SDimitry Andric   MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {}
3420b57cec5SDimitry Andric 
3430b57cec5SDimitry Andric   // Register a marker.
addMarker(StringRef MarkerName,SourceLocation Pos)3440b57cec5SDimitry Andric   void addMarker(StringRef MarkerName, SourceLocation Pos) {
3450b57cec5SDimitry Andric     auto InsertResult = Markers.insert(
3460b57cec5SDimitry Andric         {MarkerName, Marker{Pos, SourceLocation(), SourceLocation()}});
3470b57cec5SDimitry Andric 
3480b57cec5SDimitry Andric     Marker &M = InsertResult.first->second;
3490b57cec5SDimitry Andric     if (!InsertResult.second) {
3500b57cec5SDimitry Andric       // Marker was redefined.
3510b57cec5SDimitry Andric       M.RedefLoc = Pos;
3520b57cec5SDimitry Andric     } else {
3530b57cec5SDimitry Andric       // First definition: build any deferred directives.
3540b57cec5SDimitry Andric       auto Deferred = DeferredDirectives.find(MarkerName);
3550b57cec5SDimitry Andric       if (Deferred != DeferredDirectives.end()) {
3560b57cec5SDimitry Andric         for (auto &UD : Deferred->second) {
3570b57cec5SDimitry Andric           if (M.UseLoc.isInvalid())
3580b57cec5SDimitry Andric             M.UseLoc = UD.DirectivePos;
3590b57cec5SDimitry Andric           attachDirective(Diags, UD, Pos);
3600b57cec5SDimitry Andric         }
3610b57cec5SDimitry Andric         DeferredDirectives.erase(Deferred);
3620b57cec5SDimitry Andric       }
3630b57cec5SDimitry Andric     }
3640b57cec5SDimitry Andric   }
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   // Register a directive at the specified marker.
addDirective(StringRef MarkerName,const UnattachedDirective & UD)3670b57cec5SDimitry Andric   void addDirective(StringRef MarkerName, const UnattachedDirective &UD) {
3680b57cec5SDimitry Andric     auto MarkerIt = Markers.find(MarkerName);
3690b57cec5SDimitry Andric     if (MarkerIt != Markers.end()) {
3700b57cec5SDimitry Andric       Marker &M = MarkerIt->second;
3710b57cec5SDimitry Andric       if (M.UseLoc.isInvalid())
3720b57cec5SDimitry Andric         M.UseLoc = UD.DirectivePos;
3730b57cec5SDimitry Andric       return attachDirective(Diags, UD, M.DefLoc);
3740b57cec5SDimitry Andric     }
3750b57cec5SDimitry Andric     DeferredDirectives[MarkerName].push_back(UD);
3760b57cec5SDimitry Andric   }
3770b57cec5SDimitry Andric 
3780b57cec5SDimitry Andric   // Ensure we have no remaining deferred directives, and no
3790b57cec5SDimitry Andric   // multiply-defined-and-used markers.
finalize()3800b57cec5SDimitry Andric   void finalize() {
3810b57cec5SDimitry Andric     for (auto &MarkerInfo : Markers) {
3820b57cec5SDimitry Andric       StringRef Name = MarkerInfo.first();
3830b57cec5SDimitry Andric       Marker &M = MarkerInfo.second;
3840b57cec5SDimitry Andric       if (M.RedefLoc.isValid() && M.UseLoc.isValid()) {
3850b57cec5SDimitry Andric         Diags.Report(M.UseLoc, diag::err_verify_ambiguous_marker) << Name;
3860b57cec5SDimitry Andric         Diags.Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name;
3870b57cec5SDimitry Andric         Diags.Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name;
3880b57cec5SDimitry Andric       }
3890b57cec5SDimitry Andric     }
3900b57cec5SDimitry Andric 
3910b57cec5SDimitry Andric     for (auto &DeferredPair : DeferredDirectives) {
3920b57cec5SDimitry Andric       Diags.Report(DeferredPair.second.front().DirectivePos,
3930b57cec5SDimitry Andric                    diag::err_verify_no_such_marker)
3940b57cec5SDimitry Andric           << DeferredPair.first();
3950b57cec5SDimitry Andric     }
3960b57cec5SDimitry Andric   }
3970b57cec5SDimitry Andric };
3980b57cec5SDimitry Andric 
DetailedErrorString(const DiagnosticsEngine & Diags)399*0fca6ea1SDimitry Andric static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
400*0fca6ea1SDimitry Andric   if (Diags.getDiagnosticOptions().VerifyPrefixes.empty())
401*0fca6ea1SDimitry Andric     return "expected";
402*0fca6ea1SDimitry Andric   return *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
403*0fca6ea1SDimitry Andric }
404*0fca6ea1SDimitry Andric 
4050b57cec5SDimitry Andric /// ParseDirective - Go through the comment and see if it indicates expected
4060b57cec5SDimitry Andric /// diagnostics. If so, then put them in the appropriate directive list.
4070b57cec5SDimitry Andric ///
4080b57cec5SDimitry Andric /// Returns true if any valid directives were found.
ParseDirective(StringRef S,ExpectedData * ED,SourceManager & SM,Preprocessor * PP,SourceLocation Pos,VerifyDiagnosticConsumer::DirectiveStatus & Status,VerifyDiagnosticConsumer::MarkerTracker & Markers)4090b57cec5SDimitry Andric static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
4100b57cec5SDimitry Andric                            Preprocessor *PP, SourceLocation Pos,
4110b57cec5SDimitry Andric                            VerifyDiagnosticConsumer::DirectiveStatus &Status,
4120b57cec5SDimitry Andric                            VerifyDiagnosticConsumer::MarkerTracker &Markers) {
4130b57cec5SDimitry Andric   DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   // First, scan the comment looking for markers.
4160b57cec5SDimitry Andric   for (ParseHelper PH(S); !PH.Done();) {
4170b57cec5SDimitry Andric     if (!PH.Search("#", true))
4180b57cec5SDimitry Andric       break;
4190b57cec5SDimitry Andric     PH.C = PH.P;
4200b57cec5SDimitry Andric     if (!PH.NextMarker()) {
4210b57cec5SDimitry Andric       PH.Next("#");
4220b57cec5SDimitry Andric       PH.Advance();
4230b57cec5SDimitry Andric       continue;
4240b57cec5SDimitry Andric     }
4250b57cec5SDimitry Andric     PH.Advance();
4260b57cec5SDimitry Andric     Markers.addMarker(PH.Match(), Pos);
4270b57cec5SDimitry Andric   }
4280b57cec5SDimitry Andric 
4290b57cec5SDimitry Andric   // A single comment may contain multiple directives.
4300b57cec5SDimitry Andric   bool FoundDirective = false;
4310b57cec5SDimitry Andric   for (ParseHelper PH(S); !PH.Done();) {
4320b57cec5SDimitry Andric     // Search for the initial directive token.
4330b57cec5SDimitry Andric     // If one prefix, save time by searching only for its directives.
4340b57cec5SDimitry Andric     // Otherwise, search for any potential directive token and check it later.
4350b57cec5SDimitry Andric     const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes;
4360b57cec5SDimitry Andric     if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true)
4370b57cec5SDimitry Andric                                : PH.Search("", true, true)))
4380b57cec5SDimitry Andric       break;
4390b57cec5SDimitry Andric 
4400b57cec5SDimitry Andric     StringRef DToken = PH.Match();
4410b57cec5SDimitry Andric     PH.Advance();
4420b57cec5SDimitry Andric 
4430b57cec5SDimitry Andric     // Default directive kind.
4440b57cec5SDimitry Andric     UnattachedDirective D;
4450b57cec5SDimitry Andric     const char *KindStr = "string";
4460b57cec5SDimitry Andric 
4470b57cec5SDimitry Andric     // Parse the initial directive token in reverse so we can easily determine
4480b57cec5SDimitry Andric     // its exact actual prefix.  If we were to parse it from the front instead,
4490b57cec5SDimitry Andric     // it would be harder to determine where the prefix ends because there
4500b57cec5SDimitry Andric     // might be multiple matching -verify prefixes because some might prefix
4510b57cec5SDimitry Andric     // others.
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric     // Regex in initial directive token: -re
454*0fca6ea1SDimitry Andric     if (DToken.consume_back("-re")) {
4550b57cec5SDimitry Andric       D.RegexKind = true;
4560b57cec5SDimitry Andric       KindStr = "regex";
4570b57cec5SDimitry Andric     }
4580b57cec5SDimitry Andric 
4590b57cec5SDimitry Andric     // Type in initial directive token: -{error|warning|note|no-diagnostics}
4600b57cec5SDimitry Andric     bool NoDiag = false;
4610b57cec5SDimitry Andric     StringRef DType;
4625f757f3fSDimitry Andric     if (DToken.ends_with(DType = "-error"))
4630b57cec5SDimitry Andric       D.DL = ED ? &ED->Errors : nullptr;
4645f757f3fSDimitry Andric     else if (DToken.ends_with(DType = "-warning"))
4650b57cec5SDimitry Andric       D.DL = ED ? &ED->Warnings : nullptr;
4665f757f3fSDimitry Andric     else if (DToken.ends_with(DType = "-remark"))
4670b57cec5SDimitry Andric       D.DL = ED ? &ED->Remarks : nullptr;
4685f757f3fSDimitry Andric     else if (DToken.ends_with(DType = "-note"))
4690b57cec5SDimitry Andric       D.DL = ED ? &ED->Notes : nullptr;
4705f757f3fSDimitry Andric     else if (DToken.ends_with(DType = "-no-diagnostics")) {
4710b57cec5SDimitry Andric       NoDiag = true;
4720b57cec5SDimitry Andric       if (D.RegexKind)
4730b57cec5SDimitry Andric         continue;
4745f757f3fSDimitry Andric     } else
4750b57cec5SDimitry Andric       continue;
4760b57cec5SDimitry Andric     DToken = DToken.substr(0, DToken.size()-DType.size());
4770b57cec5SDimitry Andric 
4780b57cec5SDimitry Andric     // What's left in DToken is the actual prefix.  That might not be a -verify
4790b57cec5SDimitry Andric     // prefix even if there is only one -verify prefix (for example, the full
4800b57cec5SDimitry Andric     // DToken is foo-bar-warning, but foo is the only -verify prefix).
4810b57cec5SDimitry Andric     if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken))
4820b57cec5SDimitry Andric       continue;
4830b57cec5SDimitry Andric 
4840b57cec5SDimitry Andric     if (NoDiag) {
4850b57cec5SDimitry Andric       if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
4860b57cec5SDimitry Andric         Diags.Report(Pos, diag::err_verify_invalid_no_diags)
487*0fca6ea1SDimitry Andric             << DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/true;
4880b57cec5SDimitry Andric       else
4890b57cec5SDimitry Andric         Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
4900b57cec5SDimitry Andric       continue;
4910b57cec5SDimitry Andric     }
4920b57cec5SDimitry Andric     if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
4930b57cec5SDimitry Andric       Diags.Report(Pos, diag::err_verify_invalid_no_diags)
494*0fca6ea1SDimitry Andric           << DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/false;
4950b57cec5SDimitry Andric       continue;
4960b57cec5SDimitry Andric     }
4970b57cec5SDimitry Andric     Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
4980b57cec5SDimitry Andric 
4990b57cec5SDimitry Andric     // If a directive has been found but we're not interested
5000b57cec5SDimitry Andric     // in storing the directive information, return now.
5010b57cec5SDimitry Andric     if (!D.DL)
5020b57cec5SDimitry Andric       return true;
5030b57cec5SDimitry Andric 
5040b57cec5SDimitry Andric     // Next optional token: @
5050b57cec5SDimitry Andric     SourceLocation ExpectedLoc;
5060b57cec5SDimitry Andric     StringRef Marker;
5075ffd83dbSDimitry Andric     bool MatchAnyFileAndLine = false;
5080b57cec5SDimitry Andric     bool MatchAnyLine = false;
5090b57cec5SDimitry Andric     if (!PH.Next("@")) {
5100b57cec5SDimitry Andric       ExpectedLoc = Pos;
5110b57cec5SDimitry Andric     } else {
5120b57cec5SDimitry Andric       PH.Advance();
5130b57cec5SDimitry Andric       unsigned Line = 0;
5140b57cec5SDimitry Andric       bool FoundPlus = PH.Next("+");
5150b57cec5SDimitry Andric       if (FoundPlus || PH.Next("-")) {
5160b57cec5SDimitry Andric         // Relative to current line.
5170b57cec5SDimitry Andric         PH.Advance();
5180b57cec5SDimitry Andric         bool Invalid = false;
5190b57cec5SDimitry Andric         unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
5200b57cec5SDimitry Andric         if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
5210b57cec5SDimitry Andric           if (FoundPlus) ExpectedLine += Line;
5220b57cec5SDimitry Andric           else ExpectedLine -= Line;
5230b57cec5SDimitry Andric           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
5240b57cec5SDimitry Andric         }
5250b57cec5SDimitry Andric       } else if (PH.Next(Line)) {
5260b57cec5SDimitry Andric         // Absolute line number.
5270b57cec5SDimitry Andric         if (Line > 0)
5280b57cec5SDimitry Andric           ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
5290b57cec5SDimitry Andric       } else if (PH.NextMarker()) {
5300b57cec5SDimitry Andric         Marker = PH.Match();
5310b57cec5SDimitry Andric       } else if (PP && PH.Search(":")) {
5320b57cec5SDimitry Andric         // Specific source file.
5330b57cec5SDimitry Andric         StringRef Filename(PH.C, PH.P-PH.C);
5340b57cec5SDimitry Andric         PH.Advance();
5350b57cec5SDimitry Andric 
5365ffd83dbSDimitry Andric         if (Filename == "*") {
5375ffd83dbSDimitry Andric           MatchAnyFileAndLine = true;
5385ffd83dbSDimitry Andric           if (!PH.Next("*")) {
5395ffd83dbSDimitry Andric             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
5405ffd83dbSDimitry Andric                          diag::err_verify_missing_line)
5415ffd83dbSDimitry Andric                 << "'*'";
5425ffd83dbSDimitry Andric             continue;
5435ffd83dbSDimitry Andric           }
5445ffd83dbSDimitry Andric           MatchAnyLine = true;
5455ffd83dbSDimitry Andric           ExpectedLoc = SourceLocation();
5465ffd83dbSDimitry Andric         } else {
5470b57cec5SDimitry Andric           // Lookup file via Preprocessor, like a #include.
548bdd1243dSDimitry Andric           OptionalFileEntryRef File =
54904eeddc0SDimitry Andric               PP->LookupFile(Pos, Filename, false, nullptr, nullptr, nullptr,
5500b57cec5SDimitry Andric                              nullptr, nullptr, nullptr, nullptr, nullptr);
551a7dea167SDimitry Andric           if (!File) {
5520b57cec5SDimitry Andric             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
5535ffd83dbSDimitry Andric                          diag::err_verify_missing_file)
5545ffd83dbSDimitry Andric                 << Filename << KindStr;
5550b57cec5SDimitry Andric             continue;
5560b57cec5SDimitry Andric           }
5570b57cec5SDimitry Andric 
558e8d8bef9SDimitry Andric           FileID FID = SM.translateFile(*File);
559e8d8bef9SDimitry Andric           if (FID.isInvalid())
560e8d8bef9SDimitry Andric             FID = SM.createFileID(*File, Pos, SrcMgr::C_User);
5610b57cec5SDimitry Andric 
5620b57cec5SDimitry Andric           if (PH.Next(Line) && Line > 0)
563e8d8bef9SDimitry Andric             ExpectedLoc = SM.translateLineCol(FID, Line, 1);
5640b57cec5SDimitry Andric           else if (PH.Next("*")) {
5650b57cec5SDimitry Andric             MatchAnyLine = true;
566e8d8bef9SDimitry Andric             ExpectedLoc = SM.translateLineCol(FID, 1, 1);
5670b57cec5SDimitry Andric           }
5685ffd83dbSDimitry Andric         }
5690b57cec5SDimitry Andric       } else if (PH.Next("*")) {
5700b57cec5SDimitry Andric         MatchAnyLine = true;
5710b57cec5SDimitry Andric         ExpectedLoc = SourceLocation();
5720b57cec5SDimitry Andric       }
5730b57cec5SDimitry Andric 
5740b57cec5SDimitry Andric       if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) {
5750b57cec5SDimitry Andric         Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
5760b57cec5SDimitry Andric                      diag::err_verify_missing_line) << KindStr;
5770b57cec5SDimitry Andric         continue;
5780b57cec5SDimitry Andric       }
5790b57cec5SDimitry Andric       PH.Advance();
5800b57cec5SDimitry Andric     }
5810b57cec5SDimitry Andric 
5820b57cec5SDimitry Andric     // Skip optional whitespace.
5830b57cec5SDimitry Andric     PH.SkipWhitespace();
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric     // Next optional token: positive integer or a '+'.
5860b57cec5SDimitry Andric     if (PH.Next(D.Min)) {
5870b57cec5SDimitry Andric       PH.Advance();
5880b57cec5SDimitry Andric       // A positive integer can be followed by a '+' meaning min
5890b57cec5SDimitry Andric       // or more, or by a '-' meaning a range from min to max.
5900b57cec5SDimitry Andric       if (PH.Next("+")) {
5910b57cec5SDimitry Andric         D.Max = Directive::MaxCount;
5920b57cec5SDimitry Andric         PH.Advance();
5930b57cec5SDimitry Andric       } else if (PH.Next("-")) {
5940b57cec5SDimitry Andric         PH.Advance();
5950b57cec5SDimitry Andric         if (!PH.Next(D.Max) || D.Max < D.Min) {
5960b57cec5SDimitry Andric           Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
5970b57cec5SDimitry Andric                        diag::err_verify_invalid_range) << KindStr;
5980b57cec5SDimitry Andric           continue;
5990b57cec5SDimitry Andric         }
6000b57cec5SDimitry Andric         PH.Advance();
6010b57cec5SDimitry Andric       } else {
6020b57cec5SDimitry Andric         D.Max = D.Min;
6030b57cec5SDimitry Andric       }
6040b57cec5SDimitry Andric     } else if (PH.Next("+")) {
6050b57cec5SDimitry Andric       // '+' on its own means "1 or more".
6060b57cec5SDimitry Andric       D.Max = Directive::MaxCount;
6070b57cec5SDimitry Andric       PH.Advance();
6080b57cec5SDimitry Andric     }
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric     // Skip optional whitespace.
6110b57cec5SDimitry Andric     PH.SkipWhitespace();
6120b57cec5SDimitry Andric 
6130b57cec5SDimitry Andric     // Next token: {{
6140b57cec5SDimitry Andric     if (!PH.Next("{{")) {
6150b57cec5SDimitry Andric       Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
6160b57cec5SDimitry Andric                    diag::err_verify_missing_start) << KindStr;
6170b57cec5SDimitry Andric       continue;
6180b57cec5SDimitry Andric     }
619297eecfbSDimitry Andric     llvm::SmallString<8> CloseBrace("}}");
620297eecfbSDimitry Andric     const char *const DelimBegin = PH.C;
6210b57cec5SDimitry Andric     PH.Advance();
622297eecfbSDimitry Andric     // Count the number of opening braces for `string` kinds
623297eecfbSDimitry Andric     for (; !D.RegexKind && PH.Next("{"); PH.Advance())
624297eecfbSDimitry Andric       CloseBrace += '}';
6250b57cec5SDimitry Andric     const char* const ContentBegin = PH.C; // mark content begin
626297eecfbSDimitry Andric     // Search for closing brace
627297eecfbSDimitry Andric     StringRef OpenBrace(DelimBegin, ContentBegin - DelimBegin);
628297eecfbSDimitry Andric     if (!PH.SearchClosingBrace(OpenBrace, CloseBrace)) {
6290b57cec5SDimitry Andric       Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
630297eecfbSDimitry Andric                    diag::err_verify_missing_end)
631297eecfbSDimitry Andric           << KindStr << CloseBrace;
6320b57cec5SDimitry Andric       continue;
6330b57cec5SDimitry Andric     }
6340b57cec5SDimitry Andric     const char* const ContentEnd = PH.P; // mark content end
6350b57cec5SDimitry Andric     PH.Advance();
6360b57cec5SDimitry Andric 
6370b57cec5SDimitry Andric     D.DirectivePos = Pos;
6380b57cec5SDimitry Andric     D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin);
6390b57cec5SDimitry Andric 
6400b57cec5SDimitry Andric     // Build directive text; convert \n to newlines.
6410b57cec5SDimitry Andric     StringRef NewlineStr = "\\n";
6420b57cec5SDimitry Andric     StringRef Content(ContentBegin, ContentEnd-ContentBegin);
6430b57cec5SDimitry Andric     size_t CPos = 0;
6440b57cec5SDimitry Andric     size_t FPos;
6450b57cec5SDimitry Andric     while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
6460b57cec5SDimitry Andric       D.Text += Content.substr(CPos, FPos-CPos);
6470b57cec5SDimitry Andric       D.Text += '\n';
6480b57cec5SDimitry Andric       CPos = FPos + NewlineStr.size();
6490b57cec5SDimitry Andric     }
6500b57cec5SDimitry Andric     if (D.Text.empty())
6510b57cec5SDimitry Andric       D.Text.assign(ContentBegin, ContentEnd);
6520b57cec5SDimitry Andric 
6530b57cec5SDimitry Andric     // Check that regex directives contain at least one regex.
6540b57cec5SDimitry Andric     if (D.RegexKind && D.Text.find("{{") == StringRef::npos) {
6550b57cec5SDimitry Andric       Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text;
6560b57cec5SDimitry Andric       return false;
6570b57cec5SDimitry Andric     }
6580b57cec5SDimitry Andric 
6590b57cec5SDimitry Andric     if (Marker.empty())
6605ffd83dbSDimitry Andric       attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine);
6610b57cec5SDimitry Andric     else
6620b57cec5SDimitry Andric       Markers.addDirective(Marker, D);
6630b57cec5SDimitry Andric     FoundDirective = true;
6640b57cec5SDimitry Andric   }
6650b57cec5SDimitry Andric 
6660b57cec5SDimitry Andric   return FoundDirective;
6670b57cec5SDimitry Andric }
6680b57cec5SDimitry Andric 
VerifyDiagnosticConsumer(DiagnosticsEngine & Diags_)6690b57cec5SDimitry Andric VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
6700b57cec5SDimitry Andric     : Diags(Diags_), PrimaryClient(Diags.getClient()),
6710b57cec5SDimitry Andric       PrimaryClientOwner(Diags.takeClient()),
6720b57cec5SDimitry Andric       Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
6730b57cec5SDimitry Andric       Status(HasNoDirectives) {
6740b57cec5SDimitry Andric   if (Diags.hasSourceManager())
6750b57cec5SDimitry Andric     setSourceManager(Diags.getSourceManager());
6760b57cec5SDimitry Andric }
6770b57cec5SDimitry Andric 
~VerifyDiagnosticConsumer()6780b57cec5SDimitry Andric VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
6790b57cec5SDimitry Andric   assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
6800b57cec5SDimitry Andric   assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
6810b57cec5SDimitry Andric   SrcManager = nullptr;
6820b57cec5SDimitry Andric   CheckDiagnostics();
6830b57cec5SDimitry Andric   assert(!Diags.ownsClient() &&
6840b57cec5SDimitry Andric          "The VerifyDiagnosticConsumer takes over ownership of the client!");
6850b57cec5SDimitry Andric }
6860b57cec5SDimitry Andric 
6870b57cec5SDimitry Andric // DiagnosticConsumer interface.
6880b57cec5SDimitry Andric 
BeginSourceFile(const LangOptions & LangOpts,const Preprocessor * PP)6890b57cec5SDimitry Andric void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
6900b57cec5SDimitry Andric                                                const Preprocessor *PP) {
6910b57cec5SDimitry Andric   // Attach comment handler on first invocation.
6920b57cec5SDimitry Andric   if (++ActiveSourceFiles == 1) {
6930b57cec5SDimitry Andric     if (PP) {
6940b57cec5SDimitry Andric       CurrentPreprocessor = PP;
6950b57cec5SDimitry Andric       this->LangOpts = &LangOpts;
6960b57cec5SDimitry Andric       setSourceManager(PP->getSourceManager());
6970b57cec5SDimitry Andric       const_cast<Preprocessor *>(PP)->addCommentHandler(this);
6980b57cec5SDimitry Andric #ifndef NDEBUG
6990b57cec5SDimitry Andric       // Debug build tracks parsed files.
7000b57cec5SDimitry Andric       const_cast<Preprocessor *>(PP)->addPPCallbacks(
701a7dea167SDimitry Andric                       std::make_unique<VerifyFileTracker>(*this, *SrcManager));
7020b57cec5SDimitry Andric #endif
7030b57cec5SDimitry Andric     }
7040b57cec5SDimitry Andric   }
7050b57cec5SDimitry Andric 
7060b57cec5SDimitry Andric   assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
7070b57cec5SDimitry Andric   PrimaryClient->BeginSourceFile(LangOpts, PP);
7080b57cec5SDimitry Andric }
7090b57cec5SDimitry Andric 
EndSourceFile()7100b57cec5SDimitry Andric void VerifyDiagnosticConsumer::EndSourceFile() {
7110b57cec5SDimitry Andric   assert(ActiveSourceFiles && "No active source files!");
7120b57cec5SDimitry Andric   PrimaryClient->EndSourceFile();
7130b57cec5SDimitry Andric 
7140b57cec5SDimitry Andric   // Detach comment handler once last active source file completed.
7150b57cec5SDimitry Andric   if (--ActiveSourceFiles == 0) {
7160b57cec5SDimitry Andric     if (CurrentPreprocessor)
7170b57cec5SDimitry Andric       const_cast<Preprocessor *>(CurrentPreprocessor)->
7180b57cec5SDimitry Andric           removeCommentHandler(this);
7190b57cec5SDimitry Andric 
7200b57cec5SDimitry Andric     // Diagnose any used-but-not-defined markers.
7210b57cec5SDimitry Andric     Markers->finalize();
7220b57cec5SDimitry Andric 
7230b57cec5SDimitry Andric     // Check diagnostics once last file completed.
7240b57cec5SDimitry Andric     CheckDiagnostics();
7250b57cec5SDimitry Andric     CurrentPreprocessor = nullptr;
7260b57cec5SDimitry Andric     LangOpts = nullptr;
7270b57cec5SDimitry Andric   }
7280b57cec5SDimitry Andric }
7290b57cec5SDimitry Andric 
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)7300b57cec5SDimitry Andric void VerifyDiagnosticConsumer::HandleDiagnostic(
7310b57cec5SDimitry Andric       DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
7320b57cec5SDimitry Andric   if (Info.hasSourceManager()) {
7330b57cec5SDimitry Andric     // If this diagnostic is for a different source manager, ignore it.
7340b57cec5SDimitry Andric     if (SrcManager && &Info.getSourceManager() != SrcManager)
7350b57cec5SDimitry Andric       return;
7360b57cec5SDimitry Andric 
7370b57cec5SDimitry Andric     setSourceManager(Info.getSourceManager());
7380b57cec5SDimitry Andric   }
7390b57cec5SDimitry Andric 
7400b57cec5SDimitry Andric #ifndef NDEBUG
7410b57cec5SDimitry Andric   // Debug build tracks unparsed files for possible
7420b57cec5SDimitry Andric   // unparsed expected-* directives.
7430b57cec5SDimitry Andric   if (SrcManager) {
7440b57cec5SDimitry Andric     SourceLocation Loc = Info.getLocation();
7450b57cec5SDimitry Andric     if (Loc.isValid()) {
7460b57cec5SDimitry Andric       ParsedStatus PS = IsUnparsed;
7470b57cec5SDimitry Andric 
7480b57cec5SDimitry Andric       Loc = SrcManager->getExpansionLoc(Loc);
7490b57cec5SDimitry Andric       FileID FID = SrcManager->getFileID(Loc);
7500b57cec5SDimitry Andric 
75106c3fb27SDimitry Andric       auto FE = SrcManager->getFileEntryRefForID(FID);
7520b57cec5SDimitry Andric       if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
7530b57cec5SDimitry Andric         // If the file is a modules header file it shall not be parsed
7540b57cec5SDimitry Andric         // for expected-* directives.
7550b57cec5SDimitry Andric         HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
75606c3fb27SDimitry Andric         if (HS.findModuleForHeader(*FE))
7570b57cec5SDimitry Andric           PS = IsUnparsedNoDirectives;
7580b57cec5SDimitry Andric       }
7590b57cec5SDimitry Andric 
7600b57cec5SDimitry Andric       UpdateParsedFileStatus(*SrcManager, FID, PS);
7610b57cec5SDimitry Andric     }
7620b57cec5SDimitry Andric   }
7630b57cec5SDimitry Andric #endif
7640b57cec5SDimitry Andric 
7650b57cec5SDimitry Andric   // Send the diagnostic to the buffer, we will check it once we reach the end
7660b57cec5SDimitry Andric   // of the source file (or are destructed).
7670b57cec5SDimitry Andric   Buffer->HandleDiagnostic(DiagLevel, Info);
7680b57cec5SDimitry Andric }
7690b57cec5SDimitry Andric 
7700b57cec5SDimitry Andric /// HandleComment - Hook into the preprocessor and extract comments containing
7710b57cec5SDimitry Andric ///  expected errors and warnings.
HandleComment(Preprocessor & PP,SourceRange Comment)7720b57cec5SDimitry Andric bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
7730b57cec5SDimitry Andric                                              SourceRange Comment) {
7740b57cec5SDimitry Andric   SourceManager &SM = PP.getSourceManager();
7750b57cec5SDimitry Andric 
7760b57cec5SDimitry Andric   // If this comment is for a different source manager, ignore it.
7770b57cec5SDimitry Andric   if (SrcManager && &SM != SrcManager)
7780b57cec5SDimitry Andric     return false;
7790b57cec5SDimitry Andric 
7800b57cec5SDimitry Andric   SourceLocation CommentBegin = Comment.getBegin();
7810b57cec5SDimitry Andric 
7820b57cec5SDimitry Andric   const char *CommentRaw = SM.getCharacterData(CommentBegin);
7830b57cec5SDimitry Andric   StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
7840b57cec5SDimitry Andric 
7850b57cec5SDimitry Andric   if (C.empty())
7860b57cec5SDimitry Andric     return false;
7870b57cec5SDimitry Andric 
7880b57cec5SDimitry Andric   // Fold any "\<EOL>" sequences
7890b57cec5SDimitry Andric   size_t loc = C.find('\\');
7900b57cec5SDimitry Andric   if (loc == StringRef::npos) {
7910b57cec5SDimitry Andric     ParseDirective(C, &ED, SM, &PP, CommentBegin, Status, *Markers);
7920b57cec5SDimitry Andric     return false;
7930b57cec5SDimitry Andric   }
7940b57cec5SDimitry Andric 
7950b57cec5SDimitry Andric   std::string C2;
7960b57cec5SDimitry Andric   C2.reserve(C.size());
7970b57cec5SDimitry Andric 
7980b57cec5SDimitry Andric   for (size_t last = 0;; loc = C.find('\\', last)) {
7990b57cec5SDimitry Andric     if (loc == StringRef::npos || loc == C.size()) {
8000b57cec5SDimitry Andric       C2 += C.substr(last);
8010b57cec5SDimitry Andric       break;
8020b57cec5SDimitry Andric     }
8030b57cec5SDimitry Andric     C2 += C.substr(last, loc-last);
8040b57cec5SDimitry Andric     last = loc + 1;
8050b57cec5SDimitry Andric 
8060b57cec5SDimitry Andric     if (C[last] == '\n' || C[last] == '\r') {
8070b57cec5SDimitry Andric       ++last;
8080b57cec5SDimitry Andric 
8090b57cec5SDimitry Andric       // Escape \r\n  or \n\r, but not \n\n.
8100b57cec5SDimitry Andric       if (last < C.size())
8110b57cec5SDimitry Andric         if (C[last] == '\n' || C[last] == '\r')
8120b57cec5SDimitry Andric           if (C[last] != C[last-1])
8130b57cec5SDimitry Andric             ++last;
8140b57cec5SDimitry Andric     } else {
8150b57cec5SDimitry Andric       // This was just a normal backslash.
8160b57cec5SDimitry Andric       C2 += '\\';
8170b57cec5SDimitry Andric     }
8180b57cec5SDimitry Andric   }
8190b57cec5SDimitry Andric 
8200b57cec5SDimitry Andric   if (!C2.empty())
8210b57cec5SDimitry Andric     ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status, *Markers);
8220b57cec5SDimitry Andric   return false;
8230b57cec5SDimitry Andric }
8240b57cec5SDimitry Andric 
8250b57cec5SDimitry Andric #ifndef NDEBUG
8260b57cec5SDimitry Andric /// Lex the specified source file to determine whether it contains
8270b57cec5SDimitry Andric /// any expected-* directives.  As a Lexer is used rather than a full-blown
8280b57cec5SDimitry Andric /// Preprocessor, directives inside skipped #if blocks will still be found.
8290b57cec5SDimitry Andric ///
8300b57cec5SDimitry Andric /// \return true if any directives were found.
findDirectives(SourceManager & SM,FileID FID,const LangOptions & LangOpts)8310b57cec5SDimitry Andric static bool findDirectives(SourceManager &SM, FileID FID,
8320b57cec5SDimitry Andric                            const LangOptions &LangOpts) {
8330b57cec5SDimitry Andric   // Create a raw lexer to pull all the comments out of FID.
8340b57cec5SDimitry Andric   if (FID.isInvalid())
8350b57cec5SDimitry Andric     return false;
8360b57cec5SDimitry Andric 
8370b57cec5SDimitry Andric   // Create a lexer to lex all the tokens of the main file in raw mode.
838e8d8bef9SDimitry Andric   llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
8390b57cec5SDimitry Andric   Lexer RawLex(FID, FromFile, SM, LangOpts);
8400b57cec5SDimitry Andric 
8410b57cec5SDimitry Andric   // Return comments as tokens, this is how we find expected diagnostics.
8420b57cec5SDimitry Andric   RawLex.SetCommentRetentionState(true);
8430b57cec5SDimitry Andric 
8440b57cec5SDimitry Andric   Token Tok;
8450b57cec5SDimitry Andric   Tok.setKind(tok::comment);
8460b57cec5SDimitry Andric   VerifyDiagnosticConsumer::DirectiveStatus Status =
8470b57cec5SDimitry Andric     VerifyDiagnosticConsumer::HasNoDirectives;
8480b57cec5SDimitry Andric   while (Tok.isNot(tok::eof)) {
8490b57cec5SDimitry Andric     RawLex.LexFromRawLexer(Tok);
8500b57cec5SDimitry Andric     if (!Tok.is(tok::comment)) continue;
8510b57cec5SDimitry Andric 
8520b57cec5SDimitry Andric     std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
8530b57cec5SDimitry Andric     if (Comment.empty()) continue;
8540b57cec5SDimitry Andric 
8550b57cec5SDimitry Andric     // We don't care about tracking markers for this phase.
8560b57cec5SDimitry Andric     VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
8570b57cec5SDimitry Andric 
8580b57cec5SDimitry Andric     // Find first directive.
8590b57cec5SDimitry Andric     if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
8600b57cec5SDimitry Andric                        Status, Markers))
8610b57cec5SDimitry Andric       return true;
8620b57cec5SDimitry Andric   }
8630b57cec5SDimitry Andric   return false;
8640b57cec5SDimitry Andric }
8650b57cec5SDimitry Andric #endif // !NDEBUG
8660b57cec5SDimitry Andric 
8670b57cec5SDimitry Andric /// Takes a list of diagnostics that have been generated but not matched
8680b57cec5SDimitry Andric /// 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)8690b57cec5SDimitry Andric static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
8700b57cec5SDimitry Andric                                 const_diag_iterator diag_begin,
8710b57cec5SDimitry Andric                                 const_diag_iterator diag_end,
8720b57cec5SDimitry Andric                                 const char *Kind) {
8730b57cec5SDimitry Andric   if (diag_begin == diag_end) return 0;
8740b57cec5SDimitry Andric 
8750b57cec5SDimitry Andric   SmallString<256> Fmt;
8760b57cec5SDimitry Andric   llvm::raw_svector_ostream OS(Fmt);
8770b57cec5SDimitry Andric   for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
8780b57cec5SDimitry Andric     if (I->first.isInvalid() || !SourceMgr)
8790b57cec5SDimitry Andric       OS << "\n  (frontend)";
8800b57cec5SDimitry Andric     else {
8810b57cec5SDimitry Andric       OS << "\n ";
8825f757f3fSDimitry Andric       if (OptionalFileEntryRef File =
8835f757f3fSDimitry Andric               SourceMgr->getFileEntryRefForID(SourceMgr->getFileID(I->first)))
8840b57cec5SDimitry Andric         OS << " File " << File->getName();
8850b57cec5SDimitry Andric       OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
8860b57cec5SDimitry Andric     }
8870b57cec5SDimitry Andric     OS << ": " << I->second;
8880b57cec5SDimitry Andric   }
8890b57cec5SDimitry Andric 
8905f757f3fSDimitry Andric   std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
8915f757f3fSDimitry Andric   std::string KindStr = Prefix + "-" + Kind;
8920b57cec5SDimitry Andric   Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
8935f757f3fSDimitry Andric       << KindStr << /*Unexpected=*/true << OS.str();
8940b57cec5SDimitry Andric   return std::distance(diag_begin, diag_end);
8950b57cec5SDimitry Andric }
8960b57cec5SDimitry Andric 
8970b57cec5SDimitry Andric /// Takes a list of diagnostics that were expected to have been generated
8980b57cec5SDimitry Andric /// but were not and produces a diagnostic to the user from this.
PrintExpected(DiagnosticsEngine & Diags,SourceManager & SourceMgr,std::vector<Directive * > & DL,const char * Kind)8990b57cec5SDimitry Andric static unsigned PrintExpected(DiagnosticsEngine &Diags,
9000b57cec5SDimitry Andric                               SourceManager &SourceMgr,
9010b57cec5SDimitry Andric                               std::vector<Directive *> &DL, const char *Kind) {
9020b57cec5SDimitry Andric   if (DL.empty())
9030b57cec5SDimitry Andric     return 0;
9040b57cec5SDimitry Andric 
9050b57cec5SDimitry Andric   SmallString<256> Fmt;
9060b57cec5SDimitry Andric   llvm::raw_svector_ostream OS(Fmt);
9070b57cec5SDimitry Andric   for (const auto *D : DL) {
9085ffd83dbSDimitry Andric     if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
9090b57cec5SDimitry Andric       OS << "\n  File *";
9100b57cec5SDimitry Andric     else
9110b57cec5SDimitry Andric       OS << "\n  File " << SourceMgr.getFilename(D->DiagnosticLoc);
9120b57cec5SDimitry Andric     if (D->MatchAnyLine)
9130b57cec5SDimitry Andric       OS << " Line *";
9140b57cec5SDimitry Andric     else
9150b57cec5SDimitry Andric       OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
9160b57cec5SDimitry Andric     if (D->DirectiveLoc != D->DiagnosticLoc)
9170b57cec5SDimitry Andric       OS << " (directive at "
9180b57cec5SDimitry Andric          << SourceMgr.getFilename(D->DirectiveLoc) << ':'
9190b57cec5SDimitry Andric          << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
9200b57cec5SDimitry Andric     OS << ": " << D->Text;
9210b57cec5SDimitry Andric   }
9220b57cec5SDimitry Andric 
9235f757f3fSDimitry Andric   std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
9245f757f3fSDimitry Andric   std::string KindStr = Prefix + "-" + Kind;
9250b57cec5SDimitry Andric   Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
9265f757f3fSDimitry Andric       << KindStr << /*Unexpected=*/false << OS.str();
9270b57cec5SDimitry Andric   return DL.size();
9280b57cec5SDimitry Andric }
9290b57cec5SDimitry Andric 
9300b57cec5SDimitry Andric /// Determine whether two source locations come from the same file.
IsFromSameFile(SourceManager & SM,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc)9310b57cec5SDimitry Andric static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
9320b57cec5SDimitry Andric                            SourceLocation DiagnosticLoc) {
9330b57cec5SDimitry Andric   while (DiagnosticLoc.isMacroID())
9340b57cec5SDimitry Andric     DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
9350b57cec5SDimitry Andric 
9360b57cec5SDimitry Andric   if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
9370b57cec5SDimitry Andric     return true;
9380b57cec5SDimitry Andric 
9390b57cec5SDimitry Andric   const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
9400b57cec5SDimitry Andric   if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
9410b57cec5SDimitry Andric     return true;
9420b57cec5SDimitry Andric 
9430b57cec5SDimitry Andric   return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
9440b57cec5SDimitry Andric }
9450b57cec5SDimitry Andric 
9460b57cec5SDimitry Andric /// CheckLists - Compare expected to seen diagnostic lists and return the
9470b57cec5SDimitry Andric /// 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)9480b57cec5SDimitry Andric static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
9490b57cec5SDimitry Andric                            const char *Label,
9500b57cec5SDimitry Andric                            DirectiveList &Left,
9510b57cec5SDimitry Andric                            const_diag_iterator d2_begin,
9520b57cec5SDimitry Andric                            const_diag_iterator d2_end,
9530b57cec5SDimitry Andric                            bool IgnoreUnexpected) {
9540b57cec5SDimitry Andric   std::vector<Directive *> LeftOnly;
9550b57cec5SDimitry Andric   DiagList Right(d2_begin, d2_end);
9560b57cec5SDimitry Andric 
9570b57cec5SDimitry Andric   for (auto &Owner : Left) {
9580b57cec5SDimitry Andric     Directive &D = *Owner;
9590b57cec5SDimitry Andric     unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
9600b57cec5SDimitry Andric 
9610b57cec5SDimitry Andric     for (unsigned i = 0; i < D.Max; ++i) {
9620b57cec5SDimitry Andric       DiagList::iterator II, IE;
9630b57cec5SDimitry Andric       for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
9640b57cec5SDimitry Andric         if (!D.MatchAnyLine) {
9650b57cec5SDimitry Andric           unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
9660b57cec5SDimitry Andric           if (LineNo1 != LineNo2)
9670b57cec5SDimitry Andric             continue;
9680b57cec5SDimitry Andric         }
9690b57cec5SDimitry Andric 
9705ffd83dbSDimitry Andric         if (!D.DiagnosticLoc.isInvalid() && !D.MatchAnyFileAndLine &&
9710b57cec5SDimitry Andric             !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
9720b57cec5SDimitry Andric           continue;
9730b57cec5SDimitry Andric 
9740b57cec5SDimitry Andric         const std::string &RightText = II->second;
9750b57cec5SDimitry Andric         if (D.match(RightText))
9760b57cec5SDimitry Andric           break;
9770b57cec5SDimitry Andric       }
9780b57cec5SDimitry Andric       if (II == IE) {
9790b57cec5SDimitry Andric         // Not found.
9800b57cec5SDimitry Andric         if (i >= D.Min) break;
9810b57cec5SDimitry Andric         LeftOnly.push_back(&D);
9820b57cec5SDimitry Andric       } else {
9830b57cec5SDimitry Andric         // Found. The same cannot be found twice.
9840b57cec5SDimitry Andric         Right.erase(II);
9850b57cec5SDimitry Andric       }
9860b57cec5SDimitry Andric     }
9870b57cec5SDimitry Andric   }
9880b57cec5SDimitry Andric   // Now all that's left in Right are those that were not matched.
9890b57cec5SDimitry Andric   unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
9900b57cec5SDimitry Andric   if (!IgnoreUnexpected)
9910b57cec5SDimitry Andric     num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
9920b57cec5SDimitry Andric   return num;
9930b57cec5SDimitry Andric }
9940b57cec5SDimitry Andric 
9950b57cec5SDimitry Andric /// CheckResults - This compares the expected results to those that
9960b57cec5SDimitry Andric /// were actually reported. It emits any discrepencies. Return "true" if there
9970b57cec5SDimitry Andric /// were problems. Return "false" otherwise.
CheckResults(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const TextDiagnosticBuffer & Buffer,ExpectedData & ED)9980b57cec5SDimitry Andric static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
9990b57cec5SDimitry Andric                              const TextDiagnosticBuffer &Buffer,
10000b57cec5SDimitry Andric                              ExpectedData &ED) {
10010b57cec5SDimitry Andric   // We want to capture the delta between what was expected and what was
10020b57cec5SDimitry Andric   // seen.
10030b57cec5SDimitry Andric   //
10040b57cec5SDimitry Andric   //   Expected \ Seen - set expected but not seen
10050b57cec5SDimitry Andric   //   Seen \ Expected - set seen but not expected
10060b57cec5SDimitry Andric   unsigned NumProblems = 0;
10070b57cec5SDimitry Andric 
10080b57cec5SDimitry Andric   const DiagnosticLevelMask DiagMask =
10090b57cec5SDimitry Andric     Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
10100b57cec5SDimitry Andric 
10110b57cec5SDimitry Andric   // See if there are error mismatches.
10120b57cec5SDimitry Andric   NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
10130b57cec5SDimitry Andric                             Buffer.err_begin(), Buffer.err_end(),
10140b57cec5SDimitry Andric                             bool(DiagnosticLevelMask::Error & DiagMask));
10150b57cec5SDimitry Andric 
10160b57cec5SDimitry Andric   // See if there are warning mismatches.
10170b57cec5SDimitry Andric   NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
10180b57cec5SDimitry Andric                             Buffer.warn_begin(), Buffer.warn_end(),
10190b57cec5SDimitry Andric                             bool(DiagnosticLevelMask::Warning & DiagMask));
10200b57cec5SDimitry Andric 
10210b57cec5SDimitry Andric   // See if there are remark mismatches.
10220b57cec5SDimitry Andric   NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
10230b57cec5SDimitry Andric                             Buffer.remark_begin(), Buffer.remark_end(),
10240b57cec5SDimitry Andric                             bool(DiagnosticLevelMask::Remark & DiagMask));
10250b57cec5SDimitry Andric 
10260b57cec5SDimitry Andric   // See if there are note mismatches.
10270b57cec5SDimitry Andric   NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
10280b57cec5SDimitry Andric                             Buffer.note_begin(), Buffer.note_end(),
10290b57cec5SDimitry Andric                             bool(DiagnosticLevelMask::Note & DiagMask));
10300b57cec5SDimitry Andric 
10310b57cec5SDimitry Andric   return NumProblems;
10320b57cec5SDimitry Andric }
10330b57cec5SDimitry Andric 
UpdateParsedFileStatus(SourceManager & SM,FileID FID,ParsedStatus PS)10340b57cec5SDimitry Andric void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
10350b57cec5SDimitry Andric                                                       FileID FID,
10360b57cec5SDimitry Andric                                                       ParsedStatus PS) {
10370b57cec5SDimitry Andric   // Check SourceManager hasn't changed.
10380b57cec5SDimitry Andric   setSourceManager(SM);
10390b57cec5SDimitry Andric 
10400b57cec5SDimitry Andric #ifndef NDEBUG
10410b57cec5SDimitry Andric   if (FID.isInvalid())
10420b57cec5SDimitry Andric     return;
10430b57cec5SDimitry Andric 
10445f757f3fSDimitry Andric   OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID);
10450b57cec5SDimitry Andric 
10460b57cec5SDimitry Andric   if (PS == IsParsed) {
10470b57cec5SDimitry Andric     // Move the FileID from the unparsed set to the parsed set.
10480b57cec5SDimitry Andric     UnparsedFiles.erase(FID);
10495f757f3fSDimitry Andric     ParsedFiles.insert(std::make_pair(FID, FE ? &FE->getFileEntry() : nullptr));
10500b57cec5SDimitry Andric   } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
10510b57cec5SDimitry Andric     // Add the FileID to the unparsed set if we haven't seen it before.
10520b57cec5SDimitry Andric 
10530b57cec5SDimitry Andric     // Check for directives.
10540b57cec5SDimitry Andric     bool FoundDirectives;
10550b57cec5SDimitry Andric     if (PS == IsUnparsedNoDirectives)
10560b57cec5SDimitry Andric       FoundDirectives = false;
10570b57cec5SDimitry Andric     else
10580b57cec5SDimitry Andric       FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
10590b57cec5SDimitry Andric 
10600b57cec5SDimitry Andric     // Add the FileID to the unparsed set.
10610b57cec5SDimitry Andric     UnparsedFiles.insert(std::make_pair(FID,
10620b57cec5SDimitry Andric                                       UnparsedFileStatus(FE, FoundDirectives)));
10630b57cec5SDimitry Andric   }
10640b57cec5SDimitry Andric #endif
10650b57cec5SDimitry Andric }
10660b57cec5SDimitry Andric 
CheckDiagnostics()10670b57cec5SDimitry Andric void VerifyDiagnosticConsumer::CheckDiagnostics() {
10680b57cec5SDimitry Andric   // Ensure any diagnostics go to the primary client.
10690b57cec5SDimitry Andric   DiagnosticConsumer *CurClient = Diags.getClient();
10700b57cec5SDimitry Andric   std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
10710b57cec5SDimitry Andric   Diags.setClient(PrimaryClient, false);
10720b57cec5SDimitry Andric 
10730b57cec5SDimitry Andric #ifndef NDEBUG
10740b57cec5SDimitry Andric   // In a debug build, scan through any files that may have been missed
10750b57cec5SDimitry Andric   // during parsing and issue a fatal error if directives are contained
10760b57cec5SDimitry Andric   // within these files.  If a fatal error occurs, this suggests that
10770b57cec5SDimitry Andric   // this file is being parsed separately from the main file, in which
10780b57cec5SDimitry Andric   // case consider moving the directives to the correct place, if this
10790b57cec5SDimitry Andric   // is applicable.
10800b57cec5SDimitry Andric   if (!UnparsedFiles.empty()) {
10810b57cec5SDimitry Andric     // Generate a cache of parsed FileEntry pointers for alias lookups.
10820b57cec5SDimitry Andric     llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
10830b57cec5SDimitry Andric     for (const auto &I : ParsedFiles)
10840b57cec5SDimitry Andric       if (const FileEntry *FE = I.second)
10850b57cec5SDimitry Andric         ParsedFileCache.insert(FE);
10860b57cec5SDimitry Andric 
10870b57cec5SDimitry Andric     // Iterate through list of unparsed files.
10880b57cec5SDimitry Andric     for (const auto &I : UnparsedFiles) {
10890b57cec5SDimitry Andric       const UnparsedFileStatus &Status = I.second;
10905f757f3fSDimitry Andric       OptionalFileEntryRef FE = Status.getFile();
10910b57cec5SDimitry Andric 
10920b57cec5SDimitry Andric       // Skip files that have been parsed via an alias.
10935f757f3fSDimitry Andric       if (FE && ParsedFileCache.count(*FE))
10940b57cec5SDimitry Andric         continue;
10950b57cec5SDimitry Andric 
10960b57cec5SDimitry Andric       // Report a fatal error if this file contained directives.
10970b57cec5SDimitry Andric       if (Status.foundDirectives()) {
10985f757f3fSDimitry Andric         llvm::report_fatal_error("-verify directives found after rather"
10995f757f3fSDimitry Andric                                  " than during normal parsing of " +
11005f757f3fSDimitry Andric                                  (FE ? FE->getName() : "(unknown)"));
11010b57cec5SDimitry Andric       }
11020b57cec5SDimitry Andric     }
11030b57cec5SDimitry Andric 
11040b57cec5SDimitry Andric     // UnparsedFiles has been processed now, so clear it.
11050b57cec5SDimitry Andric     UnparsedFiles.clear();
11060b57cec5SDimitry Andric   }
11070b57cec5SDimitry Andric #endif // !NDEBUG
11080b57cec5SDimitry Andric 
11090b57cec5SDimitry Andric   if (SrcManager) {
11100b57cec5SDimitry Andric     // Produce an error if no expected-* directives could be found in the
11110b57cec5SDimitry Andric     // source file(s) processed.
11120b57cec5SDimitry Andric     if (Status == HasNoDirectives) {
1113*0fca6ea1SDimitry Andric       Diags.Report(diag::err_verify_no_directives).setForceEmit()
1114*0fca6ea1SDimitry Andric           << DetailedErrorString(Diags);
11150b57cec5SDimitry Andric       ++NumErrors;
11160b57cec5SDimitry Andric       Status = HasNoDirectivesReported;
11170b57cec5SDimitry Andric     }
11180b57cec5SDimitry Andric 
11190b57cec5SDimitry Andric     // Check that the expected diagnostics occurred.
11200b57cec5SDimitry Andric     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
11210b57cec5SDimitry Andric   } else {
11220b57cec5SDimitry Andric     const DiagnosticLevelMask DiagMask =
11230b57cec5SDimitry Andric         ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();
11240b57cec5SDimitry Andric     if (bool(DiagnosticLevelMask::Error & DiagMask))
11250b57cec5SDimitry Andric       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
11260b57cec5SDimitry Andric                                    Buffer->err_end(), "error");
11270b57cec5SDimitry Andric     if (bool(DiagnosticLevelMask::Warning & DiagMask))
11280b57cec5SDimitry Andric       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
11290b57cec5SDimitry Andric                                    Buffer->warn_end(), "warn");
11300b57cec5SDimitry Andric     if (bool(DiagnosticLevelMask::Remark & DiagMask))
11310b57cec5SDimitry Andric       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(),
11320b57cec5SDimitry Andric                                    Buffer->remark_end(), "remark");
11330b57cec5SDimitry Andric     if (bool(DiagnosticLevelMask::Note & DiagMask))
11340b57cec5SDimitry Andric       NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
11350b57cec5SDimitry Andric                                    Buffer->note_end(), "note");
11360b57cec5SDimitry Andric   }
11370b57cec5SDimitry Andric 
11380b57cec5SDimitry Andric   Diags.setClient(CurClient, Owner.release() != nullptr);
11390b57cec5SDimitry Andric 
11400b57cec5SDimitry Andric   // Reset the buffer, we have processed all the diagnostics in it.
11410b57cec5SDimitry Andric   Buffer.reset(new TextDiagnosticBuffer());
11420b57cec5SDimitry Andric   ED.Reset();
11430b57cec5SDimitry Andric }
11440b57cec5SDimitry Andric 
create(bool RegexKind,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)11450b57cec5SDimitry Andric std::unique_ptr<Directive> Directive::create(bool RegexKind,
11460b57cec5SDimitry Andric                                              SourceLocation DirectiveLoc,
11470b57cec5SDimitry Andric                                              SourceLocation DiagnosticLoc,
11485ffd83dbSDimitry Andric                                              bool MatchAnyFileAndLine,
11490b57cec5SDimitry Andric                                              bool MatchAnyLine, StringRef Text,
11500b57cec5SDimitry Andric                                              unsigned Min, unsigned Max) {
11510b57cec5SDimitry Andric   if (!RegexKind)
1152a7dea167SDimitry Andric     return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
11535ffd83dbSDimitry Andric                                                MatchAnyFileAndLine,
11540b57cec5SDimitry Andric                                                MatchAnyLine, Text, Min, Max);
11550b57cec5SDimitry Andric 
11560b57cec5SDimitry Andric   // Parse the directive into a regular expression.
11570b57cec5SDimitry Andric   std::string RegexStr;
11580b57cec5SDimitry Andric   StringRef S = Text;
11590b57cec5SDimitry Andric   while (!S.empty()) {
1160647cbc5dSDimitry Andric     if (S.consume_front("{{")) {
11610b57cec5SDimitry Andric       size_t RegexMatchLength = S.find("}}");
11620b57cec5SDimitry Andric       assert(RegexMatchLength != StringRef::npos);
11630b57cec5SDimitry Andric       // Append the regex, enclosed in parentheses.
11640b57cec5SDimitry Andric       RegexStr += "(";
11650b57cec5SDimitry Andric       RegexStr.append(S.data(), RegexMatchLength);
11660b57cec5SDimitry Andric       RegexStr += ")";
11670b57cec5SDimitry Andric       S = S.drop_front(RegexMatchLength + 2);
11680b57cec5SDimitry Andric     } else {
11690b57cec5SDimitry Andric       size_t VerbatimMatchLength = S.find("{{");
11700b57cec5SDimitry Andric       if (VerbatimMatchLength == StringRef::npos)
11710b57cec5SDimitry Andric         VerbatimMatchLength = S.size();
11720b57cec5SDimitry Andric       // Escape and append the fixed string.
11730b57cec5SDimitry Andric       RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
11740b57cec5SDimitry Andric       S = S.drop_front(VerbatimMatchLength);
11750b57cec5SDimitry Andric     }
11760b57cec5SDimitry Andric   }
11770b57cec5SDimitry Andric 
11785ffd83dbSDimitry Andric   return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc,
11795ffd83dbSDimitry Andric                                           MatchAnyFileAndLine, MatchAnyLine,
11805ffd83dbSDimitry Andric                                           Text, Min, Max, RegexStr);
11810b57cec5SDimitry Andric }
1182