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: 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. 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: 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 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 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: 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 1150b57cec5SDimitry Andric bool isValid(std::string &Error) override { 1160b57cec5SDimitry Andric return Regex.isValid(Error); 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 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: 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. 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. 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_-]+/. 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. 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. 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. 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. 2550b57cec5SDimitry Andric StringRef Match() { return StringRef(P, PEnd - P); } 2560b57cec5SDimitry Andric 2570b57cec5SDimitry Andric // Skip zero or more whitespace. 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. 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. 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: 3410b57cec5SDimitry Andric MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {} 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric // Register a marker. 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. 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. 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 3990b57cec5SDimitry Andric /// ParseDirective - Go through the comment and see if it indicates expected 4000b57cec5SDimitry Andric /// diagnostics. If so, then put them in the appropriate directive list. 4010b57cec5SDimitry Andric /// 4020b57cec5SDimitry Andric /// Returns true if any valid directives were found. 4030b57cec5SDimitry Andric static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, 4040b57cec5SDimitry Andric Preprocessor *PP, SourceLocation Pos, 4050b57cec5SDimitry Andric VerifyDiagnosticConsumer::DirectiveStatus &Status, 4060b57cec5SDimitry Andric VerifyDiagnosticConsumer::MarkerTracker &Markers) { 4070b57cec5SDimitry Andric DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); 4080b57cec5SDimitry Andric 4090b57cec5SDimitry Andric // First, scan the comment looking for markers. 4100b57cec5SDimitry Andric for (ParseHelper PH(S); !PH.Done();) { 4110b57cec5SDimitry Andric if (!PH.Search("#", true)) 4120b57cec5SDimitry Andric break; 4130b57cec5SDimitry Andric PH.C = PH.P; 4140b57cec5SDimitry Andric if (!PH.NextMarker()) { 4150b57cec5SDimitry Andric PH.Next("#"); 4160b57cec5SDimitry Andric PH.Advance(); 4170b57cec5SDimitry Andric continue; 4180b57cec5SDimitry Andric } 4190b57cec5SDimitry Andric PH.Advance(); 4200b57cec5SDimitry Andric Markers.addMarker(PH.Match(), Pos); 4210b57cec5SDimitry Andric } 4220b57cec5SDimitry Andric 4230b57cec5SDimitry Andric // A single comment may contain multiple directives. 4240b57cec5SDimitry Andric bool FoundDirective = false; 4250b57cec5SDimitry Andric for (ParseHelper PH(S); !PH.Done();) { 4260b57cec5SDimitry Andric // Search for the initial directive token. 4270b57cec5SDimitry Andric // If one prefix, save time by searching only for its directives. 4280b57cec5SDimitry Andric // Otherwise, search for any potential directive token and check it later. 4290b57cec5SDimitry Andric const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; 4300b57cec5SDimitry Andric if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) 4310b57cec5SDimitry Andric : PH.Search("", true, true))) 4320b57cec5SDimitry Andric break; 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric StringRef DToken = PH.Match(); 4350b57cec5SDimitry Andric PH.Advance(); 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric // Default directive kind. 4380b57cec5SDimitry Andric UnattachedDirective D; 4390b57cec5SDimitry Andric const char *KindStr = "string"; 4400b57cec5SDimitry Andric 4410b57cec5SDimitry Andric // Parse the initial directive token in reverse so we can easily determine 4420b57cec5SDimitry Andric // its exact actual prefix. If we were to parse it from the front instead, 4430b57cec5SDimitry Andric // it would be harder to determine where the prefix ends because there 4440b57cec5SDimitry Andric // might be multiple matching -verify prefixes because some might prefix 4450b57cec5SDimitry Andric // others. 4460b57cec5SDimitry Andric 4470b57cec5SDimitry Andric // Regex in initial directive token: -re 4485f757f3fSDimitry Andric if (DToken.ends_with("-re")) { 4490b57cec5SDimitry Andric D.RegexKind = true; 4500b57cec5SDimitry Andric KindStr = "regex"; 4510b57cec5SDimitry Andric DToken = DToken.substr(0, DToken.size()-3); 4520b57cec5SDimitry Andric } 4530b57cec5SDimitry Andric 4540b57cec5SDimitry Andric // Type in initial directive token: -{error|warning|note|no-diagnostics} 4550b57cec5SDimitry Andric bool NoDiag = false; 4560b57cec5SDimitry Andric StringRef DType; 4575f757f3fSDimitry Andric if (DToken.ends_with(DType = "-error")) 4580b57cec5SDimitry Andric D.DL = ED ? &ED->Errors : nullptr; 4595f757f3fSDimitry Andric else if (DToken.ends_with(DType = "-warning")) 4600b57cec5SDimitry Andric D.DL = ED ? &ED->Warnings : nullptr; 4615f757f3fSDimitry Andric else if (DToken.ends_with(DType = "-remark")) 4620b57cec5SDimitry Andric D.DL = ED ? &ED->Remarks : nullptr; 4635f757f3fSDimitry Andric else if (DToken.ends_with(DType = "-note")) 4640b57cec5SDimitry Andric D.DL = ED ? &ED->Notes : nullptr; 4655f757f3fSDimitry Andric else if (DToken.ends_with(DType = "-no-diagnostics")) { 4660b57cec5SDimitry Andric NoDiag = true; 4670b57cec5SDimitry Andric if (D.RegexKind) 4680b57cec5SDimitry Andric continue; 4695f757f3fSDimitry Andric } else 4700b57cec5SDimitry Andric continue; 4710b57cec5SDimitry Andric DToken = DToken.substr(0, DToken.size()-DType.size()); 4720b57cec5SDimitry Andric 4730b57cec5SDimitry Andric // What's left in DToken is the actual prefix. That might not be a -verify 4740b57cec5SDimitry Andric // prefix even if there is only one -verify prefix (for example, the full 4750b57cec5SDimitry Andric // DToken is foo-bar-warning, but foo is the only -verify prefix). 4760b57cec5SDimitry Andric if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) 4770b57cec5SDimitry Andric continue; 4780b57cec5SDimitry Andric 4790b57cec5SDimitry Andric if (NoDiag) { 4800b57cec5SDimitry Andric if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 4810b57cec5SDimitry Andric Diags.Report(Pos, diag::err_verify_invalid_no_diags) 4820b57cec5SDimitry Andric << /*IsExpectedNoDiagnostics=*/true; 4830b57cec5SDimitry Andric else 4840b57cec5SDimitry Andric Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 4850b57cec5SDimitry Andric continue; 4860b57cec5SDimitry Andric } 4870b57cec5SDimitry Andric if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 4880b57cec5SDimitry Andric Diags.Report(Pos, diag::err_verify_invalid_no_diags) 4890b57cec5SDimitry Andric << /*IsExpectedNoDiagnostics=*/false; 4900b57cec5SDimitry Andric continue; 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric // If a directive has been found but we're not interested 4950b57cec5SDimitry Andric // in storing the directive information, return now. 4960b57cec5SDimitry Andric if (!D.DL) 4970b57cec5SDimitry Andric return true; 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric // Next optional token: @ 5000b57cec5SDimitry Andric SourceLocation ExpectedLoc; 5010b57cec5SDimitry Andric StringRef Marker; 5025ffd83dbSDimitry Andric bool MatchAnyFileAndLine = false; 5030b57cec5SDimitry Andric bool MatchAnyLine = false; 5040b57cec5SDimitry Andric if (!PH.Next("@")) { 5050b57cec5SDimitry Andric ExpectedLoc = Pos; 5060b57cec5SDimitry Andric } else { 5070b57cec5SDimitry Andric PH.Advance(); 5080b57cec5SDimitry Andric unsigned Line = 0; 5090b57cec5SDimitry Andric bool FoundPlus = PH.Next("+"); 5100b57cec5SDimitry Andric if (FoundPlus || PH.Next("-")) { 5110b57cec5SDimitry Andric // Relative to current line. 5120b57cec5SDimitry Andric PH.Advance(); 5130b57cec5SDimitry Andric bool Invalid = false; 5140b57cec5SDimitry Andric unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 5150b57cec5SDimitry Andric if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 5160b57cec5SDimitry Andric if (FoundPlus) ExpectedLine += Line; 5170b57cec5SDimitry Andric else ExpectedLine -= Line; 5180b57cec5SDimitry Andric ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 5190b57cec5SDimitry Andric } 5200b57cec5SDimitry Andric } else if (PH.Next(Line)) { 5210b57cec5SDimitry Andric // Absolute line number. 5220b57cec5SDimitry Andric if (Line > 0) 5230b57cec5SDimitry Andric ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 5240b57cec5SDimitry Andric } else if (PH.NextMarker()) { 5250b57cec5SDimitry Andric Marker = PH.Match(); 5260b57cec5SDimitry Andric } else if (PP && PH.Search(":")) { 5270b57cec5SDimitry Andric // Specific source file. 5280b57cec5SDimitry Andric StringRef Filename(PH.C, PH.P-PH.C); 5290b57cec5SDimitry Andric PH.Advance(); 5300b57cec5SDimitry Andric 5315ffd83dbSDimitry Andric if (Filename == "*") { 5325ffd83dbSDimitry Andric MatchAnyFileAndLine = true; 5335ffd83dbSDimitry Andric if (!PH.Next("*")) { 5345ffd83dbSDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), 5355ffd83dbSDimitry Andric diag::err_verify_missing_line) 5365ffd83dbSDimitry Andric << "'*'"; 5375ffd83dbSDimitry Andric continue; 5385ffd83dbSDimitry Andric } 5395ffd83dbSDimitry Andric MatchAnyLine = true; 5405ffd83dbSDimitry Andric ExpectedLoc = SourceLocation(); 5415ffd83dbSDimitry Andric } else { 5420b57cec5SDimitry Andric // Lookup file via Preprocessor, like a #include. 543bdd1243dSDimitry Andric OptionalFileEntryRef File = 54404eeddc0SDimitry Andric PP->LookupFile(Pos, Filename, false, nullptr, nullptr, nullptr, 5450b57cec5SDimitry Andric nullptr, nullptr, nullptr, nullptr, nullptr); 546a7dea167SDimitry Andric if (!File) { 5470b57cec5SDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), 5485ffd83dbSDimitry Andric diag::err_verify_missing_file) 5495ffd83dbSDimitry Andric << Filename << KindStr; 5500b57cec5SDimitry Andric continue; 5510b57cec5SDimitry Andric } 5520b57cec5SDimitry Andric 553e8d8bef9SDimitry Andric FileID FID = SM.translateFile(*File); 554e8d8bef9SDimitry Andric if (FID.isInvalid()) 555e8d8bef9SDimitry Andric FID = SM.createFileID(*File, Pos, SrcMgr::C_User); 5560b57cec5SDimitry Andric 5570b57cec5SDimitry Andric if (PH.Next(Line) && Line > 0) 558e8d8bef9SDimitry Andric ExpectedLoc = SM.translateLineCol(FID, Line, 1); 5590b57cec5SDimitry Andric else if (PH.Next("*")) { 5600b57cec5SDimitry Andric MatchAnyLine = true; 561e8d8bef9SDimitry Andric ExpectedLoc = SM.translateLineCol(FID, 1, 1); 5620b57cec5SDimitry Andric } 5635ffd83dbSDimitry Andric } 5640b57cec5SDimitry Andric } else if (PH.Next("*")) { 5650b57cec5SDimitry Andric MatchAnyLine = true; 5660b57cec5SDimitry Andric ExpectedLoc = SourceLocation(); 5670b57cec5SDimitry Andric } 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) { 5700b57cec5SDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 5710b57cec5SDimitry Andric diag::err_verify_missing_line) << KindStr; 5720b57cec5SDimitry Andric continue; 5730b57cec5SDimitry Andric } 5740b57cec5SDimitry Andric PH.Advance(); 5750b57cec5SDimitry Andric } 5760b57cec5SDimitry Andric 5770b57cec5SDimitry Andric // Skip optional whitespace. 5780b57cec5SDimitry Andric PH.SkipWhitespace(); 5790b57cec5SDimitry Andric 5800b57cec5SDimitry Andric // Next optional token: positive integer or a '+'. 5810b57cec5SDimitry Andric if (PH.Next(D.Min)) { 5820b57cec5SDimitry Andric PH.Advance(); 5830b57cec5SDimitry Andric // A positive integer can be followed by a '+' meaning min 5840b57cec5SDimitry Andric // or more, or by a '-' meaning a range from min to max. 5850b57cec5SDimitry Andric if (PH.Next("+")) { 5860b57cec5SDimitry Andric D.Max = Directive::MaxCount; 5870b57cec5SDimitry Andric PH.Advance(); 5880b57cec5SDimitry Andric } else if (PH.Next("-")) { 5890b57cec5SDimitry Andric PH.Advance(); 5900b57cec5SDimitry Andric if (!PH.Next(D.Max) || D.Max < D.Min) { 5910b57cec5SDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 5920b57cec5SDimitry Andric diag::err_verify_invalid_range) << KindStr; 5930b57cec5SDimitry Andric continue; 5940b57cec5SDimitry Andric } 5950b57cec5SDimitry Andric PH.Advance(); 5960b57cec5SDimitry Andric } else { 5970b57cec5SDimitry Andric D.Max = D.Min; 5980b57cec5SDimitry Andric } 5990b57cec5SDimitry Andric } else if (PH.Next("+")) { 6000b57cec5SDimitry Andric // '+' on its own means "1 or more". 6010b57cec5SDimitry Andric D.Max = Directive::MaxCount; 6020b57cec5SDimitry Andric PH.Advance(); 6030b57cec5SDimitry Andric } 6040b57cec5SDimitry Andric 6050b57cec5SDimitry Andric // Skip optional whitespace. 6060b57cec5SDimitry Andric PH.SkipWhitespace(); 6070b57cec5SDimitry Andric 6080b57cec5SDimitry Andric // Next token: {{ 6090b57cec5SDimitry Andric if (!PH.Next("{{")) { 6100b57cec5SDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 6110b57cec5SDimitry Andric diag::err_verify_missing_start) << KindStr; 6120b57cec5SDimitry Andric continue; 6130b57cec5SDimitry Andric } 6140b57cec5SDimitry Andric PH.Advance(); 6150b57cec5SDimitry Andric const char* const ContentBegin = PH.C; // mark content begin 6160b57cec5SDimitry Andric // Search for token: }} 6170b57cec5SDimitry Andric if (!PH.SearchClosingBrace("{{", "}}")) { 6180b57cec5SDimitry Andric Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 6190b57cec5SDimitry Andric diag::err_verify_missing_end) << KindStr; 6200b57cec5SDimitry Andric continue; 6210b57cec5SDimitry Andric } 6220b57cec5SDimitry Andric const char* const ContentEnd = PH.P; // mark content end 6230b57cec5SDimitry Andric PH.Advance(); 6240b57cec5SDimitry Andric 6250b57cec5SDimitry Andric D.DirectivePos = Pos; 6260b57cec5SDimitry Andric D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin); 6270b57cec5SDimitry Andric 6280b57cec5SDimitry Andric // Build directive text; convert \n to newlines. 6290b57cec5SDimitry Andric StringRef NewlineStr = "\\n"; 6300b57cec5SDimitry Andric StringRef Content(ContentBegin, ContentEnd-ContentBegin); 6310b57cec5SDimitry Andric size_t CPos = 0; 6320b57cec5SDimitry Andric size_t FPos; 6330b57cec5SDimitry Andric while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 6340b57cec5SDimitry Andric D.Text += Content.substr(CPos, FPos-CPos); 6350b57cec5SDimitry Andric D.Text += '\n'; 6360b57cec5SDimitry Andric CPos = FPos + NewlineStr.size(); 6370b57cec5SDimitry Andric } 6380b57cec5SDimitry Andric if (D.Text.empty()) 6390b57cec5SDimitry Andric D.Text.assign(ContentBegin, ContentEnd); 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric // Check that regex directives contain at least one regex. 6420b57cec5SDimitry Andric if (D.RegexKind && D.Text.find("{{") == StringRef::npos) { 6430b57cec5SDimitry Andric Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text; 6440b57cec5SDimitry Andric return false; 6450b57cec5SDimitry Andric } 6460b57cec5SDimitry Andric 6470b57cec5SDimitry Andric if (Marker.empty()) 6485ffd83dbSDimitry Andric attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine); 6490b57cec5SDimitry Andric else 6500b57cec5SDimitry Andric Markers.addDirective(Marker, D); 6510b57cec5SDimitry Andric FoundDirective = true; 6520b57cec5SDimitry Andric } 6530b57cec5SDimitry Andric 6540b57cec5SDimitry Andric return FoundDirective; 6550b57cec5SDimitry Andric } 6560b57cec5SDimitry Andric 6570b57cec5SDimitry Andric VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) 6580b57cec5SDimitry Andric : Diags(Diags_), PrimaryClient(Diags.getClient()), 6590b57cec5SDimitry Andric PrimaryClientOwner(Diags.takeClient()), 6600b57cec5SDimitry Andric Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)), 6610b57cec5SDimitry Andric Status(HasNoDirectives) { 6620b57cec5SDimitry Andric if (Diags.hasSourceManager()) 6630b57cec5SDimitry Andric setSourceManager(Diags.getSourceManager()); 6640b57cec5SDimitry Andric } 6650b57cec5SDimitry Andric 6660b57cec5SDimitry Andric VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { 6670b57cec5SDimitry Andric assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); 6680b57cec5SDimitry Andric assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); 6690b57cec5SDimitry Andric SrcManager = nullptr; 6700b57cec5SDimitry Andric CheckDiagnostics(); 6710b57cec5SDimitry Andric assert(!Diags.ownsClient() && 6720b57cec5SDimitry Andric "The VerifyDiagnosticConsumer takes over ownership of the client!"); 6730b57cec5SDimitry Andric } 6740b57cec5SDimitry Andric 6750b57cec5SDimitry Andric // DiagnosticConsumer interface. 6760b57cec5SDimitry Andric 6770b57cec5SDimitry Andric void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 6780b57cec5SDimitry Andric const Preprocessor *PP) { 6790b57cec5SDimitry Andric // Attach comment handler on first invocation. 6800b57cec5SDimitry Andric if (++ActiveSourceFiles == 1) { 6810b57cec5SDimitry Andric if (PP) { 6820b57cec5SDimitry Andric CurrentPreprocessor = PP; 6830b57cec5SDimitry Andric this->LangOpts = &LangOpts; 6840b57cec5SDimitry Andric setSourceManager(PP->getSourceManager()); 6850b57cec5SDimitry Andric const_cast<Preprocessor *>(PP)->addCommentHandler(this); 6860b57cec5SDimitry Andric #ifndef NDEBUG 6870b57cec5SDimitry Andric // Debug build tracks parsed files. 6880b57cec5SDimitry Andric const_cast<Preprocessor *>(PP)->addPPCallbacks( 689a7dea167SDimitry Andric std::make_unique<VerifyFileTracker>(*this, *SrcManager)); 6900b57cec5SDimitry Andric #endif 6910b57cec5SDimitry Andric } 6920b57cec5SDimitry Andric } 6930b57cec5SDimitry Andric 6940b57cec5SDimitry Andric assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); 6950b57cec5SDimitry Andric PrimaryClient->BeginSourceFile(LangOpts, PP); 6960b57cec5SDimitry Andric } 6970b57cec5SDimitry Andric 6980b57cec5SDimitry Andric void VerifyDiagnosticConsumer::EndSourceFile() { 6990b57cec5SDimitry Andric assert(ActiveSourceFiles && "No active source files!"); 7000b57cec5SDimitry Andric PrimaryClient->EndSourceFile(); 7010b57cec5SDimitry Andric 7020b57cec5SDimitry Andric // Detach comment handler once last active source file completed. 7030b57cec5SDimitry Andric if (--ActiveSourceFiles == 0) { 7040b57cec5SDimitry Andric if (CurrentPreprocessor) 7050b57cec5SDimitry Andric const_cast<Preprocessor *>(CurrentPreprocessor)-> 7060b57cec5SDimitry Andric removeCommentHandler(this); 7070b57cec5SDimitry Andric 7080b57cec5SDimitry Andric // Diagnose any used-but-not-defined markers. 7090b57cec5SDimitry Andric Markers->finalize(); 7100b57cec5SDimitry Andric 7110b57cec5SDimitry Andric // Check diagnostics once last file completed. 7120b57cec5SDimitry Andric CheckDiagnostics(); 7130b57cec5SDimitry Andric CurrentPreprocessor = nullptr; 7140b57cec5SDimitry Andric LangOpts = nullptr; 7150b57cec5SDimitry Andric } 7160b57cec5SDimitry Andric } 7170b57cec5SDimitry Andric 7180b57cec5SDimitry Andric void VerifyDiagnosticConsumer::HandleDiagnostic( 7190b57cec5SDimitry Andric DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 7200b57cec5SDimitry Andric if (Info.hasSourceManager()) { 7210b57cec5SDimitry Andric // If this diagnostic is for a different source manager, ignore it. 7220b57cec5SDimitry Andric if (SrcManager && &Info.getSourceManager() != SrcManager) 7230b57cec5SDimitry Andric return; 7240b57cec5SDimitry Andric 7250b57cec5SDimitry Andric setSourceManager(Info.getSourceManager()); 7260b57cec5SDimitry Andric } 7270b57cec5SDimitry Andric 7280b57cec5SDimitry Andric #ifndef NDEBUG 7290b57cec5SDimitry Andric // Debug build tracks unparsed files for possible 7300b57cec5SDimitry Andric // unparsed expected-* directives. 7310b57cec5SDimitry Andric if (SrcManager) { 7320b57cec5SDimitry Andric SourceLocation Loc = Info.getLocation(); 7330b57cec5SDimitry Andric if (Loc.isValid()) { 7340b57cec5SDimitry Andric ParsedStatus PS = IsUnparsed; 7350b57cec5SDimitry Andric 7360b57cec5SDimitry Andric Loc = SrcManager->getExpansionLoc(Loc); 7370b57cec5SDimitry Andric FileID FID = SrcManager->getFileID(Loc); 7380b57cec5SDimitry Andric 73906c3fb27SDimitry Andric auto FE = SrcManager->getFileEntryRefForID(FID); 7400b57cec5SDimitry Andric if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { 7410b57cec5SDimitry Andric // If the file is a modules header file it shall not be parsed 7420b57cec5SDimitry Andric // for expected-* directives. 7430b57cec5SDimitry Andric HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); 74406c3fb27SDimitry Andric if (HS.findModuleForHeader(*FE)) 7450b57cec5SDimitry Andric PS = IsUnparsedNoDirectives; 7460b57cec5SDimitry Andric } 7470b57cec5SDimitry Andric 7480b57cec5SDimitry Andric UpdateParsedFileStatus(*SrcManager, FID, PS); 7490b57cec5SDimitry Andric } 7500b57cec5SDimitry Andric } 7510b57cec5SDimitry Andric #endif 7520b57cec5SDimitry Andric 7530b57cec5SDimitry Andric // Send the diagnostic to the buffer, we will check it once we reach the end 7540b57cec5SDimitry Andric // of the source file (or are destructed). 7550b57cec5SDimitry Andric Buffer->HandleDiagnostic(DiagLevel, Info); 7560b57cec5SDimitry Andric } 7570b57cec5SDimitry Andric 7580b57cec5SDimitry Andric /// HandleComment - Hook into the preprocessor and extract comments containing 7590b57cec5SDimitry Andric /// expected errors and warnings. 7600b57cec5SDimitry Andric bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 7610b57cec5SDimitry Andric SourceRange Comment) { 7620b57cec5SDimitry Andric SourceManager &SM = PP.getSourceManager(); 7630b57cec5SDimitry Andric 7640b57cec5SDimitry Andric // If this comment is for a different source manager, ignore it. 7650b57cec5SDimitry Andric if (SrcManager && &SM != SrcManager) 7660b57cec5SDimitry Andric return false; 7670b57cec5SDimitry Andric 7680b57cec5SDimitry Andric SourceLocation CommentBegin = Comment.getBegin(); 7690b57cec5SDimitry Andric 7700b57cec5SDimitry Andric const char *CommentRaw = SM.getCharacterData(CommentBegin); 7710b57cec5SDimitry Andric StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 7720b57cec5SDimitry Andric 7730b57cec5SDimitry Andric if (C.empty()) 7740b57cec5SDimitry Andric return false; 7750b57cec5SDimitry Andric 7760b57cec5SDimitry Andric // Fold any "\<EOL>" sequences 7770b57cec5SDimitry Andric size_t loc = C.find('\\'); 7780b57cec5SDimitry Andric if (loc == StringRef::npos) { 7790b57cec5SDimitry Andric ParseDirective(C, &ED, SM, &PP, CommentBegin, Status, *Markers); 7800b57cec5SDimitry Andric return false; 7810b57cec5SDimitry Andric } 7820b57cec5SDimitry Andric 7830b57cec5SDimitry Andric std::string C2; 7840b57cec5SDimitry Andric C2.reserve(C.size()); 7850b57cec5SDimitry Andric 7860b57cec5SDimitry Andric for (size_t last = 0;; loc = C.find('\\', last)) { 7870b57cec5SDimitry Andric if (loc == StringRef::npos || loc == C.size()) { 7880b57cec5SDimitry Andric C2 += C.substr(last); 7890b57cec5SDimitry Andric break; 7900b57cec5SDimitry Andric } 7910b57cec5SDimitry Andric C2 += C.substr(last, loc-last); 7920b57cec5SDimitry Andric last = loc + 1; 7930b57cec5SDimitry Andric 7940b57cec5SDimitry Andric if (C[last] == '\n' || C[last] == '\r') { 7950b57cec5SDimitry Andric ++last; 7960b57cec5SDimitry Andric 7970b57cec5SDimitry Andric // Escape \r\n or \n\r, but not \n\n. 7980b57cec5SDimitry Andric if (last < C.size()) 7990b57cec5SDimitry Andric if (C[last] == '\n' || C[last] == '\r') 8000b57cec5SDimitry Andric if (C[last] != C[last-1]) 8010b57cec5SDimitry Andric ++last; 8020b57cec5SDimitry Andric } else { 8030b57cec5SDimitry Andric // This was just a normal backslash. 8040b57cec5SDimitry Andric C2 += '\\'; 8050b57cec5SDimitry Andric } 8060b57cec5SDimitry Andric } 8070b57cec5SDimitry Andric 8080b57cec5SDimitry Andric if (!C2.empty()) 8090b57cec5SDimitry Andric ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status, *Markers); 8100b57cec5SDimitry Andric return false; 8110b57cec5SDimitry Andric } 8120b57cec5SDimitry Andric 8130b57cec5SDimitry Andric #ifndef NDEBUG 8140b57cec5SDimitry Andric /// Lex the specified source file to determine whether it contains 8150b57cec5SDimitry Andric /// any expected-* directives. As a Lexer is used rather than a full-blown 8160b57cec5SDimitry Andric /// Preprocessor, directives inside skipped #if blocks will still be found. 8170b57cec5SDimitry Andric /// 8180b57cec5SDimitry Andric /// \return true if any directives were found. 8190b57cec5SDimitry Andric static bool findDirectives(SourceManager &SM, FileID FID, 8200b57cec5SDimitry Andric const LangOptions &LangOpts) { 8210b57cec5SDimitry Andric // Create a raw lexer to pull all the comments out of FID. 8220b57cec5SDimitry Andric if (FID.isInvalid()) 8230b57cec5SDimitry Andric return false; 8240b57cec5SDimitry Andric 8250b57cec5SDimitry Andric // Create a lexer to lex all the tokens of the main file in raw mode. 826e8d8bef9SDimitry Andric llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID); 8270b57cec5SDimitry Andric Lexer RawLex(FID, FromFile, SM, LangOpts); 8280b57cec5SDimitry Andric 8290b57cec5SDimitry Andric // Return comments as tokens, this is how we find expected diagnostics. 8300b57cec5SDimitry Andric RawLex.SetCommentRetentionState(true); 8310b57cec5SDimitry Andric 8320b57cec5SDimitry Andric Token Tok; 8330b57cec5SDimitry Andric Tok.setKind(tok::comment); 8340b57cec5SDimitry Andric VerifyDiagnosticConsumer::DirectiveStatus Status = 8350b57cec5SDimitry Andric VerifyDiagnosticConsumer::HasNoDirectives; 8360b57cec5SDimitry Andric while (Tok.isNot(tok::eof)) { 8370b57cec5SDimitry Andric RawLex.LexFromRawLexer(Tok); 8380b57cec5SDimitry Andric if (!Tok.is(tok::comment)) continue; 8390b57cec5SDimitry Andric 8400b57cec5SDimitry Andric std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 8410b57cec5SDimitry Andric if (Comment.empty()) continue; 8420b57cec5SDimitry Andric 8430b57cec5SDimitry Andric // We don't care about tracking markers for this phase. 8440b57cec5SDimitry Andric VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics()); 8450b57cec5SDimitry Andric 8460b57cec5SDimitry Andric // Find first directive. 8470b57cec5SDimitry Andric if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), 8480b57cec5SDimitry Andric Status, Markers)) 8490b57cec5SDimitry Andric return true; 8500b57cec5SDimitry Andric } 8510b57cec5SDimitry Andric return false; 8520b57cec5SDimitry Andric } 8530b57cec5SDimitry Andric #endif // !NDEBUG 8540b57cec5SDimitry Andric 8550b57cec5SDimitry Andric /// Takes a list of diagnostics that have been generated but not matched 8560b57cec5SDimitry Andric /// by an expected-* directive and produces a diagnostic to the user from this. 8570b57cec5SDimitry Andric static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 8580b57cec5SDimitry Andric const_diag_iterator diag_begin, 8590b57cec5SDimitry Andric const_diag_iterator diag_end, 8600b57cec5SDimitry Andric const char *Kind) { 8610b57cec5SDimitry Andric if (diag_begin == diag_end) return 0; 8620b57cec5SDimitry Andric 8630b57cec5SDimitry Andric SmallString<256> Fmt; 8640b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Fmt); 8650b57cec5SDimitry Andric for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 8660b57cec5SDimitry Andric if (I->first.isInvalid() || !SourceMgr) 8670b57cec5SDimitry Andric OS << "\n (frontend)"; 8680b57cec5SDimitry Andric else { 8690b57cec5SDimitry Andric OS << "\n "; 8705f757f3fSDimitry Andric if (OptionalFileEntryRef File = 8715f757f3fSDimitry Andric SourceMgr->getFileEntryRefForID(SourceMgr->getFileID(I->first))) 8720b57cec5SDimitry Andric OS << " File " << File->getName(); 8730b57cec5SDimitry Andric OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 8740b57cec5SDimitry Andric } 8750b57cec5SDimitry Andric OS << ": " << I->second; 8760b57cec5SDimitry Andric } 8770b57cec5SDimitry Andric 8785f757f3fSDimitry Andric std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin(); 8795f757f3fSDimitry Andric std::string KindStr = Prefix + "-" + Kind; 8800b57cec5SDimitry Andric Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 8815f757f3fSDimitry Andric << KindStr << /*Unexpected=*/true << OS.str(); 8820b57cec5SDimitry Andric return std::distance(diag_begin, diag_end); 8830b57cec5SDimitry Andric } 8840b57cec5SDimitry Andric 8850b57cec5SDimitry Andric /// Takes a list of diagnostics that were expected to have been generated 8860b57cec5SDimitry Andric /// but were not and produces a diagnostic to the user from this. 8870b57cec5SDimitry Andric static unsigned PrintExpected(DiagnosticsEngine &Diags, 8880b57cec5SDimitry Andric SourceManager &SourceMgr, 8890b57cec5SDimitry Andric std::vector<Directive *> &DL, const char *Kind) { 8900b57cec5SDimitry Andric if (DL.empty()) 8910b57cec5SDimitry Andric return 0; 8920b57cec5SDimitry Andric 8930b57cec5SDimitry Andric SmallString<256> Fmt; 8940b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Fmt); 8950b57cec5SDimitry Andric for (const auto *D : DL) { 8965ffd83dbSDimitry Andric if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine) 8970b57cec5SDimitry Andric OS << "\n File *"; 8980b57cec5SDimitry Andric else 8990b57cec5SDimitry Andric OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc); 9000b57cec5SDimitry Andric if (D->MatchAnyLine) 9010b57cec5SDimitry Andric OS << " Line *"; 9020b57cec5SDimitry Andric else 9030b57cec5SDimitry Andric OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc); 9040b57cec5SDimitry Andric if (D->DirectiveLoc != D->DiagnosticLoc) 9050b57cec5SDimitry Andric OS << " (directive at " 9060b57cec5SDimitry Andric << SourceMgr.getFilename(D->DirectiveLoc) << ':' 9070b57cec5SDimitry Andric << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')'; 9080b57cec5SDimitry Andric OS << ": " << D->Text; 9090b57cec5SDimitry Andric } 9100b57cec5SDimitry Andric 9115f757f3fSDimitry Andric std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin(); 9125f757f3fSDimitry Andric std::string KindStr = Prefix + "-" + Kind; 9130b57cec5SDimitry Andric Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 9145f757f3fSDimitry Andric << KindStr << /*Unexpected=*/false << OS.str(); 9150b57cec5SDimitry Andric return DL.size(); 9160b57cec5SDimitry Andric } 9170b57cec5SDimitry Andric 9180b57cec5SDimitry Andric /// Determine whether two source locations come from the same file. 9190b57cec5SDimitry Andric static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 9200b57cec5SDimitry Andric SourceLocation DiagnosticLoc) { 9210b57cec5SDimitry Andric while (DiagnosticLoc.isMacroID()) 9220b57cec5SDimitry Andric DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 9230b57cec5SDimitry Andric 9240b57cec5SDimitry Andric if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 9250b57cec5SDimitry Andric return true; 9260b57cec5SDimitry Andric 9270b57cec5SDimitry Andric const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 9280b57cec5SDimitry Andric if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 9290b57cec5SDimitry Andric return true; 9300b57cec5SDimitry Andric 9310b57cec5SDimitry Andric return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 9320b57cec5SDimitry Andric } 9330b57cec5SDimitry Andric 9340b57cec5SDimitry Andric /// CheckLists - Compare expected to seen diagnostic lists and return the 9350b57cec5SDimitry Andric /// the difference between them. 9360b57cec5SDimitry Andric static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 9370b57cec5SDimitry Andric const char *Label, 9380b57cec5SDimitry Andric DirectiveList &Left, 9390b57cec5SDimitry Andric const_diag_iterator d2_begin, 9400b57cec5SDimitry Andric const_diag_iterator d2_end, 9410b57cec5SDimitry Andric bool IgnoreUnexpected) { 9420b57cec5SDimitry Andric std::vector<Directive *> LeftOnly; 9430b57cec5SDimitry Andric DiagList Right(d2_begin, d2_end); 9440b57cec5SDimitry Andric 9450b57cec5SDimitry Andric for (auto &Owner : Left) { 9460b57cec5SDimitry Andric Directive &D = *Owner; 9470b57cec5SDimitry Andric unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 9480b57cec5SDimitry Andric 9490b57cec5SDimitry Andric for (unsigned i = 0; i < D.Max; ++i) { 9500b57cec5SDimitry Andric DiagList::iterator II, IE; 9510b57cec5SDimitry Andric for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 9520b57cec5SDimitry Andric if (!D.MatchAnyLine) { 9530b57cec5SDimitry Andric unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 9540b57cec5SDimitry Andric if (LineNo1 != LineNo2) 9550b57cec5SDimitry Andric continue; 9560b57cec5SDimitry Andric } 9570b57cec5SDimitry Andric 9585ffd83dbSDimitry Andric if (!D.DiagnosticLoc.isInvalid() && !D.MatchAnyFileAndLine && 9590b57cec5SDimitry Andric !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 9600b57cec5SDimitry Andric continue; 9610b57cec5SDimitry Andric 9620b57cec5SDimitry Andric const std::string &RightText = II->second; 9630b57cec5SDimitry Andric if (D.match(RightText)) 9640b57cec5SDimitry Andric break; 9650b57cec5SDimitry Andric } 9660b57cec5SDimitry Andric if (II == IE) { 9670b57cec5SDimitry Andric // Not found. 9680b57cec5SDimitry Andric if (i >= D.Min) break; 9690b57cec5SDimitry Andric LeftOnly.push_back(&D); 9700b57cec5SDimitry Andric } else { 9710b57cec5SDimitry Andric // Found. The same cannot be found twice. 9720b57cec5SDimitry Andric Right.erase(II); 9730b57cec5SDimitry Andric } 9740b57cec5SDimitry Andric } 9750b57cec5SDimitry Andric } 9760b57cec5SDimitry Andric // Now all that's left in Right are those that were not matched. 9770b57cec5SDimitry Andric unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 9780b57cec5SDimitry Andric if (!IgnoreUnexpected) 9790b57cec5SDimitry Andric num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 9800b57cec5SDimitry Andric return num; 9810b57cec5SDimitry Andric } 9820b57cec5SDimitry Andric 9830b57cec5SDimitry Andric /// CheckResults - This compares the expected results to those that 9840b57cec5SDimitry Andric /// were actually reported. It emits any discrepencies. Return "true" if there 9850b57cec5SDimitry Andric /// were problems. Return "false" otherwise. 9860b57cec5SDimitry Andric static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 9870b57cec5SDimitry Andric const TextDiagnosticBuffer &Buffer, 9880b57cec5SDimitry Andric ExpectedData &ED) { 9890b57cec5SDimitry Andric // We want to capture the delta between what was expected and what was 9900b57cec5SDimitry Andric // seen. 9910b57cec5SDimitry Andric // 9920b57cec5SDimitry Andric // Expected \ Seen - set expected but not seen 9930b57cec5SDimitry Andric // Seen \ Expected - set seen but not expected 9940b57cec5SDimitry Andric unsigned NumProblems = 0; 9950b57cec5SDimitry Andric 9960b57cec5SDimitry Andric const DiagnosticLevelMask DiagMask = 9970b57cec5SDimitry Andric Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 9980b57cec5SDimitry Andric 9990b57cec5SDimitry Andric // See if there are error mismatches. 10000b57cec5SDimitry Andric NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 10010b57cec5SDimitry Andric Buffer.err_begin(), Buffer.err_end(), 10020b57cec5SDimitry Andric bool(DiagnosticLevelMask::Error & DiagMask)); 10030b57cec5SDimitry Andric 10040b57cec5SDimitry Andric // See if there are warning mismatches. 10050b57cec5SDimitry Andric NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 10060b57cec5SDimitry Andric Buffer.warn_begin(), Buffer.warn_end(), 10070b57cec5SDimitry Andric bool(DiagnosticLevelMask::Warning & DiagMask)); 10080b57cec5SDimitry Andric 10090b57cec5SDimitry Andric // See if there are remark mismatches. 10100b57cec5SDimitry Andric NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, 10110b57cec5SDimitry Andric Buffer.remark_begin(), Buffer.remark_end(), 10120b57cec5SDimitry Andric bool(DiagnosticLevelMask::Remark & DiagMask)); 10130b57cec5SDimitry Andric 10140b57cec5SDimitry Andric // See if there are note mismatches. 10150b57cec5SDimitry Andric NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 10160b57cec5SDimitry Andric Buffer.note_begin(), Buffer.note_end(), 10170b57cec5SDimitry Andric bool(DiagnosticLevelMask::Note & DiagMask)); 10180b57cec5SDimitry Andric 10190b57cec5SDimitry Andric return NumProblems; 10200b57cec5SDimitry Andric } 10210b57cec5SDimitry Andric 10220b57cec5SDimitry Andric void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 10230b57cec5SDimitry Andric FileID FID, 10240b57cec5SDimitry Andric ParsedStatus PS) { 10250b57cec5SDimitry Andric // Check SourceManager hasn't changed. 10260b57cec5SDimitry Andric setSourceManager(SM); 10270b57cec5SDimitry Andric 10280b57cec5SDimitry Andric #ifndef NDEBUG 10290b57cec5SDimitry Andric if (FID.isInvalid()) 10300b57cec5SDimitry Andric return; 10310b57cec5SDimitry Andric 10325f757f3fSDimitry Andric OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID); 10330b57cec5SDimitry Andric 10340b57cec5SDimitry Andric if (PS == IsParsed) { 10350b57cec5SDimitry Andric // Move the FileID from the unparsed set to the parsed set. 10360b57cec5SDimitry Andric UnparsedFiles.erase(FID); 10375f757f3fSDimitry Andric ParsedFiles.insert(std::make_pair(FID, FE ? &FE->getFileEntry() : nullptr)); 10380b57cec5SDimitry Andric } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 10390b57cec5SDimitry Andric // Add the FileID to the unparsed set if we haven't seen it before. 10400b57cec5SDimitry Andric 10410b57cec5SDimitry Andric // Check for directives. 10420b57cec5SDimitry Andric bool FoundDirectives; 10430b57cec5SDimitry Andric if (PS == IsUnparsedNoDirectives) 10440b57cec5SDimitry Andric FoundDirectives = false; 10450b57cec5SDimitry Andric else 10460b57cec5SDimitry Andric FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 10470b57cec5SDimitry Andric 10480b57cec5SDimitry Andric // Add the FileID to the unparsed set. 10490b57cec5SDimitry Andric UnparsedFiles.insert(std::make_pair(FID, 10500b57cec5SDimitry Andric UnparsedFileStatus(FE, FoundDirectives))); 10510b57cec5SDimitry Andric } 10520b57cec5SDimitry Andric #endif 10530b57cec5SDimitry Andric } 10540b57cec5SDimitry Andric 10550b57cec5SDimitry Andric void VerifyDiagnosticConsumer::CheckDiagnostics() { 10560b57cec5SDimitry Andric // Ensure any diagnostics go to the primary client. 10570b57cec5SDimitry Andric DiagnosticConsumer *CurClient = Diags.getClient(); 10580b57cec5SDimitry Andric std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); 10590b57cec5SDimitry Andric Diags.setClient(PrimaryClient, false); 10600b57cec5SDimitry Andric 10610b57cec5SDimitry Andric #ifndef NDEBUG 10620b57cec5SDimitry Andric // In a debug build, scan through any files that may have been missed 10630b57cec5SDimitry Andric // during parsing and issue a fatal error if directives are contained 10640b57cec5SDimitry Andric // within these files. If a fatal error occurs, this suggests that 10650b57cec5SDimitry Andric // this file is being parsed separately from the main file, in which 10660b57cec5SDimitry Andric // case consider moving the directives to the correct place, if this 10670b57cec5SDimitry Andric // is applicable. 10680b57cec5SDimitry Andric if (!UnparsedFiles.empty()) { 10690b57cec5SDimitry Andric // Generate a cache of parsed FileEntry pointers for alias lookups. 10700b57cec5SDimitry Andric llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 10710b57cec5SDimitry Andric for (const auto &I : ParsedFiles) 10720b57cec5SDimitry Andric if (const FileEntry *FE = I.second) 10730b57cec5SDimitry Andric ParsedFileCache.insert(FE); 10740b57cec5SDimitry Andric 10750b57cec5SDimitry Andric // Iterate through list of unparsed files. 10760b57cec5SDimitry Andric for (const auto &I : UnparsedFiles) { 10770b57cec5SDimitry Andric const UnparsedFileStatus &Status = I.second; 10785f757f3fSDimitry Andric OptionalFileEntryRef FE = Status.getFile(); 10790b57cec5SDimitry Andric 10800b57cec5SDimitry Andric // Skip files that have been parsed via an alias. 10815f757f3fSDimitry Andric if (FE && ParsedFileCache.count(*FE)) 10820b57cec5SDimitry Andric continue; 10830b57cec5SDimitry Andric 10840b57cec5SDimitry Andric // Report a fatal error if this file contained directives. 10850b57cec5SDimitry Andric if (Status.foundDirectives()) { 10865f757f3fSDimitry Andric llvm::report_fatal_error("-verify directives found after rather" 10875f757f3fSDimitry Andric " than during normal parsing of " + 10885f757f3fSDimitry Andric (FE ? FE->getName() : "(unknown)")); 10890b57cec5SDimitry Andric } 10900b57cec5SDimitry Andric } 10910b57cec5SDimitry Andric 10920b57cec5SDimitry Andric // UnparsedFiles has been processed now, so clear it. 10930b57cec5SDimitry Andric UnparsedFiles.clear(); 10940b57cec5SDimitry Andric } 10950b57cec5SDimitry Andric #endif // !NDEBUG 10960b57cec5SDimitry Andric 10970b57cec5SDimitry Andric if (SrcManager) { 10980b57cec5SDimitry Andric // Produce an error if no expected-* directives could be found in the 10990b57cec5SDimitry Andric // source file(s) processed. 11000b57cec5SDimitry Andric if (Status == HasNoDirectives) { 11010b57cec5SDimitry Andric Diags.Report(diag::err_verify_no_directives).setForceEmit(); 11020b57cec5SDimitry Andric ++NumErrors; 11030b57cec5SDimitry Andric Status = HasNoDirectivesReported; 11040b57cec5SDimitry Andric } 11050b57cec5SDimitry Andric 11060b57cec5SDimitry Andric // Check that the expected diagnostics occurred. 11070b57cec5SDimitry Andric NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 11080b57cec5SDimitry Andric } else { 11090b57cec5SDimitry Andric const DiagnosticLevelMask DiagMask = 11100b57cec5SDimitry Andric ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 11110b57cec5SDimitry Andric if (bool(DiagnosticLevelMask::Error & DiagMask)) 11120b57cec5SDimitry Andric NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), 11130b57cec5SDimitry Andric Buffer->err_end(), "error"); 11140b57cec5SDimitry Andric if (bool(DiagnosticLevelMask::Warning & DiagMask)) 11150b57cec5SDimitry Andric NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), 11160b57cec5SDimitry Andric Buffer->warn_end(), "warn"); 11170b57cec5SDimitry Andric if (bool(DiagnosticLevelMask::Remark & DiagMask)) 11180b57cec5SDimitry Andric NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), 11190b57cec5SDimitry Andric Buffer->remark_end(), "remark"); 11200b57cec5SDimitry Andric if (bool(DiagnosticLevelMask::Note & DiagMask)) 11210b57cec5SDimitry Andric NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), 11220b57cec5SDimitry Andric Buffer->note_end(), "note"); 11230b57cec5SDimitry Andric } 11240b57cec5SDimitry Andric 11250b57cec5SDimitry Andric Diags.setClient(CurClient, Owner.release() != nullptr); 11260b57cec5SDimitry Andric 11270b57cec5SDimitry Andric // Reset the buffer, we have processed all the diagnostics in it. 11280b57cec5SDimitry Andric Buffer.reset(new TextDiagnosticBuffer()); 11290b57cec5SDimitry Andric ED.Reset(); 11300b57cec5SDimitry Andric } 11310b57cec5SDimitry Andric 11320b57cec5SDimitry Andric std::unique_ptr<Directive> Directive::create(bool RegexKind, 11330b57cec5SDimitry Andric SourceLocation DirectiveLoc, 11340b57cec5SDimitry Andric SourceLocation DiagnosticLoc, 11355ffd83dbSDimitry Andric bool MatchAnyFileAndLine, 11360b57cec5SDimitry Andric bool MatchAnyLine, StringRef Text, 11370b57cec5SDimitry Andric unsigned Min, unsigned Max) { 11380b57cec5SDimitry Andric if (!RegexKind) 1139a7dea167SDimitry Andric return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, 11405ffd83dbSDimitry Andric MatchAnyFileAndLine, 11410b57cec5SDimitry Andric MatchAnyLine, Text, Min, Max); 11420b57cec5SDimitry Andric 11430b57cec5SDimitry Andric // Parse the directive into a regular expression. 11440b57cec5SDimitry Andric std::string RegexStr; 11450b57cec5SDimitry Andric StringRef S = Text; 11460b57cec5SDimitry Andric while (!S.empty()) { 1147*647cbc5dSDimitry Andric if (S.consume_front("{{")) { 11480b57cec5SDimitry Andric size_t RegexMatchLength = S.find("}}"); 11490b57cec5SDimitry Andric assert(RegexMatchLength != StringRef::npos); 11500b57cec5SDimitry Andric // Append the regex, enclosed in parentheses. 11510b57cec5SDimitry Andric RegexStr += "("; 11520b57cec5SDimitry Andric RegexStr.append(S.data(), RegexMatchLength); 11530b57cec5SDimitry Andric RegexStr += ")"; 11540b57cec5SDimitry Andric S = S.drop_front(RegexMatchLength + 2); 11550b57cec5SDimitry Andric } else { 11560b57cec5SDimitry Andric size_t VerbatimMatchLength = S.find("{{"); 11570b57cec5SDimitry Andric if (VerbatimMatchLength == StringRef::npos) 11580b57cec5SDimitry Andric VerbatimMatchLength = S.size(); 11590b57cec5SDimitry Andric // Escape and append the fixed string. 11600b57cec5SDimitry Andric RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 11610b57cec5SDimitry Andric S = S.drop_front(VerbatimMatchLength); 11620b57cec5SDimitry Andric } 11630b57cec5SDimitry Andric } 11640b57cec5SDimitry Andric 11655ffd83dbSDimitry Andric return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc, 11665ffd83dbSDimitry Andric MatchAnyFileAndLine, MatchAnyLine, 11675ffd83dbSDimitry Andric Text, Min, Max, RegexStr); 11680b57cec5SDimitry Andric } 1169