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