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 S is matched in content. 233 // When true, P marks begin-position of the match, and calling Advance sets C 234 // to end-position of the match. 235 // If S is the empty string, then search for any letter instead (makes sense 236 // with FinishDirectiveToken=true). 237 // If EnsureStartOfWord, then skip matches that don't start a new word. 238 // If FinishDirectiveToken, then assume the match is the start of a comment 239 // directive for -verify, and extend the match to include the entire first 240 // token of that directive. 241 bool Search(StringRef S, bool EnsureStartOfWord = false, 242 bool FinishDirectiveToken = false) { 243 do { 244 if (!S.empty()) { 245 P = std::search(C, End, S.begin(), S.end()); 246 PEnd = P + S.size(); 247 } 248 else { 249 P = C; 250 while (P != End && !isLetter(*P)) 251 ++P; 252 PEnd = P + 1; 253 } 254 if (P == End) 255 break; 256 // If not start of word but required, skip and search again. 257 if (EnsureStartOfWord 258 // Check if string literal starts a new word. 259 && !(P == Begin || isWhitespace(P[-1]) 260 // Or it could be preceded by the start of a comment. 261 || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') 262 && P[-2] == '/'))) 263 continue; 264 if (FinishDirectiveToken) { 265 while (PEnd != End && (isAlphanumeric(*PEnd) 266 || *PEnd == '-' || *PEnd == '_')) 267 ++PEnd; 268 // Put back trailing digits and hyphens to be parsed later as a count 269 // or count range. Because -verify prefixes must start with letters, 270 // we know the actual directive we found starts with a letter, so 271 // we won't put back the entire directive word and thus record an empty 272 // string. 273 assert(isLetter(*P) && "-verify prefix must start with a letter"); 274 while (isDigit(PEnd[-1]) || PEnd[-1] == '-') 275 --PEnd; 276 } 277 return true; 278 } while (Advance()); 279 return false; 280 } 281 282 // Return true if a CloseBrace that closes the OpenBrace at the current nest 283 // level is found. When true, P marks begin-position of CloseBrace. 284 bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { 285 unsigned Depth = 1; 286 P = C; 287 while (P < End) { 288 StringRef S(P, End - P); 289 if (S.startswith(OpenBrace)) { 290 ++Depth; 291 P += OpenBrace.size(); 292 } else if (S.startswith(CloseBrace)) { 293 --Depth; 294 if (Depth == 0) { 295 PEnd = P + CloseBrace.size(); 296 return true; 297 } 298 P += CloseBrace.size(); 299 } else { 300 ++P; 301 } 302 } 303 return false; 304 } 305 306 // Advance 1-past previous next/search. 307 // Behavior is undefined if previous next/search failed. 308 bool Advance() { 309 C = PEnd; 310 return C < End; 311 } 312 313 // Skip zero or more whitespace. 314 void SkipWhitespace() { 315 for (; C < End && isWhitespace(*C); ++C) 316 ; 317 } 318 319 // Return true if EOF reached. 320 bool Done() { 321 return !(C < End); 322 } 323 324 const char * const Begin; // beginning of expected content 325 const char * const End; // end of expected content (1-past) 326 const char *C; // position of next char in content 327 const char *P; 328 329 private: 330 const char *PEnd; // previous next/search subject end (1-past) 331 }; 332 333 } // namespace anonymous 334 335 /// ParseDirective - Go through the comment and see if it indicates expected 336 /// diagnostics. If so, then put them in the appropriate directive list. 337 /// 338 /// Returns true if any valid directives were found. 339 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, 340 Preprocessor *PP, SourceLocation Pos, 341 VerifyDiagnosticConsumer::DirectiveStatus &Status) { 342 DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); 343 344 // A single comment may contain multiple directives. 345 bool FoundDirective = false; 346 for (ParseHelper PH(S); !PH.Done();) { 347 // Search for the initial directive token. 348 // If one prefix, save time by searching only for its directives. 349 // Otherwise, search for any potential directive token and check it later. 350 const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; 351 if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) 352 : PH.Search("", true, true))) 353 break; 354 PH.Advance(); 355 356 // Default directive kind. 357 bool RegexKind = false; 358 const char* KindStr = "string"; 359 360 // Parse the initial directive token in reverse so we can easily determine 361 // its exact actual prefix. If we were to parse it from the front instead, 362 // it would be harder to determine where the prefix ends because there 363 // might be multiple matching -verify prefixes because some might prefix 364 // others. 365 StringRef DToken(PH.P, PH.C - PH.P); 366 367 // Regex in initial directive token: -re 368 if (DToken.endswith("-re")) { 369 RegexKind = true; 370 KindStr = "regex"; 371 DToken = DToken.substr(0, DToken.size()-3); 372 } 373 374 // Type in initial directive token: -{error|warning|note|no-diagnostics} 375 DirectiveList *DL = nullptr; 376 bool NoDiag = false; 377 StringRef DType; 378 if (DToken.endswith(DType="-error")) 379 DL = ED ? &ED->Errors : nullptr; 380 else if (DToken.endswith(DType="-warning")) 381 DL = ED ? &ED->Warnings : nullptr; 382 else if (DToken.endswith(DType="-remark")) 383 DL = ED ? &ED->Remarks : nullptr; 384 else if (DToken.endswith(DType="-note")) 385 DL = ED ? &ED->Notes : nullptr; 386 else if (DToken.endswith(DType="-no-diagnostics")) { 387 NoDiag = true; 388 if (RegexKind) 389 continue; 390 } 391 else 392 continue; 393 DToken = DToken.substr(0, DToken.size()-DType.size()); 394 395 // What's left in DToken is the actual prefix. That might not be a -verify 396 // prefix even if there is only one -verify prefix (for example, the full 397 // DToken is foo-bar-warning, but foo is the only -verify prefix). 398 if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) 399 continue; 400 401 if (NoDiag) { 402 if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 403 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 404 << /*IsExpectedNoDiagnostics=*/true; 405 else 406 Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 407 continue; 408 } 409 if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 410 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 411 << /*IsExpectedNoDiagnostics=*/false; 412 continue; 413 } 414 Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 415 416 // If a directive has been found but we're not interested 417 // in storing the directive information, return now. 418 if (!DL) 419 return true; 420 421 // Next optional token: @ 422 SourceLocation ExpectedLoc; 423 bool MatchAnyLine = false; 424 if (!PH.Next("@")) { 425 ExpectedLoc = Pos; 426 } else { 427 PH.Advance(); 428 unsigned Line = 0; 429 bool FoundPlus = PH.Next("+"); 430 if (FoundPlus || PH.Next("-")) { 431 // Relative to current line. 432 PH.Advance(); 433 bool Invalid = false; 434 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 435 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 436 if (FoundPlus) ExpectedLine += Line; 437 else ExpectedLine -= Line; 438 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 439 } 440 } else if (PH.Next(Line)) { 441 // Absolute line number. 442 if (Line > 0) 443 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 444 } else if (PP && PH.Search(":")) { 445 // Specific source file. 446 StringRef Filename(PH.C, PH.P-PH.C); 447 PH.Advance(); 448 449 // Lookup file via Preprocessor, like a #include. 450 const DirectoryLookup *CurDir; 451 const FileEntry *FE = 452 PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, 453 nullptr, nullptr, nullptr, nullptr); 454 if (!FE) { 455 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 456 diag::err_verify_missing_file) << Filename << KindStr; 457 continue; 458 } 459 460 if (SM.translateFile(FE).isInvalid()) 461 SM.createFileID(FE, Pos, SrcMgr::C_User); 462 463 if (PH.Next(Line) && Line > 0) 464 ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); 465 else if (PH.Next("*")) { 466 MatchAnyLine = true; 467 ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); 468 } 469 } else if (PH.Next("*")) { 470 MatchAnyLine = true; 471 ExpectedLoc = SourceLocation(); 472 } 473 474 if (ExpectedLoc.isInvalid() && !MatchAnyLine) { 475 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 476 diag::err_verify_missing_line) << KindStr; 477 continue; 478 } 479 PH.Advance(); 480 } 481 482 // Skip optional whitespace. 483 PH.SkipWhitespace(); 484 485 // Next optional token: positive integer or a '+'. 486 unsigned Min = 1; 487 unsigned Max = 1; 488 if (PH.Next(Min)) { 489 PH.Advance(); 490 // A positive integer can be followed by a '+' meaning min 491 // or more, or by a '-' meaning a range from min to max. 492 if (PH.Next("+")) { 493 Max = Directive::MaxCount; 494 PH.Advance(); 495 } else if (PH.Next("-")) { 496 PH.Advance(); 497 if (!PH.Next(Max) || Max < Min) { 498 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 499 diag::err_verify_invalid_range) << KindStr; 500 continue; 501 } 502 PH.Advance(); 503 } else { 504 Max = Min; 505 } 506 } else if (PH.Next("+")) { 507 // '+' on its own means "1 or more". 508 Max = Directive::MaxCount; 509 PH.Advance(); 510 } 511 512 // Skip optional whitespace. 513 PH.SkipWhitespace(); 514 515 // Next token: {{ 516 if (!PH.Next("{{")) { 517 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 518 diag::err_verify_missing_start) << KindStr; 519 continue; 520 } 521 PH.Advance(); 522 const char* const ContentBegin = PH.C; // mark content begin 523 524 // Search for token: }} 525 if (!PH.SearchClosingBrace("{{", "}}")) { 526 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 527 diag::err_verify_missing_end) << KindStr; 528 continue; 529 } 530 const char* const ContentEnd = PH.P; // mark content end 531 PH.Advance(); 532 533 // Build directive text; convert \n to newlines. 534 std::string Text; 535 StringRef NewlineStr = "\\n"; 536 StringRef Content(ContentBegin, ContentEnd-ContentBegin); 537 size_t CPos = 0; 538 size_t FPos; 539 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 540 Text += Content.substr(CPos, FPos-CPos); 541 Text += '\n'; 542 CPos = FPos + NewlineStr.size(); 543 } 544 if (Text.empty()) 545 Text.assign(ContentBegin, ContentEnd); 546 547 // Check that regex directives contain at least one regex. 548 if (RegexKind && Text.find("{{") == StringRef::npos) { 549 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 550 diag::err_verify_missing_regex) << Text; 551 return false; 552 } 553 554 // Construct new directive. 555 std::unique_ptr<Directive> D = Directive::create( 556 RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); 557 558 std::string Error; 559 if (D->isValid(Error)) { 560 DL->push_back(std::move(D)); 561 FoundDirective = true; 562 } else { 563 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 564 diag::err_verify_invalid_content) 565 << KindStr << Error; 566 } 567 } 568 569 return FoundDirective; 570 } 571 572 /// HandleComment - Hook into the preprocessor and extract comments containing 573 /// expected errors and warnings. 574 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 575 SourceRange Comment) { 576 SourceManager &SM = PP.getSourceManager(); 577 578 // If this comment is for a different source manager, ignore it. 579 if (SrcManager && &SM != SrcManager) 580 return false; 581 582 SourceLocation CommentBegin = Comment.getBegin(); 583 584 const char *CommentRaw = SM.getCharacterData(CommentBegin); 585 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 586 587 if (C.empty()) 588 return false; 589 590 // Fold any "\<EOL>" sequences 591 size_t loc = C.find('\\'); 592 if (loc == StringRef::npos) { 593 ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 594 return false; 595 } 596 597 std::string C2; 598 C2.reserve(C.size()); 599 600 for (size_t last = 0;; loc = C.find('\\', last)) { 601 if (loc == StringRef::npos || loc == C.size()) { 602 C2 += C.substr(last); 603 break; 604 } 605 C2 += C.substr(last, loc-last); 606 last = loc + 1; 607 608 if (C[last] == '\n' || C[last] == '\r') { 609 ++last; 610 611 // Escape \r\n or \n\r, but not \n\n. 612 if (last < C.size()) 613 if (C[last] == '\n' || C[last] == '\r') 614 if (C[last] != C[last-1]) 615 ++last; 616 } else { 617 // This was just a normal backslash. 618 C2 += '\\'; 619 } 620 } 621 622 if (!C2.empty()) 623 ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 624 return false; 625 } 626 627 #ifndef NDEBUG 628 /// \brief Lex the specified source file to determine whether it contains 629 /// any expected-* directives. As a Lexer is used rather than a full-blown 630 /// Preprocessor, directives inside skipped #if blocks will still be found. 631 /// 632 /// \return true if any directives were found. 633 static bool findDirectives(SourceManager &SM, FileID FID, 634 const LangOptions &LangOpts) { 635 // Create a raw lexer to pull all the comments out of FID. 636 if (FID.isInvalid()) 637 return false; 638 639 // Create a lexer to lex all the tokens of the main file in raw mode. 640 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 641 Lexer RawLex(FID, FromFile, SM, LangOpts); 642 643 // Return comments as tokens, this is how we find expected diagnostics. 644 RawLex.SetCommentRetentionState(true); 645 646 Token Tok; 647 Tok.setKind(tok::comment); 648 VerifyDiagnosticConsumer::DirectiveStatus Status = 649 VerifyDiagnosticConsumer::HasNoDirectives; 650 while (Tok.isNot(tok::eof)) { 651 RawLex.LexFromRawLexer(Tok); 652 if (!Tok.is(tok::comment)) continue; 653 654 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 655 if (Comment.empty()) continue; 656 657 // Find first directive. 658 if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), 659 Status)) 660 return true; 661 } 662 return false; 663 } 664 #endif // !NDEBUG 665 666 /// \brief Takes a list of diagnostics that have been generated but not matched 667 /// by an expected-* directive and produces a diagnostic to the user from this. 668 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 669 const_diag_iterator diag_begin, 670 const_diag_iterator diag_end, 671 const char *Kind) { 672 if (diag_begin == diag_end) return 0; 673 674 SmallString<256> Fmt; 675 llvm::raw_svector_ostream OS(Fmt); 676 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 677 if (I->first.isInvalid() || !SourceMgr) 678 OS << "\n (frontend)"; 679 else { 680 OS << "\n "; 681 if (const FileEntry *File = SourceMgr->getFileEntryForID( 682 SourceMgr->getFileID(I->first))) 683 OS << " File " << File->getName(); 684 OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 685 } 686 OS << ": " << I->second; 687 } 688 689 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 690 << Kind << /*Unexpected=*/true << OS.str(); 691 return std::distance(diag_begin, diag_end); 692 } 693 694 /// \brief Takes a list of diagnostics that were expected to have been generated 695 /// but were not and produces a diagnostic to the user from this. 696 static unsigned PrintExpected(DiagnosticsEngine &Diags, 697 SourceManager &SourceMgr, 698 std::vector<Directive *> &DL, const char *Kind) { 699 if (DL.empty()) 700 return 0; 701 702 SmallString<256> Fmt; 703 llvm::raw_svector_ostream OS(Fmt); 704 for (auto *DirPtr : DL) { 705 Directive &D = *DirPtr; 706 if (D.DiagnosticLoc.isInvalid()) 707 OS << "\n File *"; 708 else 709 OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc); 710 if (D.MatchAnyLine) 711 OS << " Line *"; 712 else 713 OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 714 if (D.DirectiveLoc != D.DiagnosticLoc) 715 OS << " (directive at " 716 << SourceMgr.getFilename(D.DirectiveLoc) << ':' 717 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 718 OS << ": " << D.Text; 719 } 720 721 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 722 << Kind << /*Unexpected=*/false << OS.str(); 723 return DL.size(); 724 } 725 726 /// \brief Determine whether two source locations come from the same file. 727 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 728 SourceLocation DiagnosticLoc) { 729 while (DiagnosticLoc.isMacroID()) 730 DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 731 732 if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 733 return true; 734 735 const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 736 if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 737 return true; 738 739 return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 740 } 741 742 /// CheckLists - Compare expected to seen diagnostic lists and return the 743 /// the difference between them. 744 /// 745 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 746 const char *Label, 747 DirectiveList &Left, 748 const_diag_iterator d2_begin, 749 const_diag_iterator d2_end, 750 bool IgnoreUnexpected) { 751 std::vector<Directive *> LeftOnly; 752 DiagList Right(d2_begin, d2_end); 753 754 for (auto &Owner : Left) { 755 Directive &D = *Owner; 756 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 757 758 for (unsigned i = 0; i < D.Max; ++i) { 759 DiagList::iterator II, IE; 760 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 761 if (!D.MatchAnyLine) { 762 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 763 if (LineNo1 != LineNo2) 764 continue; 765 } 766 767 if (!D.DiagnosticLoc.isInvalid() && 768 !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 769 continue; 770 771 const std::string &RightText = II->second; 772 if (D.match(RightText)) 773 break; 774 } 775 if (II == IE) { 776 // Not found. 777 if (i >= D.Min) break; 778 LeftOnly.push_back(&D); 779 } else { 780 // Found. The same cannot be found twice. 781 Right.erase(II); 782 } 783 } 784 } 785 // Now all that's left in Right are those that were not matched. 786 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 787 if (!IgnoreUnexpected) 788 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 789 return num; 790 } 791 792 /// CheckResults - This compares the expected results to those that 793 /// were actually reported. It emits any discrepencies. Return "true" if there 794 /// were problems. Return "false" otherwise. 795 /// 796 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 797 const TextDiagnosticBuffer &Buffer, 798 ExpectedData &ED) { 799 // We want to capture the delta between what was expected and what was 800 // seen. 801 // 802 // Expected \ Seen - set expected but not seen 803 // Seen \ Expected - set seen but not expected 804 unsigned NumProblems = 0; 805 806 const DiagnosticLevelMask DiagMask = 807 Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 808 809 // See if there are error mismatches. 810 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 811 Buffer.err_begin(), Buffer.err_end(), 812 bool(DiagnosticLevelMask::Error & DiagMask)); 813 814 // See if there are warning mismatches. 815 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 816 Buffer.warn_begin(), Buffer.warn_end(), 817 bool(DiagnosticLevelMask::Warning & DiagMask)); 818 819 // See if there are remark mismatches. 820 NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, 821 Buffer.remark_begin(), Buffer.remark_end(), 822 bool(DiagnosticLevelMask::Remark & DiagMask)); 823 824 // See if there are note mismatches. 825 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 826 Buffer.note_begin(), Buffer.note_end(), 827 bool(DiagnosticLevelMask::Note & DiagMask)); 828 829 return NumProblems; 830 } 831 832 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 833 FileID FID, 834 ParsedStatus PS) { 835 // Check SourceManager hasn't changed. 836 setSourceManager(SM); 837 838 #ifndef NDEBUG 839 if (FID.isInvalid()) 840 return; 841 842 const FileEntry *FE = SM.getFileEntryForID(FID); 843 844 if (PS == IsParsed) { 845 // Move the FileID from the unparsed set to the parsed set. 846 UnparsedFiles.erase(FID); 847 ParsedFiles.insert(std::make_pair(FID, FE)); 848 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 849 // Add the FileID to the unparsed set if we haven't seen it before. 850 851 // Check for directives. 852 bool FoundDirectives; 853 if (PS == IsUnparsedNoDirectives) 854 FoundDirectives = false; 855 else 856 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 857 858 // Add the FileID to the unparsed set. 859 UnparsedFiles.insert(std::make_pair(FID, 860 UnparsedFileStatus(FE, FoundDirectives))); 861 } 862 #endif 863 } 864 865 void VerifyDiagnosticConsumer::CheckDiagnostics() { 866 // Ensure any diagnostics go to the primary client. 867 DiagnosticConsumer *CurClient = Diags.getClient(); 868 std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); 869 Diags.setClient(PrimaryClient, false); 870 871 #ifndef NDEBUG 872 // In a debug build, scan through any files that may have been missed 873 // during parsing and issue a fatal error if directives are contained 874 // within these files. If a fatal error occurs, this suggests that 875 // this file is being parsed separately from the main file, in which 876 // case consider moving the directives to the correct place, if this 877 // is applicable. 878 if (UnparsedFiles.size() > 0) { 879 // Generate a cache of parsed FileEntry pointers for alias lookups. 880 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 881 for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 882 End = ParsedFiles.end(); I != End; ++I) { 883 if (const FileEntry *FE = I->second) 884 ParsedFileCache.insert(FE); 885 } 886 887 // Iterate through list of unparsed files. 888 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 889 End = UnparsedFiles.end(); I != End; ++I) { 890 const UnparsedFileStatus &Status = I->second; 891 const FileEntry *FE = Status.getFile(); 892 893 // Skip files that have been parsed via an alias. 894 if (FE && ParsedFileCache.count(FE)) 895 continue; 896 897 // Report a fatal error if this file contained directives. 898 if (Status.foundDirectives()) { 899 llvm::report_fatal_error(Twine("-verify directives found after rather" 900 " than during normal parsing of ", 901 StringRef(FE ? FE->getName() : "(unknown)"))); 902 } 903 } 904 905 // UnparsedFiles has been processed now, so clear it. 906 UnparsedFiles.clear(); 907 } 908 #endif // !NDEBUG 909 910 if (SrcManager) { 911 // Produce an error if no expected-* directives could be found in the 912 // source file(s) processed. 913 if (Status == HasNoDirectives) { 914 Diags.Report(diag::err_verify_no_directives).setForceEmit(); 915 ++NumErrors; 916 Status = HasNoDirectivesReported; 917 } 918 919 // Check that the expected diagnostics occurred. 920 NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 921 } else { 922 const DiagnosticLevelMask DiagMask = 923 ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); 924 if (bool(DiagnosticLevelMask::Error & DiagMask)) 925 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), 926 Buffer->err_end(), "error"); 927 if (bool(DiagnosticLevelMask::Warning & DiagMask)) 928 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), 929 Buffer->warn_end(), "warn"); 930 if (bool(DiagnosticLevelMask::Remark & DiagMask)) 931 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), 932 Buffer->remark_end(), "remark"); 933 if (bool(DiagnosticLevelMask::Note & DiagMask)) 934 NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), 935 Buffer->note_end(), "note"); 936 } 937 938 Diags.setClient(CurClient, Owner.release() != nullptr); 939 940 // Reset the buffer, we have processed all the diagnostics in it. 941 Buffer.reset(new TextDiagnosticBuffer()); 942 ED.Reset(); 943 } 944 945 std::unique_ptr<Directive> Directive::create(bool RegexKind, 946 SourceLocation DirectiveLoc, 947 SourceLocation DiagnosticLoc, 948 bool MatchAnyLine, StringRef Text, 949 unsigned Min, unsigned Max) { 950 if (!RegexKind) 951 return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, 952 MatchAnyLine, Text, Min, Max); 953 954 // Parse the directive into a regular expression. 955 std::string RegexStr; 956 StringRef S = Text; 957 while (!S.empty()) { 958 if (S.startswith("{{")) { 959 S = S.drop_front(2); 960 size_t RegexMatchLength = S.find("}}"); 961 assert(RegexMatchLength != StringRef::npos); 962 // Append the regex, enclosed in parentheses. 963 RegexStr += "("; 964 RegexStr.append(S.data(), RegexMatchLength); 965 RegexStr += ")"; 966 S = S.drop_front(RegexMatchLength + 2); 967 } else { 968 size_t VerbatimMatchLength = S.find("{{"); 969 if (VerbatimMatchLength == StringRef::npos) 970 VerbatimMatchLength = S.size(); 971 // Escape and append the fixed string. 972 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 973 S = S.drop_front(VerbatimMatchLength); 974 } 975 } 976 977 return llvm::make_unique<RegexDirective>( 978 DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); 979 } 980