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); 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 } 420 421 if (ExpectedLoc.isInvalid()) { 422 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 423 diag::err_verify_missing_line) << KindStr; 424 continue; 425 } 426 PH.Advance(); 427 } 428 429 // Skip optional whitespace. 430 PH.SkipWhitespace(); 431 432 // Next optional token: positive integer or a '+'. 433 unsigned Min = 1; 434 unsigned Max = 1; 435 if (PH.Next(Min)) { 436 PH.Advance(); 437 // A positive integer can be followed by a '+' meaning min 438 // or more, or by a '-' meaning a range from min to max. 439 if (PH.Next("+")) { 440 Max = Directive::MaxCount; 441 PH.Advance(); 442 } else if (PH.Next("-")) { 443 PH.Advance(); 444 if (!PH.Next(Max) || Max < Min) { 445 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 446 diag::err_verify_invalid_range) << KindStr; 447 continue; 448 } 449 PH.Advance(); 450 } else { 451 Max = Min; 452 } 453 } else if (PH.Next("+")) { 454 // '+' on its own means "1 or more". 455 Max = Directive::MaxCount; 456 PH.Advance(); 457 } 458 459 // Skip optional whitespace. 460 PH.SkipWhitespace(); 461 462 // Next token: {{ 463 if (!PH.Next("{{")) { 464 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 465 diag::err_verify_missing_start) << KindStr; 466 continue; 467 } 468 PH.Advance(); 469 const char* const ContentBegin = PH.C; // mark content begin 470 471 // Search for token: }} 472 if (!PH.SearchClosingBrace("{{", "}}")) { 473 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 474 diag::err_verify_missing_end) << KindStr; 475 continue; 476 } 477 const char* const ContentEnd = PH.P; // mark content end 478 PH.Advance(); 479 480 // Build directive text; convert \n to newlines. 481 std::string Text; 482 StringRef NewlineStr = "\\n"; 483 StringRef Content(ContentBegin, ContentEnd-ContentBegin); 484 size_t CPos = 0; 485 size_t FPos; 486 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 487 Text += Content.substr(CPos, FPos-CPos); 488 Text += '\n'; 489 CPos = FPos + NewlineStr.size(); 490 } 491 if (Text.empty()) 492 Text.assign(ContentBegin, ContentEnd); 493 494 // Check that regex directives contain at least one regex. 495 if (RegexKind && Text.find("{{") == StringRef::npos) { 496 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 497 diag::err_verify_missing_regex) << Text; 498 return false; 499 } 500 501 // Construct new directive. 502 std::unique_ptr<Directive> D = Directive::create( 503 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); 504 505 std::string Error; 506 if (D->isValid(Error)) { 507 DL->push_back(std::move(D)); 508 FoundDirective = true; 509 } else { 510 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 511 diag::err_verify_invalid_content) 512 << KindStr << Error; 513 } 514 } 515 516 return FoundDirective; 517 } 518 519 /// HandleComment - Hook into the preprocessor and extract comments containing 520 /// expected errors and warnings. 521 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 522 SourceRange Comment) { 523 SourceManager &SM = PP.getSourceManager(); 524 525 // If this comment is for a different source manager, ignore it. 526 if (SrcManager && &SM != SrcManager) 527 return false; 528 529 SourceLocation CommentBegin = Comment.getBegin(); 530 531 const char *CommentRaw = SM.getCharacterData(CommentBegin); 532 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 533 534 if (C.empty()) 535 return false; 536 537 // Fold any "\<EOL>" sequences 538 size_t loc = C.find('\\'); 539 if (loc == StringRef::npos) { 540 ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 541 return false; 542 } 543 544 std::string C2; 545 C2.reserve(C.size()); 546 547 for (size_t last = 0;; loc = C.find('\\', last)) { 548 if (loc == StringRef::npos || loc == C.size()) { 549 C2 += C.substr(last); 550 break; 551 } 552 C2 += C.substr(last, loc-last); 553 last = loc + 1; 554 555 if (C[last] == '\n' || C[last] == '\r') { 556 ++last; 557 558 // Escape \r\n or \n\r, but not \n\n. 559 if (last < C.size()) 560 if (C[last] == '\n' || C[last] == '\r') 561 if (C[last] != C[last-1]) 562 ++last; 563 } else { 564 // This was just a normal backslash. 565 C2 += '\\'; 566 } 567 } 568 569 if (!C2.empty()) 570 ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 571 return false; 572 } 573 574 #ifndef NDEBUG 575 /// \brief Lex the specified source file to determine whether it contains 576 /// any expected-* directives. As a Lexer is used rather than a full-blown 577 /// Preprocessor, directives inside skipped #if blocks will still be found. 578 /// 579 /// \return true if any directives were found. 580 static bool findDirectives(SourceManager &SM, FileID FID, 581 const LangOptions &LangOpts) { 582 // Create a raw lexer to pull all the comments out of FID. 583 if (FID.isInvalid()) 584 return false; 585 586 // Create a lexer to lex all the tokens of the main file in raw mode. 587 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 588 Lexer RawLex(FID, FromFile, SM, LangOpts); 589 590 // Return comments as tokens, this is how we find expected diagnostics. 591 RawLex.SetCommentRetentionState(true); 592 593 Token Tok; 594 Tok.setKind(tok::comment); 595 VerifyDiagnosticConsumer::DirectiveStatus Status = 596 VerifyDiagnosticConsumer::HasNoDirectives; 597 while (Tok.isNot(tok::eof)) { 598 RawLex.LexFromRawLexer(Tok); 599 if (!Tok.is(tok::comment)) continue; 600 601 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 602 if (Comment.empty()) continue; 603 604 // Find first directive. 605 if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), 606 Status)) 607 return true; 608 } 609 return false; 610 } 611 #endif // !NDEBUG 612 613 /// \brief Takes a list of diagnostics that have been generated but not matched 614 /// by an expected-* directive and produces a diagnostic to the user from this. 615 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 616 const_diag_iterator diag_begin, 617 const_diag_iterator diag_end, 618 const char *Kind) { 619 if (diag_begin == diag_end) return 0; 620 621 SmallString<256> Fmt; 622 llvm::raw_svector_ostream OS(Fmt); 623 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 624 if (I->first.isInvalid() || !SourceMgr) 625 OS << "\n (frontend)"; 626 else { 627 OS << "\n "; 628 if (const FileEntry *File = SourceMgr->getFileEntryForID( 629 SourceMgr->getFileID(I->first))) 630 OS << " File " << File->getName(); 631 OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 632 } 633 OS << ": " << I->second; 634 } 635 636 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 637 << Kind << /*Unexpected=*/true << OS.str(); 638 return std::distance(diag_begin, diag_end); 639 } 640 641 /// \brief Takes a list of diagnostics that were expected to have been generated 642 /// but were not and produces a diagnostic to the user from this. 643 static unsigned PrintExpected(DiagnosticsEngine &Diags, 644 SourceManager &SourceMgr, 645 std::vector<Directive *> &DL, const char *Kind) { 646 if (DL.empty()) 647 return 0; 648 649 SmallString<256> Fmt; 650 llvm::raw_svector_ostream OS(Fmt); 651 for (auto *DirPtr : DL) { 652 Directive &D = *DirPtr; 653 OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc); 654 if (D.MatchAnyLine) 655 OS << " Line *"; 656 else 657 OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 658 if (D.DirectiveLoc != D.DiagnosticLoc) 659 OS << " (directive at " 660 << SourceMgr.getFilename(D.DirectiveLoc) << ':' 661 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 662 OS << ": " << D.Text; 663 } 664 665 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 666 << Kind << /*Unexpected=*/false << OS.str(); 667 return DL.size(); 668 } 669 670 /// \brief Determine whether two source locations come from the same file. 671 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 672 SourceLocation DiagnosticLoc) { 673 while (DiagnosticLoc.isMacroID()) 674 DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 675 676 if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 677 return true; 678 679 const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 680 if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 681 return true; 682 683 return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 684 } 685 686 /// CheckLists - Compare expected to seen diagnostic lists and return the 687 /// the difference between them. 688 /// 689 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 690 const char *Label, 691 DirectiveList &Left, 692 const_diag_iterator d2_begin, 693 const_diag_iterator d2_end, 694 bool IgnoreUnexpected) { 695 std::vector<Directive *> LeftOnly; 696 DiagList Right(d2_begin, d2_end); 697 698 for (auto &Owner : Left) { 699 Directive &D = *Owner; 700 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 701 702 for (unsigned i = 0; i < D.Max; ++i) { 703 DiagList::iterator II, IE; 704 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 705 if (!D.MatchAnyLine) { 706 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 707 if (LineNo1 != LineNo2) 708 continue; 709 } 710 711 if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 712 continue; 713 714 const std::string &RightText = II->second; 715 if (D.match(RightText)) 716 break; 717 } 718 if (II == IE) { 719 // Not found. 720 if (i >= D.Min) break; 721 LeftOnly.push_back(&D); 722 } else { 723 // Found. The same cannot be found twice. 724 Right.erase(II); 725 } 726 } 727 } 728 // Now all that's left in Right are those that were not matched. 729 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 730 if (!IgnoreUnexpected) 731 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 732 return num; 733 } 734 735 /// CheckResults - This compares the expected results to those that 736 /// were actually reported. It emits any discrepencies. Return "true" if there 737 /// were problems. Return "false" otherwise. 738 /// 739 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 740 const TextDiagnosticBuffer &Buffer, 741 ExpectedData &ED) { 742 // We want to capture the delta between what was expected and what was 743 // seen. 744 // 745 // Expected \ Seen - set expected but not seen 746 // Seen \ Expected - set seen but not expected 747 unsigned NumProblems = 0; 748 749 const DiagnosticLevelMask DiagMask = 750 Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 751 752 // See if there are error mismatches. 753 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 754 Buffer.err_begin(), Buffer.err_end(), 755 bool(DiagnosticLevelMask::Error & DiagMask)); 756 757 // See if there are warning mismatches. 758 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 759 Buffer.warn_begin(), Buffer.warn_end(), 760 bool(DiagnosticLevelMask::Warning & DiagMask)); 761 762 // See if there are remark mismatches. 763 NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, 764 Buffer.remark_begin(), Buffer.remark_end(), 765 bool(DiagnosticLevelMask::Remark & DiagMask)); 766 767 // See if there are note mismatches. 768 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 769 Buffer.note_begin(), Buffer.note_end(), 770 bool(DiagnosticLevelMask::Note & DiagMask)); 771 772 return NumProblems; 773 } 774 775 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 776 FileID FID, 777 ParsedStatus PS) { 778 // Check SourceManager hasn't changed. 779 setSourceManager(SM); 780 781 #ifndef NDEBUG 782 if (FID.isInvalid()) 783 return; 784 785 const FileEntry *FE = SM.getFileEntryForID(FID); 786 787 if (PS == IsParsed) { 788 // Move the FileID from the unparsed set to the parsed set. 789 UnparsedFiles.erase(FID); 790 ParsedFiles.insert(std::make_pair(FID, FE)); 791 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 792 // Add the FileID to the unparsed set if we haven't seen it before. 793 794 // Check for directives. 795 bool FoundDirectives; 796 if (PS == IsUnparsedNoDirectives) 797 FoundDirectives = false; 798 else 799 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 800 801 // Add the FileID to the unparsed set. 802 UnparsedFiles.insert(std::make_pair(FID, 803 UnparsedFileStatus(FE, FoundDirectives))); 804 } 805 #endif 806 } 807 808 void VerifyDiagnosticConsumer::CheckDiagnostics() { 809 // Ensure any diagnostics go to the primary client. 810 DiagnosticConsumer *CurClient = Diags.getClient(); 811 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); 812 Diags.setClient(PrimaryClient, false); 813 814 #ifndef NDEBUG 815 // In a debug build, scan through any files that may have been missed 816 // during parsing and issue a fatal error if directives are contained 817 // within these files. If a fatal error occurs, this suggests that 818 // this file is being parsed separately from the main file, in which 819 // case consider moving the directives to the correct place, if this 820 // is applicable. 821 if (UnparsedFiles.size() > 0) { 822 // Generate a cache of parsed FileEntry pointers for alias lookups. 823 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 824 for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 825 End = ParsedFiles.end(); I != End; ++I) { 826 if (const FileEntry *FE = I->second) 827 ParsedFileCache.insert(FE); 828 } 829 830 // Iterate through list of unparsed files. 831 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 832 End = UnparsedFiles.end(); I != End; ++I) { 833 const UnparsedFileStatus &Status = I->second; 834 const FileEntry *FE = Status.getFile(); 835 836 // Skip files that have been parsed via an alias. 837 if (FE && ParsedFileCache.count(FE)) 838 continue; 839 840 // Report a fatal error if this file contained directives. 841 if (Status.foundDirectives()) { 842 llvm::report_fatal_error(Twine("-verify directives found after rather" 843 " than during normal parsing of ", 844 StringRef(FE ? FE->getName() : "(unknown)"))); 845 } 846 } 847 848 // UnparsedFiles has been processed now, so clear it. 849 UnparsedFiles.clear(); 850 } 851 #endif // !NDEBUG 852 853 if (SrcManager) { 854 // Produce an error if no expected-* directives could be found in the 855 // source file(s) processed. 856 if (Status == HasNoDirectives) { 857 Diags.Report(diag::err_verify_no_directives).setForceEmit(); 858 ++NumErrors; 859 Status = HasNoDirectivesReported; 860 } 861 862 // Check that the expected diagnostics occurred. 863 NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 864 } else { 865 const DiagnosticLevelMask DiagMask = 866 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 867 if (bool(DiagnosticLevelMask::Error & DiagMask)) 868 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), 869 Buffer->err_end(), "error"); 870 if (bool(DiagnosticLevelMask::Warning & DiagMask)) 871 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), 872 Buffer->warn_end(), "warn"); 873 if (bool(DiagnosticLevelMask::Remark & DiagMask)) 874 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), 875 Buffer->remark_end(), "remark"); 876 if (bool(DiagnosticLevelMask::Note & DiagMask)) 877 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), 878 Buffer->note_end(), "note"); 879 } 880 881 Diags.setClient(CurClient, Owner.release() != nullptr); 882 883 // Reset the buffer, we have processed all the diagnostics in it. 884 Buffer.reset(new TextDiagnosticBuffer()); 885 ED.Reset(); 886 } 887 888 std::unique_ptr<Directive> Directive::create(bool RegexKind, 889 SourceLocation DirectiveLoc, 890 SourceLocation DiagnosticLoc, 891 bool MatchAnyLine, StringRef Text, 892 unsigned Min, unsigned Max) { 893 if (!RegexKind) 894 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, 895 MatchAnyLine, Text, Min, Max); 896 897 // Parse the directive into a regular expression. 898 std::string RegexStr; 899 StringRef S = Text; 900 while (!S.empty()) { 901 if (S.startswith("{{")) { 902 S = S.drop_front(2); 903 size_t RegexMatchLength = S.find("}}"); 904 assert(RegexMatchLength != StringRef::npos); 905 // Append the regex, enclosed in parentheses. 906 RegexStr += "("; 907 RegexStr.append(S.data(), RegexMatchLength); 908 RegexStr += ")"; 909 S = S.drop_front(RegexMatchLength + 2); 910 } else { 911 size_t VerbatimMatchLength = S.find("{{"); 912 if (VerbatimMatchLength == StringRef::npos) 913 VerbatimMatchLength = S.size(); 914 // Escape and append the fixed string. 915 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 916 S = S.drop_front(VerbatimMatchLength); 917 } 918 } 919 920 return llvm::make_unique<RegexDirective>( 921 DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); 922 } 923