VerifyDiagnosticConsumer.cpp revision 296417
1226586Sdim//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===// 2226586Sdim// 3226586Sdim// The LLVM Compiler Infrastructure 4226586Sdim// 5226586Sdim// This file is distributed under the University of Illinois Open Source 6226586Sdim// License. See LICENSE.TXT for details. 7226586Sdim// 8226586Sdim//===----------------------------------------------------------------------===// 9226586Sdim// 10226586Sdim// This is a concrete diagnostic client, which buffers the diagnostic messages. 11226586Sdim// 12226586Sdim//===----------------------------------------------------------------------===// 13226586Sdim 14249423Sdim#include "clang/Frontend/VerifyDiagnosticConsumer.h" 15249423Sdim#include "clang/Basic/CharInfo.h" 16239462Sdim#include "clang/Basic/FileManager.h" 17226586Sdim#include "clang/Frontend/FrontendDiagnostic.h" 18226586Sdim#include "clang/Frontend/TextDiagnosticBuffer.h" 19239462Sdim#include "clang/Lex/HeaderSearch.h" 20226586Sdim#include "clang/Lex/Preprocessor.h" 21226586Sdim#include "llvm/ADT/SmallString.h" 22226586Sdim#include "llvm/Support/Regex.h" 23226586Sdim#include "llvm/Support/raw_ostream.h" 24234353Sdim 25226586Sdimusing namespace clang; 26239462Sdimtypedef VerifyDiagnosticConsumer::Directive Directive; 27239462Sdimtypedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; 28239462Sdimtypedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; 29226586Sdim 30280031SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) 31280031Sdim : Diags(Diags_), 32280031Sdim PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()), 33276479Sdim Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr), 34276479Sdim LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0), 35276479Sdim Status(HasNoDirectives) 36226586Sdim{ 37243830Sdim if (Diags.hasSourceManager()) 38243830Sdim setSourceManager(Diags.getSourceManager()); 39226586Sdim} 40226586Sdim 41226586SdimVerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { 42239462Sdim assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); 43239462Sdim assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); 44276479Sdim SrcManager = nullptr; 45280031Sdim CheckDiagnostics(); 46280031Sdim Diags.takeClient().release(); 47226586Sdim} 48226586Sdim 49239462Sdim#ifndef NDEBUG 50239462Sdimnamespace { 51239462Sdimclass VerifyFileTracker : public PPCallbacks { 52243830Sdim VerifyDiagnosticConsumer &Verify; 53239462Sdim SourceManager &SM; 54239462Sdim 55239462Sdimpublic: 56243830Sdim VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) 57243830Sdim : Verify(Verify), SM(SM) { } 58239462Sdim 59239462Sdim /// \brief Hook into the preprocessor and update the list of parsed 60239462Sdim /// files when the preprocessor indicates a new file is entered. 61288943Sdim void FileChanged(SourceLocation Loc, FileChangeReason Reason, 62288943Sdim SrcMgr::CharacteristicKind FileType, 63288943Sdim FileID PrevFID) override { 64243830Sdim Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), 65243830Sdim VerifyDiagnosticConsumer::IsParsed); 66239462Sdim } 67239462Sdim}; 68239462Sdim} // End anonymous namespace. 69239462Sdim#endif 70239462Sdim 71226586Sdim// DiagnosticConsumer interface. 72226586Sdim 73226586Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 74234353Sdim const Preprocessor *PP) { 75239462Sdim // Attach comment handler on first invocation. 76239462Sdim if (++ActiveSourceFiles == 1) { 77239462Sdim if (PP) { 78239462Sdim CurrentPreprocessor = PP; 79243830Sdim this->LangOpts = &LangOpts; 80243830Sdim setSourceManager(PP->getSourceManager()); 81239462Sdim const_cast<Preprocessor*>(PP)->addCommentHandler(this); 82239462Sdim#ifndef NDEBUG 83243830Sdim // Debug build tracks parsed files. 84280031Sdim const_cast<Preprocessor*>(PP)->addPPCallbacks( 85280031Sdim llvm::make_unique<VerifyFileTracker>(*this, *SrcManager)); 86239462Sdim#endif 87239462Sdim } 88239462Sdim } 89226586Sdim 90239462Sdim assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); 91226586Sdim PrimaryClient->BeginSourceFile(LangOpts, PP); 92226586Sdim} 93226586Sdim 94226586Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() { 95239462Sdim assert(ActiveSourceFiles && "No active source files!"); 96226586Sdim PrimaryClient->EndSourceFile(); 97226586Sdim 98239462Sdim // Detach comment handler once last active source file completed. 99239462Sdim if (--ActiveSourceFiles == 0) { 100239462Sdim if (CurrentPreprocessor) 101239462Sdim const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); 102239462Sdim 103239462Sdim // Check diagnostics once last file completed. 104239462Sdim CheckDiagnostics(); 105276479Sdim CurrentPreprocessor = nullptr; 106276479Sdim LangOpts = nullptr; 107239462Sdim } 108226586Sdim} 109226586Sdim 110226586Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic( 111226586Sdim DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 112251662Sdim if (Info.hasSourceManager()) { 113251662Sdim // If this diagnostic is for a different source manager, ignore it. 114251662Sdim if (SrcManager && &Info.getSourceManager() != SrcManager) 115251662Sdim return; 116251662Sdim 117243830Sdim setSourceManager(Info.getSourceManager()); 118251662Sdim } 119243830Sdim 120239462Sdim#ifndef NDEBUG 121243830Sdim // Debug build tracks unparsed files for possible 122243830Sdim // unparsed expected-* directives. 123243830Sdim if (SrcManager) { 124243830Sdim SourceLocation Loc = Info.getLocation(); 125243830Sdim if (Loc.isValid()) { 126243830Sdim ParsedStatus PS = IsUnparsed; 127243830Sdim 128243830Sdim Loc = SrcManager->getExpansionLoc(Loc); 129243830Sdim FileID FID = SrcManager->getFileID(Loc); 130243830Sdim 131243830Sdim const FileEntry *FE = SrcManager->getFileEntryForID(FID); 132243830Sdim if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { 133243830Sdim // If the file is a modules header file it shall not be parsed 134243830Sdim // for expected-* directives. 135243830Sdim HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); 136243830Sdim if (HS.findModuleForHeader(FE)) 137243830Sdim PS = IsUnparsedNoDirectives; 138243830Sdim } 139243830Sdim 140243830Sdim UpdateParsedFileStatus(*SrcManager, FID, PS); 141243830Sdim } 142226586Sdim } 143239462Sdim#endif 144243830Sdim 145226586Sdim // Send the diagnostic to the buffer, we will check it once we reach the end 146226586Sdim // of the source file (or are destructed). 147226586Sdim Buffer->HandleDiagnostic(DiagLevel, Info); 148226586Sdim} 149226586Sdim 150226586Sdim//===----------------------------------------------------------------------===// 151226586Sdim// Checking diagnostics implementation. 152226586Sdim//===----------------------------------------------------------------------===// 153226586Sdim 154226586Sdimtypedef TextDiagnosticBuffer::DiagList DiagList; 155226586Sdimtypedef TextDiagnosticBuffer::const_iterator const_diag_iterator; 156226586Sdim 157226586Sdimnamespace { 158226586Sdim 159226586Sdim/// StandardDirective - Directive with string matching. 160226586Sdim/// 161226586Sdimclass StandardDirective : public Directive { 162226586Sdimpublic: 163239462Sdim StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 164276479Sdim bool MatchAnyLine, StringRef Text, unsigned Min, 165276479Sdim unsigned Max) 166276479Sdim : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { } 167226586Sdim 168276479Sdim bool isValid(std::string &Error) override { 169226586Sdim // all strings are considered valid; even empty ones 170226586Sdim return true; 171226586Sdim } 172226586Sdim 173276479Sdim bool match(StringRef S) override { 174239462Sdim return S.find(Text) != StringRef::npos; 175226586Sdim } 176226586Sdim}; 177226586Sdim 178226586Sdim/// RegexDirective - Directive with regular-expression matching. 179226586Sdim/// 180226586Sdimclass RegexDirective : public Directive { 181226586Sdimpublic: 182239462Sdim RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 183276479Sdim bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, 184276479Sdim StringRef RegexStr) 185276479Sdim : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), 186276479Sdim Regex(RegexStr) { } 187226586Sdim 188276479Sdim bool isValid(std::string &Error) override { 189296417Sdim return Regex.isValid(Error); 190226586Sdim } 191226586Sdim 192276479Sdim bool match(StringRef S) override { 193226586Sdim return Regex.match(S); 194226586Sdim } 195226586Sdim 196226586Sdimprivate: 197226586Sdim llvm::Regex Regex; 198226586Sdim}; 199226586Sdim 200226586Sdimclass ParseHelper 201226586Sdim{ 202226586Sdimpublic: 203239462Sdim ParseHelper(StringRef S) 204276479Sdim : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {} 205226586Sdim 206226586Sdim // Return true if string literal is next. 207226586Sdim bool Next(StringRef S) { 208226586Sdim P = C; 209226586Sdim PEnd = C + S.size(); 210226586Sdim if (PEnd > End) 211226586Sdim return false; 212226586Sdim return !memcmp(P, S.data(), S.size()); 213226586Sdim } 214226586Sdim 215226586Sdim // Return true if number is next. 216226586Sdim // Output N only if number is next. 217226586Sdim bool Next(unsigned &N) { 218226586Sdim unsigned TMP = 0; 219226586Sdim P = C; 220226586Sdim for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { 221226586Sdim TMP *= 10; 222226586Sdim TMP += P[0] - '0'; 223226586Sdim } 224226586Sdim if (P == C) 225226586Sdim return false; 226226586Sdim PEnd = P; 227226586Sdim N = TMP; 228226586Sdim return true; 229226586Sdim } 230226586Sdim 231226586Sdim // Return true if string literal is found. 232226586Sdim // When true, P marks begin-position of S in content. 233243830Sdim bool Search(StringRef S, bool EnsureStartOfWord = false) { 234243830Sdim do { 235243830Sdim P = std::search(C, End, S.begin(), S.end()); 236243830Sdim PEnd = P + S.size(); 237243830Sdim if (P == End) 238243830Sdim break; 239243830Sdim if (!EnsureStartOfWord 240243830Sdim // Check if string literal starts a new word. 241249423Sdim || P == Begin || isWhitespace(P[-1]) 242276479Sdim // Or it could be preceded by the start of a comment. 243243830Sdim || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') 244243830Sdim && P[-2] == '/')) 245243830Sdim return true; 246243830Sdim // Otherwise, skip and search again. 247243830Sdim } while (Advance()); 248243830Sdim return false; 249226586Sdim } 250226586Sdim 251276479Sdim // Return true if a CloseBrace that closes the OpenBrace at the current nest 252276479Sdim // level is found. When true, P marks begin-position of CloseBrace. 253276479Sdim bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { 254276479Sdim unsigned Depth = 1; 255276479Sdim P = C; 256276479Sdim while (P < End) { 257276479Sdim StringRef S(P, End - P); 258276479Sdim if (S.startswith(OpenBrace)) { 259276479Sdim ++Depth; 260276479Sdim P += OpenBrace.size(); 261276479Sdim } else if (S.startswith(CloseBrace)) { 262276479Sdim --Depth; 263276479Sdim if (Depth == 0) { 264276479Sdim PEnd = P + CloseBrace.size(); 265276479Sdim return true; 266276479Sdim } 267276479Sdim P += CloseBrace.size(); 268276479Sdim } else { 269276479Sdim ++P; 270276479Sdim } 271276479Sdim } 272276479Sdim return false; 273276479Sdim } 274276479Sdim 275226586Sdim // Advance 1-past previous next/search. 276226586Sdim // Behavior is undefined if previous next/search failed. 277226586Sdim bool Advance() { 278226586Sdim C = PEnd; 279226586Sdim return C < End; 280226586Sdim } 281226586Sdim 282226586Sdim // Skip zero or more whitespace. 283226586Sdim void SkipWhitespace() { 284249423Sdim for (; C < End && isWhitespace(*C); ++C) 285226586Sdim ; 286226586Sdim } 287226586Sdim 288226586Sdim // Return true if EOF reached. 289226586Sdim bool Done() { 290226586Sdim return !(C < End); 291226586Sdim } 292226586Sdim 293226586Sdim const char * const Begin; // beginning of expected content 294226586Sdim const char * const End; // end of expected content (1-past) 295226586Sdim const char *C; // position of next char in content 296226586Sdim const char *P; 297226586Sdim 298226586Sdimprivate: 299226586Sdim const char *PEnd; // previous next/search subject end (1-past) 300226586Sdim}; 301226586Sdim 302226586Sdim} // namespace anonymous 303226586Sdim 304226586Sdim/// ParseDirective - Go through the comment and see if it indicates expected 305226586Sdim/// diagnostics. If so, then put them in the appropriate directive list. 306226586Sdim/// 307239462Sdim/// Returns true if any valid directives were found. 308239462Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, 309251662Sdim Preprocessor *PP, SourceLocation Pos, 310243830Sdim VerifyDiagnosticConsumer::DirectiveStatus &Status) { 311251662Sdim DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); 312251662Sdim 313226586Sdim // A single comment may contain multiple directives. 314239462Sdim bool FoundDirective = false; 315239462Sdim for (ParseHelper PH(S); !PH.Done();) { 316239462Sdim // Search for token: expected 317243830Sdim if (!PH.Search("expected", true)) 318226586Sdim break; 319226586Sdim PH.Advance(); 320226586Sdim 321239462Sdim // Next token: - 322226586Sdim if (!PH.Next("-")) 323226586Sdim continue; 324226586Sdim PH.Advance(); 325226586Sdim 326239462Sdim // Next token: { error | warning | note } 327276479Sdim DirectiveList *DL = nullptr; 328226586Sdim if (PH.Next("error")) 329276479Sdim DL = ED ? &ED->Errors : nullptr; 330226586Sdim else if (PH.Next("warning")) 331276479Sdim DL = ED ? &ED->Warnings : nullptr; 332276479Sdim else if (PH.Next("remark")) 333276479Sdim DL = ED ? &ED->Remarks : nullptr; 334226586Sdim else if (PH.Next("note")) 335276479Sdim DL = ED ? &ED->Notes : nullptr; 336243830Sdim else if (PH.Next("no-diagnostics")) { 337243830Sdim if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 338243830Sdim Diags.Report(Pos, diag::err_verify_invalid_no_diags) 339243830Sdim << /*IsExpectedNoDiagnostics=*/true; 340243830Sdim else 341243830Sdim Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 342226586Sdim continue; 343243830Sdim } else 344243830Sdim continue; 345226586Sdim PH.Advance(); 346226586Sdim 347243830Sdim if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 348243830Sdim Diags.Report(Pos, diag::err_verify_invalid_no_diags) 349243830Sdim << /*IsExpectedNoDiagnostics=*/false; 350243830Sdim continue; 351243830Sdim } 352243830Sdim Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 353243830Sdim 354239462Sdim // If a directive has been found but we're not interested 355239462Sdim // in storing the directive information, return now. 356239462Sdim if (!DL) 357239462Sdim return true; 358239462Sdim 359239462Sdim // Default directive kind. 360226586Sdim bool RegexKind = false; 361226586Sdim const char* KindStr = "string"; 362226586Sdim 363239462Sdim // Next optional token: - 364226586Sdim if (PH.Next("-re")) { 365226586Sdim PH.Advance(); 366226586Sdim RegexKind = true; 367226586Sdim KindStr = "regex"; 368226586Sdim } 369226586Sdim 370239462Sdim // Next optional token: @ 371239462Sdim SourceLocation ExpectedLoc; 372276479Sdim bool MatchAnyLine = false; 373239462Sdim if (!PH.Next("@")) { 374239462Sdim ExpectedLoc = Pos; 375239462Sdim } else { 376239462Sdim PH.Advance(); 377239462Sdim unsigned Line = 0; 378239462Sdim bool FoundPlus = PH.Next("+"); 379239462Sdim if (FoundPlus || PH.Next("-")) { 380239462Sdim // Relative to current line. 381239462Sdim PH.Advance(); 382239462Sdim bool Invalid = false; 383239462Sdim unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 384239462Sdim if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 385239462Sdim if (FoundPlus) ExpectedLine += Line; 386239462Sdim else ExpectedLine -= Line; 387239462Sdim ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 388239462Sdim } 389251662Sdim } else if (PH.Next(Line)) { 390239462Sdim // Absolute line number. 391251662Sdim if (Line > 0) 392251662Sdim ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 393251662Sdim } else if (PP && PH.Search(":")) { 394251662Sdim // Specific source file. 395251662Sdim StringRef Filename(PH.C, PH.P-PH.C); 396251662Sdim PH.Advance(); 397251662Sdim 398251662Sdim // Lookup file via Preprocessor, like a #include. 399251662Sdim const DirectoryLookup *CurDir; 400280031Sdim const FileEntry *FE = 401280031Sdim PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, 402280031Sdim nullptr, nullptr, nullptr); 403251662Sdim if (!FE) { 404251662Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 405251662Sdim diag::err_verify_missing_file) << Filename << KindStr; 406251662Sdim continue; 407251662Sdim } 408251662Sdim 409251662Sdim if (SM.translateFile(FE).isInvalid()) 410251662Sdim SM.createFileID(FE, Pos, SrcMgr::C_User); 411251662Sdim 412239462Sdim if (PH.Next(Line) && Line > 0) 413251662Sdim ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); 414276479Sdim else if (PH.Next("*")) { 415276479Sdim MatchAnyLine = true; 416276479Sdim ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); 417276479Sdim } 418239462Sdim } 419239462Sdim 420239462Sdim if (ExpectedLoc.isInvalid()) { 421239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 422239462Sdim diag::err_verify_missing_line) << KindStr; 423239462Sdim continue; 424239462Sdim } 425239462Sdim PH.Advance(); 426239462Sdim } 427239462Sdim 428239462Sdim // Skip optional whitespace. 429226586Sdim PH.SkipWhitespace(); 430226586Sdim 431239462Sdim // Next optional token: positive integer or a '+'. 432239462Sdim unsigned Min = 1; 433239462Sdim unsigned Max = 1; 434239462Sdim if (PH.Next(Min)) { 435226586Sdim PH.Advance(); 436239462Sdim // A positive integer can be followed by a '+' meaning min 437239462Sdim // or more, or by a '-' meaning a range from min to max. 438239462Sdim if (PH.Next("+")) { 439239462Sdim Max = Directive::MaxCount; 440239462Sdim PH.Advance(); 441239462Sdim } else if (PH.Next("-")) { 442239462Sdim PH.Advance(); 443239462Sdim if (!PH.Next(Max) || Max < Min) { 444239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 445239462Sdim diag::err_verify_invalid_range) << KindStr; 446239462Sdim continue; 447239462Sdim } 448239462Sdim PH.Advance(); 449239462Sdim } else { 450239462Sdim Max = Min; 451239462Sdim } 452239462Sdim } else if (PH.Next("+")) { 453239462Sdim // '+' on its own means "1 or more". 454239462Sdim Max = Directive::MaxCount; 455234353Sdim PH.Advance(); 456234353Sdim } 457226586Sdim 458239462Sdim // Skip optional whitespace. 459226586Sdim PH.SkipWhitespace(); 460226586Sdim 461239462Sdim // Next token: {{ 462226586Sdim if (!PH.Next("{{")) { 463239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 464239462Sdim diag::err_verify_missing_start) << KindStr; 465226586Sdim continue; 466226586Sdim } 467226586Sdim PH.Advance(); 468226586Sdim const char* const ContentBegin = PH.C; // mark content begin 469226586Sdim 470239462Sdim // Search for token: }} 471276479Sdim if (!PH.SearchClosingBrace("{{", "}}")) { 472239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 473239462Sdim diag::err_verify_missing_end) << KindStr; 474226586Sdim continue; 475226586Sdim } 476226586Sdim const char* const ContentEnd = PH.P; // mark content end 477226586Sdim PH.Advance(); 478226586Sdim 479239462Sdim // Build directive text; convert \n to newlines. 480226586Sdim std::string Text; 481226586Sdim StringRef NewlineStr = "\\n"; 482226586Sdim StringRef Content(ContentBegin, ContentEnd-ContentBegin); 483226586Sdim size_t CPos = 0; 484226586Sdim size_t FPos; 485226586Sdim while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 486226586Sdim Text += Content.substr(CPos, FPos-CPos); 487226586Sdim Text += '\n'; 488226586Sdim CPos = FPos + NewlineStr.size(); 489226586Sdim } 490226586Sdim if (Text.empty()) 491226586Sdim Text.assign(ContentBegin, ContentEnd); 492226586Sdim 493276479Sdim // Check that regex directives contain at least one regex. 494276479Sdim if (RegexKind && Text.find("{{") == StringRef::npos) { 495276479Sdim Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 496276479Sdim diag::err_verify_missing_regex) << Text; 497276479Sdim return false; 498276479Sdim } 499276479Sdim 500239462Sdim // Construct new directive. 501280031Sdim std::unique_ptr<Directive> D = Directive::create( 502280031Sdim RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); 503276479Sdim 504226586Sdim std::string Error; 505239462Sdim if (D->isValid(Error)) { 506280031Sdim DL->push_back(std::move(D)); 507239462Sdim FoundDirective = true; 508239462Sdim } else { 509239462Sdim Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 510239462Sdim diag::err_verify_invalid_content) 511226586Sdim << KindStr << Error; 512226586Sdim } 513226586Sdim } 514239462Sdim 515239462Sdim return FoundDirective; 516226586Sdim} 517226586Sdim 518239462Sdim/// HandleComment - Hook into the preprocessor and extract comments containing 519239462Sdim/// expected errors and warnings. 520239462Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 521239462Sdim SourceRange Comment) { 522239462Sdim SourceManager &SM = PP.getSourceManager(); 523251662Sdim 524251662Sdim // If this comment is for a different source manager, ignore it. 525251662Sdim if (SrcManager && &SM != SrcManager) 526251662Sdim return false; 527251662Sdim 528239462Sdim SourceLocation CommentBegin = Comment.getBegin(); 529239462Sdim 530239462Sdim const char *CommentRaw = SM.getCharacterData(CommentBegin); 531239462Sdim StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 532239462Sdim 533239462Sdim if (C.empty()) 534239462Sdim return false; 535239462Sdim 536239462Sdim // Fold any "\<EOL>" sequences 537239462Sdim size_t loc = C.find('\\'); 538239462Sdim if (loc == StringRef::npos) { 539251662Sdim ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 540239462Sdim return false; 541239462Sdim } 542239462Sdim 543239462Sdim std::string C2; 544239462Sdim C2.reserve(C.size()); 545239462Sdim 546239462Sdim for (size_t last = 0;; loc = C.find('\\', last)) { 547239462Sdim if (loc == StringRef::npos || loc == C.size()) { 548239462Sdim C2 += C.substr(last); 549239462Sdim break; 550239462Sdim } 551239462Sdim C2 += C.substr(last, loc-last); 552239462Sdim last = loc + 1; 553239462Sdim 554239462Sdim if (C[last] == '\n' || C[last] == '\r') { 555239462Sdim ++last; 556239462Sdim 557239462Sdim // Escape \r\n or \n\r, but not \n\n. 558239462Sdim if (last < C.size()) 559239462Sdim if (C[last] == '\n' || C[last] == '\r') 560239462Sdim if (C[last] != C[last-1]) 561239462Sdim ++last; 562239462Sdim } else { 563239462Sdim // This was just a normal backslash. 564239462Sdim C2 += '\\'; 565239462Sdim } 566239462Sdim } 567239462Sdim 568239462Sdim if (!C2.empty()) 569251662Sdim ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 570239462Sdim return false; 571239462Sdim} 572239462Sdim 573239462Sdim#ifndef NDEBUG 574239462Sdim/// \brief Lex the specified source file to determine whether it contains 575239462Sdim/// any expected-* directives. As a Lexer is used rather than a full-blown 576239462Sdim/// Preprocessor, directives inside skipped #if blocks will still be found. 577239462Sdim/// 578239462Sdim/// \return true if any directives were found. 579243830Sdimstatic bool findDirectives(SourceManager &SM, FileID FID, 580243830Sdim const LangOptions &LangOpts) { 581226586Sdim // Create a raw lexer to pull all the comments out of FID. 582226586Sdim if (FID.isInvalid()) 583239462Sdim return false; 584226586Sdim 585226586Sdim // Create a lexer to lex all the tokens of the main file in raw mode. 586226586Sdim const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 587243830Sdim Lexer RawLex(FID, FromFile, SM, LangOpts); 588226586Sdim 589226586Sdim // Return comments as tokens, this is how we find expected diagnostics. 590226586Sdim RawLex.SetCommentRetentionState(true); 591226586Sdim 592226586Sdim Token Tok; 593226586Sdim Tok.setKind(tok::comment); 594243830Sdim VerifyDiagnosticConsumer::DirectiveStatus Status = 595243830Sdim VerifyDiagnosticConsumer::HasNoDirectives; 596226586Sdim while (Tok.isNot(tok::eof)) { 597261991Sdim RawLex.LexFromRawLexer(Tok); 598226586Sdim if (!Tok.is(tok::comment)) continue; 599226586Sdim 600243830Sdim std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 601226586Sdim if (Comment.empty()) continue; 602226586Sdim 603243830Sdim // Find first directive. 604276479Sdim if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), 605276479Sdim Status)) 606243830Sdim return true; 607239462Sdim } 608243830Sdim return false; 609226586Sdim} 610239462Sdim#endif // !NDEBUG 611226586Sdim 612239462Sdim/// \brief Takes a list of diagnostics that have been generated but not matched 613239462Sdim/// by an expected-* directive and produces a diagnostic to the user from this. 614239462Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 615239462Sdim const_diag_iterator diag_begin, 616239462Sdim const_diag_iterator diag_end, 617239462Sdim const char *Kind) { 618226586Sdim if (diag_begin == diag_end) return 0; 619226586Sdim 620234353Sdim SmallString<256> Fmt; 621226586Sdim llvm::raw_svector_ostream OS(Fmt); 622226586Sdim for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 623226586Sdim if (I->first.isInvalid() || !SourceMgr) 624226586Sdim OS << "\n (frontend)"; 625251662Sdim else { 626251662Sdim OS << "\n "; 627251662Sdim if (const FileEntry *File = SourceMgr->getFileEntryForID( 628251662Sdim SourceMgr->getFileID(I->first))) 629251662Sdim OS << " File " << File->getName(); 630251662Sdim OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 631251662Sdim } 632226586Sdim OS << ": " << I->second; 633226586Sdim } 634226586Sdim 635239462Sdim Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 636239462Sdim << Kind << /*Unexpected=*/true << OS.str(); 637226586Sdim return std::distance(diag_begin, diag_end); 638226586Sdim} 639226586Sdim 640239462Sdim/// \brief Takes a list of diagnostics that were expected to have been generated 641239462Sdim/// but were not and produces a diagnostic to the user from this. 642280031Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags, 643280031Sdim SourceManager &SourceMgr, 644280031Sdim std::vector<Directive *> &DL, const char *Kind) { 645226586Sdim if (DL.empty()) 646226586Sdim return 0; 647226586Sdim 648234353Sdim SmallString<256> Fmt; 649226586Sdim llvm::raw_svector_ostream OS(Fmt); 650280031Sdim for (auto *DirPtr : DL) { 651280031Sdim Directive &D = *DirPtr; 652276479Sdim OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc); 653276479Sdim if (D.MatchAnyLine) 654276479Sdim OS << " Line *"; 655276479Sdim else 656276479Sdim OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 657239462Sdim if (D.DirectiveLoc != D.DiagnosticLoc) 658239462Sdim OS << " (directive at " 659251662Sdim << SourceMgr.getFilename(D.DirectiveLoc) << ':' 660251662Sdim << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 661226586Sdim OS << ": " << D.Text; 662226586Sdim } 663226586Sdim 664239462Sdim Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 665239462Sdim << Kind << /*Unexpected=*/false << OS.str(); 666226586Sdim return DL.size(); 667226586Sdim} 668226586Sdim 669251662Sdim/// \brief Determine whether two source locations come from the same file. 670251662Sdimstatic bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 671251662Sdim SourceLocation DiagnosticLoc) { 672251662Sdim while (DiagnosticLoc.isMacroID()) 673251662Sdim DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 674251662Sdim 675261991Sdim if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 676251662Sdim return true; 677251662Sdim 678251662Sdim const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 679261991Sdim if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 680251662Sdim return true; 681251662Sdim 682251662Sdim return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 683251662Sdim} 684251662Sdim 685226586Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the 686226586Sdim/// the difference between them. 687226586Sdim/// 688226586Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 689226586Sdim const char *Label, 690226586Sdim DirectiveList &Left, 691226586Sdim const_diag_iterator d2_begin, 692288943Sdim const_diag_iterator d2_end, 693288943Sdim bool IgnoreUnexpected) { 694280031Sdim std::vector<Directive *> LeftOnly; 695226586Sdim DiagList Right(d2_begin, d2_end); 696226586Sdim 697280031Sdim for (auto &Owner : Left) { 698280031Sdim Directive &D = *Owner; 699239462Sdim unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 700226586Sdim 701239462Sdim for (unsigned i = 0; i < D.Max; ++i) { 702226586Sdim DiagList::iterator II, IE; 703226586Sdim for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 704276479Sdim if (!D.MatchAnyLine) { 705276479Sdim unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 706276479Sdim if (LineNo1 != LineNo2) 707276479Sdim continue; 708276479Sdim } 709226586Sdim 710251662Sdim if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 711251662Sdim continue; 712251662Sdim 713226586Sdim const std::string &RightText = II->second; 714239462Sdim if (D.match(RightText)) 715226586Sdim break; 716226586Sdim } 717226586Sdim if (II == IE) { 718226586Sdim // Not found. 719239462Sdim if (i >= D.Min) break; 720280031Sdim LeftOnly.push_back(&D); 721226586Sdim } else { 722226586Sdim // Found. The same cannot be found twice. 723226586Sdim Right.erase(II); 724226586Sdim } 725226586Sdim } 726226586Sdim } 727226586Sdim // Now all that's left in Right are those that were not matched. 728239462Sdim unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 729288943Sdim if (!IgnoreUnexpected) 730288943Sdim num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 731234353Sdim return num; 732226586Sdim} 733226586Sdim 734226586Sdim/// CheckResults - This compares the expected results to those that 735226586Sdim/// were actually reported. It emits any discrepencies. Return "true" if there 736226586Sdim/// were problems. Return "false" otherwise. 737226586Sdim/// 738226586Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 739226586Sdim const TextDiagnosticBuffer &Buffer, 740226586Sdim ExpectedData &ED) { 741226586Sdim // We want to capture the delta between what was expected and what was 742226586Sdim // seen. 743226586Sdim // 744226586Sdim // Expected \ Seen - set expected but not seen 745226586Sdim // Seen \ Expected - set seen but not expected 746226586Sdim unsigned NumProblems = 0; 747226586Sdim 748288943Sdim const DiagnosticLevelMask DiagMask = 749288943Sdim Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 750288943Sdim 751226586Sdim // See if there are error mismatches. 752226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 753288943Sdim Buffer.err_begin(), Buffer.err_end(), 754288943Sdim bool(DiagnosticLevelMask::Error & DiagMask)); 755226586Sdim 756226586Sdim // See if there are warning mismatches. 757226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 758288943Sdim Buffer.warn_begin(), Buffer.warn_end(), 759288943Sdim bool(DiagnosticLevelMask::Warning & DiagMask)); 760226586Sdim 761276479Sdim // See if there are remark mismatches. 762276479Sdim NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, 763288943Sdim Buffer.remark_begin(), Buffer.remark_end(), 764288943Sdim bool(DiagnosticLevelMask::Remark & DiagMask)); 765276479Sdim 766226586Sdim // See if there are note mismatches. 767226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 768288943Sdim Buffer.note_begin(), Buffer.note_end(), 769288943Sdim bool(DiagnosticLevelMask::Note & DiagMask)); 770226586Sdim 771226586Sdim return NumProblems; 772226586Sdim} 773226586Sdim 774243830Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 775243830Sdim FileID FID, 776243830Sdim ParsedStatus PS) { 777243830Sdim // Check SourceManager hasn't changed. 778243830Sdim setSourceManager(SM); 779243830Sdim 780243830Sdim#ifndef NDEBUG 781243830Sdim if (FID.isInvalid()) 782243830Sdim return; 783243830Sdim 784243830Sdim const FileEntry *FE = SM.getFileEntryForID(FID); 785243830Sdim 786243830Sdim if (PS == IsParsed) { 787243830Sdim // Move the FileID from the unparsed set to the parsed set. 788243830Sdim UnparsedFiles.erase(FID); 789243830Sdim ParsedFiles.insert(std::make_pair(FID, FE)); 790243830Sdim } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 791243830Sdim // Add the FileID to the unparsed set if we haven't seen it before. 792243830Sdim 793243830Sdim // Check for directives. 794243830Sdim bool FoundDirectives; 795243830Sdim if (PS == IsUnparsedNoDirectives) 796243830Sdim FoundDirectives = false; 797243830Sdim else 798243830Sdim FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 799243830Sdim 800243830Sdim // Add the FileID to the unparsed set. 801243830Sdim UnparsedFiles.insert(std::make_pair(FID, 802243830Sdim UnparsedFileStatus(FE, FoundDirectives))); 803243830Sdim } 804243830Sdim#endif 805243830Sdim} 806243830Sdim 807226586Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() { 808226586Sdim // Ensure any diagnostics go to the primary client. 809280031Sdim DiagnosticConsumer *CurClient = Diags.getClient(); 810280031Sdim std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); 811226586Sdim Diags.setClient(PrimaryClient, false); 812226586Sdim 813243830Sdim#ifndef NDEBUG 814243830Sdim // In a debug build, scan through any files that may have been missed 815243830Sdim // during parsing and issue a fatal error if directives are contained 816243830Sdim // within these files. If a fatal error occurs, this suggests that 817243830Sdim // this file is being parsed separately from the main file, in which 818243830Sdim // case consider moving the directives to the correct place, if this 819243830Sdim // is applicable. 820243830Sdim if (UnparsedFiles.size() > 0) { 821243830Sdim // Generate a cache of parsed FileEntry pointers for alias lookups. 822243830Sdim llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 823243830Sdim for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 824243830Sdim End = ParsedFiles.end(); I != End; ++I) { 825243830Sdim if (const FileEntry *FE = I->second) 826243830Sdim ParsedFileCache.insert(FE); 827243830Sdim } 828239462Sdim 829243830Sdim // Iterate through list of unparsed files. 830243830Sdim for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 831243830Sdim End = UnparsedFiles.end(); I != End; ++I) { 832243830Sdim const UnparsedFileStatus &Status = I->second; 833243830Sdim const FileEntry *FE = Status.getFile(); 834243830Sdim 835243830Sdim // Skip files that have been parsed via an alias. 836243830Sdim if (FE && ParsedFileCache.count(FE)) 837239462Sdim continue; 838239462Sdim 839243830Sdim // Report a fatal error if this file contained directives. 840243830Sdim if (Status.foundDirectives()) { 841239462Sdim llvm::report_fatal_error(Twine("-verify directives found after rather" 842239462Sdim " than during normal parsing of ", 843243830Sdim StringRef(FE ? FE->getName() : "(unknown)"))); 844243830Sdim } 845226586Sdim } 846226586Sdim 847243830Sdim // UnparsedFiles has been processed now, so clear it. 848243830Sdim UnparsedFiles.clear(); 849243830Sdim } 850243830Sdim#endif // !NDEBUG 851243830Sdim 852243830Sdim if (SrcManager) { 853243830Sdim // Produce an error if no expected-* directives could be found in the 854243830Sdim // source file(s) processed. 855243830Sdim if (Status == HasNoDirectives) { 856243830Sdim Diags.Report(diag::err_verify_no_directives).setForceEmit(); 857243830Sdim ++NumErrors; 858243830Sdim Status = HasNoDirectivesReported; 859243830Sdim } 860243830Sdim 861226586Sdim // Check that the expected diagnostics occurred. 862243830Sdim NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 863226586Sdim } else { 864288943Sdim const DiagnosticLevelMask DiagMask = 865288943Sdim ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 866288943Sdim if (bool(DiagnosticLevelMask::Error & DiagMask)) 867288943Sdim NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), 868288943Sdim Buffer->err_end(), "error"); 869288943Sdim if (bool(DiagnosticLevelMask::Warning & DiagMask)) 870288943Sdim NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), 871288943Sdim Buffer->warn_end(), "warn"); 872288943Sdim if (bool(DiagnosticLevelMask::Remark & DiagMask)) 873288943Sdim NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), 874288943Sdim Buffer->remark_end(), "remark"); 875288943Sdim if (bool(DiagnosticLevelMask::Note & DiagMask)) 876288943Sdim NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), 877288943Sdim Buffer->note_end(), "note"); 878226586Sdim } 879226586Sdim 880280031Sdim Diags.setClient(CurClient, Owner.release() != nullptr); 881226586Sdim 882226586Sdim // Reset the buffer, we have processed all the diagnostics in it. 883226586Sdim Buffer.reset(new TextDiagnosticBuffer()); 884276479Sdim ED.Reset(); 885226586Sdim} 886226586Sdim 887280031Sdimstd::unique_ptr<Directive> Directive::create(bool RegexKind, 888280031Sdim SourceLocation DirectiveLoc, 889280031Sdim SourceLocation DiagnosticLoc, 890280031Sdim bool MatchAnyLine, StringRef Text, 891280031Sdim unsigned Min, unsigned Max) { 892276479Sdim if (!RegexKind) 893280031Sdim return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, 894280031Sdim MatchAnyLine, Text, Min, Max); 895276479Sdim 896276479Sdim // Parse the directive into a regular expression. 897276479Sdim std::string RegexStr; 898276479Sdim StringRef S = Text; 899276479Sdim while (!S.empty()) { 900276479Sdim if (S.startswith("{{")) { 901276479Sdim S = S.drop_front(2); 902276479Sdim size_t RegexMatchLength = S.find("}}"); 903276479Sdim assert(RegexMatchLength != StringRef::npos); 904276479Sdim // Append the regex, enclosed in parentheses. 905276479Sdim RegexStr += "("; 906276479Sdim RegexStr.append(S.data(), RegexMatchLength); 907276479Sdim RegexStr += ")"; 908276479Sdim S = S.drop_front(RegexMatchLength + 2); 909276479Sdim } else { 910276479Sdim size_t VerbatimMatchLength = S.find("{{"); 911276479Sdim if (VerbatimMatchLength == StringRef::npos) 912276479Sdim VerbatimMatchLength = S.size(); 913276479Sdim // Escape and append the fixed string. 914276479Sdim RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 915276479Sdim S = S.drop_front(VerbatimMatchLength); 916276479Sdim } 917276479Sdim } 918276479Sdim 919280031Sdim return llvm::make_unique<RegexDirective>( 920280031Sdim DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); 921226586Sdim} 922