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