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()), OwnsPrimaryClient(Diags.ownsClient()), 33 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), 34 LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives) 35 { 36 Diags.takeClient(); 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 = 0; 45 CheckDiagnostics(); 46 Diags.takeClient(); 47 if (OwnsPrimaryClient) 48 delete PrimaryClient; 49 } 50 51 #ifndef NDEBUG 52 namespace { 53 class VerifyFileTracker : public PPCallbacks { 54 VerifyDiagnosticConsumer &Verify; 55 SourceManager &SM; 56 57 public: 58 VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) 59 : Verify(Verify), SM(SM) { } 60 61 /// \brief Hook into the preprocessor and update the list of parsed 62 /// files when the preprocessor indicates a new file is entered. 63 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, 64 SrcMgr::CharacteristicKind FileType, 65 FileID PrevFID) { 66 Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), 67 VerifyDiagnosticConsumer::IsParsed); 68 } 69 }; 70 } // End anonymous namespace. 71 #endif 72 73 // DiagnosticConsumer interface. 74 75 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 76 const Preprocessor *PP) { 77 // Attach comment handler on first invocation. 78 if (++ActiveSourceFiles == 1) { 79 if (PP) { 80 CurrentPreprocessor = PP; 81 this->LangOpts = &LangOpts; 82 setSourceManager(PP->getSourceManager()); 83 const_cast<Preprocessor*>(PP)->addCommentHandler(this); 84 #ifndef NDEBUG 85 // Debug build tracks parsed files. 86 VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager); 87 const_cast<Preprocessor*>(PP)->addPPCallbacks(V); 88 #endif 89 } 90 } 91 92 assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); 93 PrimaryClient->BeginSourceFile(LangOpts, PP); 94 } 95 96 void VerifyDiagnosticConsumer::EndSourceFile() { 97 assert(ActiveSourceFiles && "No active source files!"); 98 PrimaryClient->EndSourceFile(); 99 100 // Detach comment handler once last active source file completed. 101 if (--ActiveSourceFiles == 0) { 102 if (CurrentPreprocessor) 103 const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); 104 105 // Check diagnostics once last file completed. 106 CheckDiagnostics(); 107 CurrentPreprocessor = 0; 108 LangOpts = 0; 109 } 110 } 111 112 void VerifyDiagnosticConsumer::HandleDiagnostic( 113 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 114 if (Info.hasSourceManager()) { 115 // If this diagnostic is for a different source manager, ignore it. 116 if (SrcManager && &Info.getSourceManager() != SrcManager) 117 return; 118 119 setSourceManager(Info.getSourceManager()); 120 } 121 122 #ifndef NDEBUG 123 // Debug build tracks unparsed files for possible 124 // unparsed expected-* directives. 125 if (SrcManager) { 126 SourceLocation Loc = Info.getLocation(); 127 if (Loc.isValid()) { 128 ParsedStatus PS = IsUnparsed; 129 130 Loc = SrcManager->getExpansionLoc(Loc); 131 FileID FID = SrcManager->getFileID(Loc); 132 133 const FileEntry *FE = SrcManager->getFileEntryForID(FID); 134 if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { 135 // If the file is a modules header file it shall not be parsed 136 // for expected-* directives. 137 HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); 138 if (HS.findModuleForHeader(FE)) 139 PS = IsUnparsedNoDirectives; 140 } 141 142 UpdateParsedFileStatus(*SrcManager, FID, PS); 143 } 144 } 145 #endif 146 147 // Send the diagnostic to the buffer, we will check it once we reach the end 148 // of the source file (or are destructed). 149 Buffer->HandleDiagnostic(DiagLevel, Info); 150 } 151 152 //===----------------------------------------------------------------------===// 153 // Checking diagnostics implementation. 154 //===----------------------------------------------------------------------===// 155 156 typedef TextDiagnosticBuffer::DiagList DiagList; 157 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; 158 159 namespace { 160 161 /// StandardDirective - Directive with string matching. 162 /// 163 class StandardDirective : public Directive { 164 public: 165 StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 166 StringRef Text, unsigned Min, unsigned Max) 167 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { } 168 169 virtual bool isValid(std::string &Error) { 170 // all strings are considered valid; even empty ones 171 return true; 172 } 173 174 virtual bool match(StringRef S) { 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 StringRef Text, unsigned Min, unsigned Max, StringRef RegexStr) 185 : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(RegexStr) { } 186 187 virtual bool isValid(std::string &Error) { 188 if (Regex.isValid(Error)) 189 return true; 190 return false; 191 } 192 193 virtual bool match(StringRef S) { 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(NULL) { } 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 = NULL; 329 if (PH.Next("error")) 330 DL = ED ? &ED->Errors : NULL; 331 else if (PH.Next("warning")) 332 DL = ED ? &ED->Warnings : NULL; 333 else if (PH.Next("note")) 334 DL = ED ? &ED->Notes : NULL; 335 else if (PH.Next("no-diagnostics")) { 336 if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 337 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 338 << /*IsExpectedNoDiagnostics=*/true; 339 else 340 Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 341 continue; 342 } else 343 continue; 344 PH.Advance(); 345 346 if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 347 Diags.Report(Pos, diag::err_verify_invalid_no_diags) 348 << /*IsExpectedNoDiagnostics=*/false; 349 continue; 350 } 351 Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 352 353 // If a directive has been found but we're not interested 354 // in storing the directive information, return now. 355 if (!DL) 356 return true; 357 358 // Default directive kind. 359 bool RegexKind = false; 360 const char* KindStr = "string"; 361 362 // Next optional token: - 363 if (PH.Next("-re")) { 364 PH.Advance(); 365 RegexKind = true; 366 KindStr = "regex"; 367 } 368 369 // Next optional token: @ 370 SourceLocation ExpectedLoc; 371 if (!PH.Next("@")) { 372 ExpectedLoc = Pos; 373 } else { 374 PH.Advance(); 375 unsigned Line = 0; 376 bool FoundPlus = PH.Next("+"); 377 if (FoundPlus || PH.Next("-")) { 378 // Relative to current line. 379 PH.Advance(); 380 bool Invalid = false; 381 unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 382 if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 383 if (FoundPlus) ExpectedLine += Line; 384 else ExpectedLine -= Line; 385 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 386 } 387 } else if (PH.Next(Line)) { 388 // Absolute line number. 389 if (Line > 0) 390 ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 391 } else if (PP && PH.Search(":")) { 392 // Specific source file. 393 StringRef Filename(PH.C, PH.P-PH.C); 394 PH.Advance(); 395 396 // Lookup file via Preprocessor, like a #include. 397 const DirectoryLookup *CurDir; 398 const FileEntry *FE = PP->LookupFile(Pos, Filename, false, NULL, CurDir, 399 NULL, NULL, 0); 400 if (!FE) { 401 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 402 diag::err_verify_missing_file) << Filename << KindStr; 403 continue; 404 } 405 406 if (SM.translateFile(FE).isInvalid()) 407 SM.createFileID(FE, Pos, SrcMgr::C_User); 408 409 if (PH.Next(Line) && Line > 0) 410 ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); 411 } 412 413 if (ExpectedLoc.isInvalid()) { 414 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 415 diag::err_verify_missing_line) << KindStr; 416 continue; 417 } 418 PH.Advance(); 419 } 420 421 // Skip optional whitespace. 422 PH.SkipWhitespace(); 423 424 // Next optional token: positive integer or a '+'. 425 unsigned Min = 1; 426 unsigned Max = 1; 427 if (PH.Next(Min)) { 428 PH.Advance(); 429 // A positive integer can be followed by a '+' meaning min 430 // or more, or by a '-' meaning a range from min to max. 431 if (PH.Next("+")) { 432 Max = Directive::MaxCount; 433 PH.Advance(); 434 } else if (PH.Next("-")) { 435 PH.Advance(); 436 if (!PH.Next(Max) || Max < Min) { 437 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 438 diag::err_verify_invalid_range) << KindStr; 439 continue; 440 } 441 PH.Advance(); 442 } else { 443 Max = Min; 444 } 445 } else if (PH.Next("+")) { 446 // '+' on its own means "1 or more". 447 Max = Directive::MaxCount; 448 PH.Advance(); 449 } 450 451 // Skip optional whitespace. 452 PH.SkipWhitespace(); 453 454 // Next token: {{ 455 if (!PH.Next("{{")) { 456 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 457 diag::err_verify_missing_start) << KindStr; 458 continue; 459 } 460 PH.Advance(); 461 const char* const ContentBegin = PH.C; // mark content begin 462 463 // Search for token: }} 464 if (!PH.SearchClosingBrace("{{", "}}")) { 465 Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 466 diag::err_verify_missing_end) << KindStr; 467 continue; 468 } 469 const char* const ContentEnd = PH.P; // mark content end 470 PH.Advance(); 471 472 // Build directive text; convert \n to newlines. 473 std::string Text; 474 StringRef NewlineStr = "\\n"; 475 StringRef Content(ContentBegin, ContentEnd-ContentBegin); 476 size_t CPos = 0; 477 size_t FPos; 478 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 479 Text += Content.substr(CPos, FPos-CPos); 480 Text += '\n'; 481 CPos = FPos + NewlineStr.size(); 482 } 483 if (Text.empty()) 484 Text.assign(ContentBegin, ContentEnd); 485 486 // Check that regex directives contain at least one regex. 487 if (RegexKind && Text.find("{{") == StringRef::npos) { 488 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 489 diag::err_verify_missing_regex) << Text; 490 return false; 491 } 492 493 // Construct new directive. 494 Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, 495 Min, Max); 496 std::string Error; 497 if (D->isValid(Error)) { 498 DL->push_back(D); 499 FoundDirective = true; 500 } else { 501 Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 502 diag::err_verify_invalid_content) 503 << KindStr << Error; 504 } 505 } 506 507 return FoundDirective; 508 } 509 510 /// HandleComment - Hook into the preprocessor and extract comments containing 511 /// expected errors and warnings. 512 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 513 SourceRange Comment) { 514 SourceManager &SM = PP.getSourceManager(); 515 516 // If this comment is for a different source manager, ignore it. 517 if (SrcManager && &SM != SrcManager) 518 return false; 519 520 SourceLocation CommentBegin = Comment.getBegin(); 521 522 const char *CommentRaw = SM.getCharacterData(CommentBegin); 523 StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 524 525 if (C.empty()) 526 return false; 527 528 // Fold any "\<EOL>" sequences 529 size_t loc = C.find('\\'); 530 if (loc == StringRef::npos) { 531 ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 532 return false; 533 } 534 535 std::string C2; 536 C2.reserve(C.size()); 537 538 for (size_t last = 0;; loc = C.find('\\', last)) { 539 if (loc == StringRef::npos || loc == C.size()) { 540 C2 += C.substr(last); 541 break; 542 } 543 C2 += C.substr(last, loc-last); 544 last = loc + 1; 545 546 if (C[last] == '\n' || C[last] == '\r') { 547 ++last; 548 549 // Escape \r\n or \n\r, but not \n\n. 550 if (last < C.size()) 551 if (C[last] == '\n' || C[last] == '\r') 552 if (C[last] != C[last-1]) 553 ++last; 554 } else { 555 // This was just a normal backslash. 556 C2 += '\\'; 557 } 558 } 559 560 if (!C2.empty()) 561 ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 562 return false; 563 } 564 565 #ifndef NDEBUG 566 /// \brief Lex the specified source file to determine whether it contains 567 /// any expected-* directives. As a Lexer is used rather than a full-blown 568 /// Preprocessor, directives inside skipped #if blocks will still be found. 569 /// 570 /// \return true if any directives were found. 571 static bool findDirectives(SourceManager &SM, FileID FID, 572 const LangOptions &LangOpts) { 573 // Create a raw lexer to pull all the comments out of FID. 574 if (FID.isInvalid()) 575 return false; 576 577 // Create a lexer to lex all the tokens of the main file in raw mode. 578 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 579 Lexer RawLex(FID, FromFile, SM, LangOpts); 580 581 // Return comments as tokens, this is how we find expected diagnostics. 582 RawLex.SetCommentRetentionState(true); 583 584 Token Tok; 585 Tok.setKind(tok::comment); 586 VerifyDiagnosticConsumer::DirectiveStatus Status = 587 VerifyDiagnosticConsumer::HasNoDirectives; 588 while (Tok.isNot(tok::eof)) { 589 RawLex.LexFromRawLexer(Tok); 590 if (!Tok.is(tok::comment)) continue; 591 592 std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 593 if (Comment.empty()) continue; 594 595 // Find first directive. 596 if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status)) 597 return true; 598 } 599 return false; 600 } 601 #endif // !NDEBUG 602 603 /// \brief Takes a list of diagnostics that have been generated but not matched 604 /// by an expected-* directive and produces a diagnostic to the user from this. 605 static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 606 const_diag_iterator diag_begin, 607 const_diag_iterator diag_end, 608 const char *Kind) { 609 if (diag_begin == diag_end) return 0; 610 611 SmallString<256> Fmt; 612 llvm::raw_svector_ostream OS(Fmt); 613 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 614 if (I->first.isInvalid() || !SourceMgr) 615 OS << "\n (frontend)"; 616 else { 617 OS << "\n "; 618 if (const FileEntry *File = SourceMgr->getFileEntryForID( 619 SourceMgr->getFileID(I->first))) 620 OS << " File " << File->getName(); 621 OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 622 } 623 OS << ": " << I->second; 624 } 625 626 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 627 << Kind << /*Unexpected=*/true << OS.str(); 628 return std::distance(diag_begin, diag_end); 629 } 630 631 /// \brief Takes a list of diagnostics that were expected to have been generated 632 /// but were not and produces a diagnostic to the user from this. 633 static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 634 DirectiveList &DL, const char *Kind) { 635 if (DL.empty()) 636 return 0; 637 638 SmallString<256> Fmt; 639 llvm::raw_svector_ostream OS(Fmt); 640 for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { 641 Directive &D = **I; 642 OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc) 643 << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 644 if (D.DirectiveLoc != D.DiagnosticLoc) 645 OS << " (directive at " 646 << SourceMgr.getFilename(D.DirectiveLoc) << ':' 647 << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 648 OS << ": " << D.Text; 649 } 650 651 Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 652 << Kind << /*Unexpected=*/false << OS.str(); 653 return DL.size(); 654 } 655 656 /// \brief Determine whether two source locations come from the same file. 657 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 658 SourceLocation DiagnosticLoc) { 659 while (DiagnosticLoc.isMacroID()) 660 DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 661 662 if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 663 return true; 664 665 const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 666 if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 667 return true; 668 669 return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 670 } 671 672 /// CheckLists - Compare expected to seen diagnostic lists and return the 673 /// the difference between them. 674 /// 675 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 676 const char *Label, 677 DirectiveList &Left, 678 const_diag_iterator d2_begin, 679 const_diag_iterator d2_end) { 680 DirectiveList LeftOnly; 681 DiagList Right(d2_begin, d2_end); 682 683 for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { 684 Directive& D = **I; 685 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 686 687 for (unsigned i = 0; i < D.Max; ++i) { 688 DiagList::iterator II, IE; 689 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 690 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 691 if (LineNo1 != LineNo2) 692 continue; 693 694 if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 695 continue; 696 697 const std::string &RightText = II->second; 698 if (D.match(RightText)) 699 break; 700 } 701 if (II == IE) { 702 // Not found. 703 if (i >= D.Min) break; 704 LeftOnly.push_back(*I); 705 } else { 706 // Found. The same cannot be found twice. 707 Right.erase(II); 708 } 709 } 710 } 711 // Now all that's left in Right are those that were not matched. 712 unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 713 num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 714 return num; 715 } 716 717 /// CheckResults - This compares the expected results to those that 718 /// were actually reported. It emits any discrepencies. Return "true" if there 719 /// were problems. Return "false" otherwise. 720 /// 721 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 722 const TextDiagnosticBuffer &Buffer, 723 ExpectedData &ED) { 724 // We want to capture the delta between what was expected and what was 725 // seen. 726 // 727 // Expected \ Seen - set expected but not seen 728 // Seen \ Expected - set seen but not expected 729 unsigned NumProblems = 0; 730 731 // See if there are error mismatches. 732 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 733 Buffer.err_begin(), Buffer.err_end()); 734 735 // See if there are warning mismatches. 736 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 737 Buffer.warn_begin(), Buffer.warn_end()); 738 739 // See if there are note mismatches. 740 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 741 Buffer.note_begin(), Buffer.note_end()); 742 743 return NumProblems; 744 } 745 746 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 747 FileID FID, 748 ParsedStatus PS) { 749 // Check SourceManager hasn't changed. 750 setSourceManager(SM); 751 752 #ifndef NDEBUG 753 if (FID.isInvalid()) 754 return; 755 756 const FileEntry *FE = SM.getFileEntryForID(FID); 757 758 if (PS == IsParsed) { 759 // Move the FileID from the unparsed set to the parsed set. 760 UnparsedFiles.erase(FID); 761 ParsedFiles.insert(std::make_pair(FID, FE)); 762 } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 763 // Add the FileID to the unparsed set if we haven't seen it before. 764 765 // Check for directives. 766 bool FoundDirectives; 767 if (PS == IsUnparsedNoDirectives) 768 FoundDirectives = false; 769 else 770 FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 771 772 // Add the FileID to the unparsed set. 773 UnparsedFiles.insert(std::make_pair(FID, 774 UnparsedFileStatus(FE, FoundDirectives))); 775 } 776 #endif 777 } 778 779 void VerifyDiagnosticConsumer::CheckDiagnostics() { 780 // Ensure any diagnostics go to the primary client. 781 bool OwnsCurClient = Diags.ownsClient(); 782 DiagnosticConsumer *CurClient = Diags.takeClient(); 783 Diags.setClient(PrimaryClient, false); 784 785 #ifndef NDEBUG 786 // In a debug build, scan through any files that may have been missed 787 // during parsing and issue a fatal error if directives are contained 788 // within these files. If a fatal error occurs, this suggests that 789 // this file is being parsed separately from the main file, in which 790 // case consider moving the directives to the correct place, if this 791 // is applicable. 792 if (UnparsedFiles.size() > 0) { 793 // Generate a cache of parsed FileEntry pointers for alias lookups. 794 llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 795 for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 796 End = ParsedFiles.end(); I != End; ++I) { 797 if (const FileEntry *FE = I->second) 798 ParsedFileCache.insert(FE); 799 } 800 801 // Iterate through list of unparsed files. 802 for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 803 End = UnparsedFiles.end(); I != End; ++I) { 804 const UnparsedFileStatus &Status = I->second; 805 const FileEntry *FE = Status.getFile(); 806 807 // Skip files that have been parsed via an alias. 808 if (FE && ParsedFileCache.count(FE)) 809 continue; 810 811 // Report a fatal error if this file contained directives. 812 if (Status.foundDirectives()) { 813 llvm::report_fatal_error(Twine("-verify directives found after rather" 814 " than during normal parsing of ", 815 StringRef(FE ? FE->getName() : "(unknown)"))); 816 } 817 } 818 819 // UnparsedFiles has been processed now, so clear it. 820 UnparsedFiles.clear(); 821 } 822 #endif // !NDEBUG 823 824 if (SrcManager) { 825 // Produce an error if no expected-* directives could be found in the 826 // source file(s) processed. 827 if (Status == HasNoDirectives) { 828 Diags.Report(diag::err_verify_no_directives).setForceEmit(); 829 ++NumErrors; 830 Status = HasNoDirectivesReported; 831 } 832 833 // Check that the expected diagnostics occurred. 834 NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 835 } else { 836 NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(), 837 Buffer->err_end(), "error") + 838 PrintUnexpected(Diags, 0, Buffer->warn_begin(), 839 Buffer->warn_end(), "warn") + 840 PrintUnexpected(Diags, 0, Buffer->note_begin(), 841 Buffer->note_end(), "note")); 842 } 843 844 Diags.takeClient(); 845 Diags.setClient(CurClient, OwnsCurClient); 846 847 // Reset the buffer, we have processed all the diagnostics in it. 848 Buffer.reset(new TextDiagnosticBuffer()); 849 ED.Errors.clear(); 850 ED.Warnings.clear(); 851 ED.Notes.clear(); 852 } 853 854 Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, 855 SourceLocation DiagnosticLoc, StringRef Text, 856 unsigned Min, unsigned Max) { 857 if (!RegexKind) 858 return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); 859 860 // Parse the directive into a regular expression. 861 std::string RegexStr; 862 StringRef S = Text; 863 while (!S.empty()) { 864 if (S.startswith("{{")) { 865 S = S.drop_front(2); 866 size_t RegexMatchLength = S.find("}}"); 867 assert(RegexMatchLength != StringRef::npos); 868 // Append the regex, enclosed in parentheses. 869 RegexStr += "("; 870 RegexStr.append(S.data(), RegexMatchLength); 871 RegexStr += ")"; 872 S = S.drop_front(RegexMatchLength + 2); 873 } else { 874 size_t VerbatimMatchLength = S.find("{{"); 875 if (VerbatimMatchLength == StringRef::npos) 876 VerbatimMatchLength = S.size(); 877 // Escape and append the fixed string. 878 RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); 879 S = S.drop_front(VerbatimMatchLength); 880 } 881 } 882 883 return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max, RegexStr); 884 } 885