1 //===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This is a concrete diagnostic client, which buffers the diagnostic messages. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Frontend/VerifyDiagnosticConsumer.h" 15 #include "clang/Basic/CharInfo.h" 16 #include "clang/Basic/FileManager.h" 17 #include "clang/Frontend/FrontendDiagnostic.h" 18 #include "clang/Frontend/TextDiagnosticBuffer.h" 19 #include "clang/Lex/HeaderSearch.h" 20 #include "clang/Lex/Preprocessor.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/Support/Regex.h" 23 #include "llvm/Support/raw_ostream.h" 24 25 using namespace clang; 26 typedef VerifyDiagnosticConsumer::Directive Directive; 27 typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; 28 typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; 29 30 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) 31 : Diags(Diags_), 32 PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()), 33 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr), 34 LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0), 35 Status(HasNoDirectives) 36 { 37 if (Diags.hasSourceManager()) 38 setSourceManager(Diags.getSourceManager()); 39 } 40 41 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { 42 assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); 43 assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); 44 SrcManager = nullptr; 45 CheckDiagnostics(); 46 assert(!Diags.ownsClient() && 47 "The VerifyDiagnosticConsumer takes over ownership of the client!"); 48 } 49 50 #ifndef NDEBUG 51 namespace { 52 class VerifyFileTracker : public PPCallbacks { 53 VerifyDiagnosticConsumer &Verify; 54 SourceManager &SM; 55 56 public: 57 VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) 58 : Verify(Verify), SM(SM) { } 59 60 /// \brief Hook into the preprocessor and update the list of parsed 61 /// files when the preprocessor indicates a new file is entered. 62 void FileChanged(SourceLocation Loc, FileChangeReason Reason, 63 SrcMgr::CharacteristicKind FileType, 64 FileID PrevFID) override { 65 Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), 66 VerifyDiagnosticConsumer::IsParsed); 67 } 68 }; 69 } // End anonymous namespace. 70 #endif 71 72 // DiagnosticConsumer interface. 73 74 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 75 const Preprocessor *PP) { 76 // Attach comment handler on first invocation. 77 if (++ActiveSourceFiles == 1) { 78 if (PP) { 79 CurrentPreprocessor = PP; 80 this->LangOpts = &LangOpts; 81 setSourceManager(PP->getSourceManager()); 82 const_cast<Preprocessor*>(PP)->addCommentHandler(this); 83 #ifndef NDEBUG 84 // Debug build tracks parsed files. 85 const_cast<Preprocessor*>(PP)->addPPCallbacks( 86 llvm::make_unique<VerifyFileTracker>(*this, *SrcManager)); 87 #endif 88 } 89 } 90 91 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); 92 PrimaryClient->BeginSourceFile(LangOpts, PP); 93 } 94 95 void VerifyDiagnosticConsumer::EndSourceFile() { 96 assert(ActiveSourceFiles && "No active source files!"); 97 PrimaryClient->EndSourceFile(); 98 99 // Detach comment handler once last active source file completed. 100 if (--ActiveSourceFiles == 0) { 101 if (CurrentPreprocessor) 102 const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); 103 104 // Check diagnostics once last file completed. 105 CheckDiagnostics(); 106 CurrentPreprocessor = nullptr; 107 LangOpts = nullptr; 108 } 109 } 110 111 void VerifyDiagnosticConsumer::HandleDiagnostic( 112 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 113 if (Info.hasSourceManager()) { 114 // If this diagnostic is for a different source manager, ignore it. 115 if (SrcManager && &Info.getSourceManager() != SrcManager) 116 return; 117 118 setSourceManager(Info.getSourceManager()); 119 } 120 121 #ifndef NDEBUG 122 // Debug build tracks unparsed files for possible 123 // unparsed expected-* directives. 124 if (SrcManager) { 125 SourceLocation Loc = Info.getLocation(); 126 if (Loc.isValid()) { 127 ParsedStatus PS = IsUnparsed; 128 129 Loc = SrcManager->getExpansionLoc(Loc); 130 FileID FID = SrcManager->getFileID(Loc); 131 132 const FileEntry *FE = SrcManager->getFileEntryForID(FID); 133 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { 134 // If the file is a modules header file it shall not be parsed 135 // for expected-* directives. 136 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); 137 if (HS.findModuleForHeader(FE)) 138 PS = IsUnparsedNoDirectives; 139 } 140 141 UpdateParsedFileStatus(*SrcManager, FID, PS); 142 } 143 } 144 #endif 145 146 // Send the diagnostic to the buffer, we will check it once we reach the end 147 // of the source file (or are destructed). 148 Buffer->HandleDiagnostic(DiagLevel, Info); 149 } 150 151 //===----------------------------------------------------------------------===// 152 // Checking diagnostics implementation. 153 //===----------------------------------------------------------------------===// 154 155 typedef TextDiagnosticBuffer::DiagList DiagList; 156 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; 157 158 namespace { 159 160 /// StandardDirective - Directive with string matching. 161 /// 162 class StandardDirective : public Directive { 163 public: 164 StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 165 bool MatchAnyLine, StringRef Text, unsigned Min, 166 unsigned Max) 167 : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { } 168 169 bool isValid(std::string &Error) override { 170 // all strings are considered valid; even empty ones 171 return true; 172 } 173 174 bool match(StringRef S) override { 175 return S.find(Text) != StringRef::npos; 176 } 177 }; 178 179 /// RegexDirective - Directive with regular-expression matching. 180 /// 181 class RegexDirective : public Directive { 182 public: 183 RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 184 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, 185 StringRef RegexStr) 186 : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), 187 Regex(RegexStr) { } 188 189 bool isValid(std::string &Error) override { 190 return Regex.isValid(Error); 191 } 192 193 bool match(StringRef S) override { 194 return Regex.match(S); 195 } 196 197 private: 198 llvm::Regex Regex; 199 }; 200 201 class ParseHelper 202 { 203 public: 204 ParseHelper(StringRef S) 205 : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {} 206 207 // Return true if string literal is next. 208 bool Next(StringRef S) { 209 P = C; 210 PEnd = C + S.size(); 211 if (PEnd > End) 212 return false; 213 return !memcmp(P, S.data(), S.size()); 214 } 215 216 // Return true if number is next. 217 // Output N only if number is next. 218 bool Next(unsigned &N) { 219 unsigned TMP = 0; 220 P = C; 221 for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { 222 TMP *= 10; 223 TMP += P[0] - '0'; 224 } 225 if (P == C) 226 return false; 227 PEnd = P; 228 N = TMP; 229 return true; 230 } 231 232 // Return true if string literal is found. 233 // When true, P marks begin-position of S in content. 234 bool Search(StringRef S, bool EnsureStartOfWord = false) { 235 do { 236 P = std::search(C, End, S.begin(), S.end()); 237 PEnd = P + S.size(); 238 if (P == End) 239 break; 240 if (!EnsureStartOfWord 241 // Check if string literal starts a new word. 242 || P == Begin || isWhitespace(P[-1]) 243 // Or it could be preceded by the start of a comment. 244 || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') 245 && P[-2] == '/')) 246 return true; 247 // Otherwise, skip and search again. 248 } while (Advance()); 249 return false; 250 } 251 252 // Return true if a CloseBrace that closes the OpenBrace at the current nest 253 // level is found. When true, P marks begin-position of CloseBrace. 254 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { 255 unsigned Depth = 1; 256 P = C; 257 while (P < End) { 258 StringRef S(P, End - P); 259 if (S.startswith(OpenBrace)) { 260 ++Depth; 261 P += OpenBrace.size(); 262 } else if (S.startswith(CloseBrace)) { 263 --Depth; 264 if (Depth == 0) { 265 PEnd = P + CloseBrace.size(); 266 return true; 267 } 268 P += CloseBrace.size(); 269 } else { 270 ++P; 271 } 272 } 273 return false; 274 } 275 276 // Advance 1-past previous next/search. 277 // Behavior is undefined if previous next/search failed. 278 bool Advance() { 279 C = PEnd; 280 return C < End; 281 } 282 283 // Skip zero or more whitespace. 284 void SkipWhitespace() { 285 for (; C < End && isWhitespace(*C); ++C) 286 ; 287 } 288 289 // Return true if EOF reached. 290 bool Done() { 291 return !(C < End); 292 } 293 294 const char * const Begin; // beginning of expected content 295 const char * const End; // end of expected content (1-past) 296 const char *C; // position of next char in content 297 const char *P; 298 299 private: 300 const char *PEnd; // previous next/search subject end (1-past) 301 }; 302 303 } // namespace anonymous 304 305 /// ParseDirective - Go through the comment and see if it indicates expected 306 /// diagnostics. If so, then put them in the appropriate directive list. 307 /// 308 /// Returns true if any valid directives were found. 309 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, 310 Preprocessor *PP, SourceLocation Pos, 311 VerifyDiagnosticConsumer::DirectiveStatus &Status) { 312 DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); 313 314 // A single comment may contain multiple directives. 315 bool FoundDirective = false; 316 for (ParseHelper PH(S); !PH.Done();) { 317 // Search for token: expected 318 if (!PH.Search("expected", true)) 319 break; 320 PH.Advance(); 321 322 // Next token: - 323 if (!PH.Next("-")) 324 continue; 325 PH.Advance(); 326 327 // Next token: { error | warning | note } 328 DirectiveList *DL = nullptr; 329 if (PH.Next("error")) 330 DL = ED ? &ED->Errors : nullptr; 331 else if (PH.Next("warning")) 332 DL = ED ? &ED->Warnings : nullptr; 333 else if (PH.Next("remark")) 334 DL = ED ? &ED->Remarks : nullptr; 335 else if (PH.Next("note")) 336 DL = ED ? &ED->Notes : nullptr; 337 else if (PH.Next("no-diagnostics")) { 338 if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 339 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 340 << /*IsExpectedNoDiagnostics=*/true; 341 else 342 Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 343 continue; 344 } else 345 continue; 346 PH.Advance(); 347 348 if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 349 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 350 << /*IsExpectedNoDiagnostics=*/false; 351 continue; 352 } 353 Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 354 355 // If a directive has been found but we're not interested 356 // in storing the directive information, return now. 357 if (!DL) 358 return true; 359 360 // Default directive kind. 361 bool RegexKind = false; 362 const char* KindStr = "string"; 363 364 // Next optional token: - 365 if (PH.Next("-re")) { 366 PH.Advance(); 367 RegexKind = true; 368 KindStr = "regex"; 369 } 370 371 // Next optional token: @ 372 SourceLocation ExpectedLoc; 373 bool MatchAnyLine = false; 374 if (!PH.Next("@")) { 375 ExpectedLoc = Pos; 376 } else { 377 PH.Advance(); 378 unsigned Line = 0; 379 bool FoundPlus = PH.Next("+"); 380 if (FoundPlus || PH.Next("-")) { 381 // Relative to current line. 382 PH.Advance(); 383 bool Invalid = false; 384 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 385 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 386 if (FoundPlus) ExpectedLine += Line; 387 else ExpectedLine -= Line; 388 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 389 } 390 } else if (PH.Next(Line)) { 391 // Absolute line number. 392 if (Line > 0) 393 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 394 } else if (PP && PH.Search(":")) { 395 // Specific source file. 396 StringRef Filename(PH.C, PH.P-PH.C); 397 PH.Advance(); 398 399 // Lookup file via Preprocessor, like a #include. 400 const DirectoryLookup *CurDir; 401 const FileEntry *FE = 402 PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, 403 nullptr, nullptr, nullptr, nullptr); 404 if (!FE) { 405 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 406 diag::err_verify_missing_file) << Filename << KindStr; 407 continue; 408 } 409 410 if (SM.translateFile(FE).isInvalid()) 411 SM.createFileID(FE, Pos, SrcMgr::C_User); 412 413 if (PH.Next(Line) && Line > 0) 414 ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); 415 else if (PH.Next("*")) { 416 MatchAnyLine = true; 417 ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); 418 } 419 } else if (PH.Next("*")) { 420 MatchAnyLine = true; 421 ExpectedLoc = SourceLocation(); 422 } 423 424 if (ExpectedLoc.isInvalid() && !MatchAnyLine) { 425 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 426 diag::err_verify_missing_line) << KindStr; 427 continue; 428 } 429 PH.Advance(); 430 } 431 432 // Skip optional whitespace. 433 PH.SkipWhitespace(); 434 435 // Next optional token: positive integer or a '+'. 436 unsigned Min = 1; 437 unsigned Max = 1; 438 if (PH.Next(Min)) { 439 PH.Advance(); 440 // A positive integer can be followed by a '+' meaning min 441 // or more, or by a '-' meaning a range from min to max. 442 if (PH.Next("+")) { 443 Max = Directive::MaxCount; 444 PH.Advance(); 445 } else if (PH.Next("-")) { 446 PH.Advance(); 447 if (!PH.Next(Max) || Max < Min) { 448 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 449 diag::err_verify_invalid_range) << KindStr; 450 continue; 451 } 452 PH.Advance(); 453 } else { 454 Max = Min; 455 } 456 } else if (PH.Next("+")) { 457 // '+' on its own means "1 or more". 458 Max = Directive::MaxCount; 459 PH.Advance(); 460 } 461 462 // Skip optional whitespace. 463 PH.SkipWhitespace(); 464 465 // Next token: {{ 466 if (!PH.Next("{{")) { 467 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 468 diag::err_verify_missing_start) << KindStr; 469 continue; 470 } 471 PH.Advance(); 472 const char* const ContentBegin = PH.C; // mark content begin 473 474 // Search for token: }} 475 if (!PH.SearchClosingBrace("{{", "}}")) { 476 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 477 diag::err_verify_missing_end) << KindStr; 478 continue; 479 } 480 const char* const ContentEnd = PH.P; // mark content end 481 PH.Advance(); 482 483 // Build directive text; convert \n to newlines. 484 std::string Text; 485 StringRef NewlineStr = "\\n"; 486 StringRef Content(ContentBegin, ContentEnd-ContentBegin); 487 size_t CPos = 0; 488 size_t FPos; 489 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 490 Text += Content.substr(CPos, FPos-CPos); 491 Text += '\n'; 492 CPos = FPos + NewlineStr.size(); 493 } 494 if (Text.empty()) 495 Text.assign(ContentBegin, ContentEnd); 496 497 // Check that regex directives contain at least one regex. 498 if (RegexKind && Text.find("{{") == StringRef::npos) { 499 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 500 diag::err_verify_missing_regex) << Text; 501 return false; 502 } 503 504 // Construct new directive. 505 std::unique_ptr<Directive> D = Directive::create( 506 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); 507 508 std::string Error; 509 if (D->isValid(Error)) { 510 DL->push_back(std::move(D)); 511 FoundDirective = true; 512 } else { 513 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 514 diag::err_verify_invalid_content) 515 << KindStr << Error; 516 } 517 } 518 519 return FoundDirective; 520 } 521 522 /// HandleComment - Hook into the preprocessor and extract comments containing 523 /// expected errors and warnings. 524 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 525 SourceRange Comment) { 526 SourceManager &SM = PP.getSourceManager(); 527 528 // If this comment is for a different source manager, ignore it. 529 if (SrcManager && &SM != SrcManager) 530 return false; 531 532 SourceLocation CommentBegin = Comment.getBegin(); 533 534 const char *CommentRaw = SM.getCharacterData(CommentBegin); 535 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 536 537 if (C.empty()) 538 return false; 539 540 // Fold any "\<EOL>" sequences 541 size_t loc = C.find('\\'); 542 if (loc == StringRef::npos) { 543 ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 544 return false; 545 } 546 547 std::string C2; 548 C2.reserve(C.size()); 549 550 for (size_t last = 0;; loc = C.find('\\', last)) { 551 if (loc == StringRef::npos || loc == C.size()) { 552 C2 += C.substr(last); 553 break; 554 } 555 C2 += C.substr(last, loc-last); 556 last = loc + 1; 557 558 if (C[last] == '\n' || C[last] == '\r') { 559 ++last; 560 561 // Escape \r\n or \n\r, but not \n\n. 562 if (last < C.size()) 563 if (C[last] == '\n' || C[last] == '\r') 564 if (C[last] != C[last-1]) 565 ++last; 566 } else { 567 // This was just a normal backslash. 568 C2 += '\\'; 569 } 570 } 571 572 if (!C2.empty()) 573 ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 574 return false; 575 } 576 577 #ifndef NDEBUG 578 /// \brief Lex the specified source file to determine whether it contains 579 /// any expected-* directives. As a Lexer is used rather than a full-blown 580 /// Preprocessor, directives inside skipped #if blocks will still be found. 581 /// 582 /// \return true if any directives were found. 583 static bool findDirectives(SourceManager &SM, FileID FID, 584 const LangOptions &LangOpts) { 585 // Create a raw lexer to pull all the comments out of FID. 586 if (FID.isInvalid()) 587 return false; 588 589 // Create a lexer to lex all the tokens of the main file in raw mode. 590 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 591 Lexer RawLex(FID, FromFile, SM, LangOpts); 592 593 // Return comments as tokens, this is how we find expected diagnostics. 594 RawLex.SetCommentRetentionState(true); 595 596 Token Tok; 597 Tok.setKind(tok::comment); 598 VerifyDiagnosticConsumer::DirectiveStatus Status = 599 VerifyDiagnosticConsumer::HasNoDirectives; 600 while (Tok.isNot(tok::eof)) { 601 RawLex.LexFromRawLexer(Tok); 602 if (!Tok.is(tok::comment)) continue; 603 604 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 605 if (Comment.empty()) continue; 606 607 // Find first directive. 608 if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), 609 Status)) 610 return true; 611 } 612 return false; 613 } 614 #endif // !NDEBUG 615 616 /// \brief Takes a list of diagnostics that have been generated but not matched 617 /// by an expected-* directive and produces a diagnostic to the user from this. 618 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 619 const_diag_iterator diag_begin, 620 const_diag_iterator diag_end, 621 const char *Kind) { 622 if (diag_begin == diag_end) return 0; 623 624 SmallString<256> Fmt; 625 llvm::raw_svector_ostream OS(Fmt); 626 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 627 if (I->first.isInvalid() || !SourceMgr) 628 OS << "\n (frontend)"; 629 else { 630 OS << "\n "; 631 if (const FileEntry *File = SourceMgr->getFileEntryForID( 632 SourceMgr->getFileID(I->first))) 633 OS << " File " << File->getName(); 634 OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 635 } 636 OS << ": " << I->second; 637 } 638 639 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 640 << Kind << /*Unexpected=*/true << OS.str(); 641 return std::distance(diag_begin, diag_end); 642 } 643 644 /// \brief Takes a list of diagnostics that were expected to have been generated 645 /// but were not and produces a diagnostic to the user from this. 646 static unsigned PrintExpected(DiagnosticsEngine &Diags, 647 SourceManager &SourceMgr, 648 std::vector<Directive *> &DL, const char *Kind) { 649 if (DL.empty()) 650 return 0; 651 652 SmallString<256> Fmt; 653 llvm::raw_svector_ostream OS(Fmt); 654 for (auto *DirPtr : DL) { 655 Directive &D = *DirPtr; 656 if (D.DiagnosticLoc.isInvalid()) 657 OS << "\n File *"; 658 else 659 OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc); 660 if (D.MatchAnyLine) 661 OS << " Line *"; 662 else 663 OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 664 if (D.DirectiveLoc != D.DiagnosticLoc) 665 OS << " (directive at " 666 << SourceMgr.getFilename(D.DirectiveLoc) << ':' 667 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 668 OS << ": " << D.Text; 669 } 670 671 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 672 << Kind << /*Unexpected=*/false << OS.str(); 673 return DL.size(); 674 } 675 676 /// \brief Determine whether two source locations come from the same file. 677 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 678 SourceLocation DiagnosticLoc) { 679 while (DiagnosticLoc.isMacroID()) 680 DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 681 682 if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 683 return true; 684 685 const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 686 if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 687 return true; 688 689 return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 690 } 691 692 /// CheckLists - Compare expected to seen diagnostic lists and return the 693 /// the difference between them. 694 /// 695 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 696 const char *Label, 697 DirectiveList &Left, 698 const_diag_iterator d2_begin, 699 const_diag_iterator d2_end, 700 bool IgnoreUnexpected) { 701 std::vector<Directive *> LeftOnly; 702 DiagList Right(d2_begin, d2_end); 703 704 for (auto &Owner : Left) { 705 Directive &D = *Owner; 706 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 707 708 for (unsigned i = 0; i < D.Max; ++i) { 709 DiagList::iterator II, IE; 710 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 711 if (!D.MatchAnyLine) { 712 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 713 if (LineNo1 != LineNo2) 714 continue; 715 } 716 717 if (!D.DiagnosticLoc.isInvalid() && 718 !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 719 continue; 720 721 const std::string &RightText = II->second; 722 if (D.match(RightText)) 723 break; 724 } 725 if (II == IE) { 726 // Not found. 727 if (i >= D.Min) break; 728 LeftOnly.push_back(&D); 729 } else { 730 // Found. The same cannot be found twice. 731 Right.erase(II); 732 } 733 } 734 } 735 // Now all that's left in Right are those that were not matched. 736 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 737 if (!IgnoreUnexpected) 738 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 739 return num; 740 } 741 742 /// CheckResults - This compares the expected results to those that 743 /// were actually reported. It emits any discrepencies. Return "true" if there 744 /// were problems. Return "false" otherwise. 745 /// 746 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 747 const TextDiagnosticBuffer &Buffer, 748 ExpectedData &ED) { 749 // We want to capture the delta between what was expected and what was 750 // seen. 751 // 752 // Expected \ Seen - set expected but not seen 753 // Seen \ Expected - set seen but not expected 754 unsigned NumProblems = 0; 755 756 const DiagnosticLevelMask DiagMask = 757 Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 758 759 // See if there are error mismatches. 760 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 761 Buffer.err_begin(), Buffer.err_end(), 762 bool(DiagnosticLevelMask::Error & DiagMask)); 763 764 // See if there are warning mismatches. 765 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 766 Buffer.warn_begin(), Buffer.warn_end(), 767 bool(DiagnosticLevelMask::Warning & DiagMask)); 768 769 // See if there are remark mismatches. 770 NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, 771 Buffer.remark_begin(), Buffer.remark_end(), 772 bool(DiagnosticLevelMask::Remark & DiagMask)); 773 774 // See if there are note mismatches. 775 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 776 Buffer.note_begin(), Buffer.note_end(), 777 bool(DiagnosticLevelMask::Note & DiagMask)); 778 779 return NumProblems; 780 } 781 782 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 783 FileID FID, 784 ParsedStatus PS) { 785 // Check SourceManager hasn't changed. 786 setSourceManager(SM); 787 788 #ifndef NDEBUG 789 if (FID.isInvalid()) 790 return; 791 792 const FileEntry *FE = SM.getFileEntryForID(FID); 793 794 if (PS == IsParsed) { 795 // Move the FileID from the unparsed set to the parsed set. 796 UnparsedFiles.erase(FID); 797 ParsedFiles.insert(std::make_pair(FID, FE)); 798 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 799 // Add the FileID to the unparsed set if we haven't seen it before. 800 801 // Check for directives. 802 bool FoundDirectives; 803 if (PS == IsUnparsedNoDirectives) 804 FoundDirectives = false; 805 else 806 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 807 808 // Add the FileID to the unparsed set. 809 UnparsedFiles.insert(std::make_pair(FID, 810 UnparsedFileStatus(FE, FoundDirectives))); 811 } 812 #endif 813 } 814 815 void VerifyDiagnosticConsumer::CheckDiagnostics() { 816 // Ensure any diagnostics go to the primary client. 817 DiagnosticConsumer *CurClient = Diags.getClient(); 818 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); 819 Diags.setClient(PrimaryClient, false); 820 821 #ifndef NDEBUG 822 // In a debug build, scan through any files that may have been missed 823 // during parsing and issue a fatal error if directives are contained 824 // within these files. If a fatal error occurs, this suggests that 825 // this file is being parsed separately from the main file, in which 826 // case consider moving the directives to the correct place, if this 827 // is applicable. 828 if (UnparsedFiles.size() > 0) { 829 // Generate a cache of parsed FileEntry pointers for alias lookups. 830 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 831 for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 832 End = ParsedFiles.end(); I != End; ++I) { 833 if (const FileEntry *FE = I->second) 834 ParsedFileCache.insert(FE); 835 } 836 837 // Iterate through list of unparsed files. 838 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 839 End = UnparsedFiles.end(); I != End; ++I) { 840 const UnparsedFileStatus &Status = I->second; 841 const FileEntry *FE = Status.getFile(); 842 843 // Skip files that have been parsed via an alias. 844 if (FE && ParsedFileCache.count(FE)) 845 continue; 846 847 // Report a fatal error if this file contained directives. 848 if (Status.foundDirectives()) { 849 llvm::report_fatal_error(Twine("-verify directives found after rather" 850 " than during normal parsing of ", 851 StringRef(FE ? FE->getName() : "(unknown)"))); 852 } 853 } 854 855 // UnparsedFiles has been processed now, so clear it. 856 UnparsedFiles.clear(); 857 } 858 #endif // !NDEBUG 859 860 if (SrcManager) { 861 // Produce an error if no expected-* directives could be found in the 862 // source file(s) processed. 863 if (Status == HasNoDirectives) { 864 Diags.Report(diag::err_verify_no_directives).setForceEmit(); 865 ++NumErrors; 866 Status = HasNoDirectivesReported; 867 } 868 869 // Check that the expected diagnostics occurred. 870 NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 871 } else { 872 const DiagnosticLevelMask DiagMask = 873 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 874 if (bool(DiagnosticLevelMask::Error & DiagMask)) 875 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), 876 Buffer->err_end(), "error"); 877 if (bool(DiagnosticLevelMask::Warning & DiagMask)) 878 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), 879 Buffer->warn_end(), "warn"); 880 if (bool(DiagnosticLevelMask::Remark & DiagMask)) 881 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), 882 Buffer->remark_end(), "remark"); 883 if (bool(DiagnosticLevelMask::Note & DiagMask)) 884 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), 885 Buffer->note_end(), "note"); 886 } 887 888 Diags.setClient(CurClient, Owner.release() != nullptr); 889 890 // Reset the buffer, we have processed all the diagnostics in it. 891 Buffer.reset(new TextDiagnosticBuffer()); 892 ED.Reset(); 893 } 894 895 std::unique_ptr<Directive> Directive::create(bool RegexKind, 896 SourceLocation DirectiveLoc, 897 SourceLocation DiagnosticLoc, 898 bool MatchAnyLine, StringRef Text, 899 unsigned Min, unsigned Max) { 900 if (!RegexKind) 901 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, 902 MatchAnyLine, Text, Min, Max); 903 904 // Parse the directive into a regular expression. 905 std::string RegexStr; 906 StringRef S = Text; 907 while (!S.empty()) { 908 if (S.startswith("{{")) { 909 S = S.drop_front(2); 910 size_t RegexMatchLength = S.find("}}"); 911 assert(RegexMatchLength != StringRef::npos); 912 // Append the regex, enclosed in parentheses. 913 RegexStr += "("; 914 RegexStr.append(S.data(), RegexMatchLength); 915 RegexStr += ")"; 916 S = S.drop_front(RegexMatchLength + 2); 917 } else { 918 size_t VerbatimMatchLength = S.find("{{"); 919 if (VerbatimMatchLength == StringRef::npos) 920 VerbatimMatchLength = S.size(); 921 // Escape and append the fixed string. 922 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 923 S = S.drop_front(VerbatimMatchLength); 924 } 925 } 926 927 return llvm::make_unique<RegexDirective>( 928 DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); 929 } 930