1226586Sdim//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===// 2226586Sdim// 3226586Sdim// The LLVM Compiler Infrastructure 4226586Sdim// 5226586Sdim// This file is distributed under the University of Illinois Open Source 6226586Sdim// License. See LICENSE.TXT for details. 7226586Sdim// 8226586Sdim//===----------------------------------------------------------------------===// 9226586Sdim// 10226586Sdim// This is a concrete diagnostic client, which buffers the diagnostic messages. 11226586Sdim// 12226586Sdim//===----------------------------------------------------------------------===// 13226586Sdim 14249423Sdim#include "clang/Frontend/VerifyDiagnosticConsumer.h" 15249423Sdim#include "clang/Basic/CharInfo.h" 16239462Sdim#include "clang/Basic/FileManager.h" 17226586Sdim#include "clang/Frontend/FrontendDiagnostic.h" 18226586Sdim#include "clang/Frontend/TextDiagnosticBuffer.h" 19239462Sdim#include "clang/Lex/HeaderSearch.h" 20226586Sdim#include "clang/Lex/Preprocessor.h" 21226586Sdim#include "llvm/ADT/SmallString.h" 22226586Sdim#include "llvm/Support/Regex.h" 23226586Sdim#include "llvm/Support/raw_ostream.h" 24234353Sdim 25226586Sdimusing namespace clang; 26239462Sdimtypedef VerifyDiagnosticConsumer::Directive Directive; 27239462Sdimtypedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; 28239462Sdimtypedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; 29226586Sdim 30226586SdimVerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags) 31239462Sdim : Diags(_Diags), 32239462Sdim PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()), 33239462Sdim Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), 34243830Sdim LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives) 35226586Sdim{ 36226586Sdim Diags.takeClient(); 37243830Sdim if (Diags.hasSourceManager()) 38243830Sdim setSourceManager(Diags.getSourceManager()); 39226586Sdim} 40226586Sdim 41226586SdimVerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { 42239462Sdim assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); 43239462Sdim assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); 44243830Sdim SrcManager = 0; 45226586Sdim CheckDiagnostics(); 46226586Sdim Diags.takeClient(); 47226586Sdim if (OwnsPrimaryClient) 48226586Sdim delete PrimaryClient; 49226586Sdim} 50226586Sdim 51239462Sdim#ifndef NDEBUG 52239462Sdimnamespace { 53239462Sdimclass VerifyFileTracker : public PPCallbacks { 54243830Sdim VerifyDiagnosticConsumer &Verify; 55239462Sdim SourceManager &SM; 56239462Sdim 57239462Sdimpublic: 58243830Sdim VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) 59243830Sdim : Verify(Verify), SM(SM) { } 60239462Sdim 61239462Sdim /// \brief Hook into the preprocessor and update the list of parsed 62239462Sdim /// files when the preprocessor indicates a new file is entered. 63239462Sdim virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, 64239462Sdim SrcMgr::CharacteristicKind FileType, 65239462Sdim FileID PrevFID) { 66243830Sdim Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), 67243830Sdim VerifyDiagnosticConsumer::IsParsed); 68239462Sdim } 69239462Sdim}; 70239462Sdim} // End anonymous namespace. 71239462Sdim#endif 72239462Sdim 73226586Sdim// DiagnosticConsumer interface. 74226586Sdim 75226586Sdimvoid VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 76234353Sdim const Preprocessor *PP) { 77239462Sdim // Attach comment handler on first invocation. 78239462Sdim if (++ActiveSourceFiles == 1) { 79239462Sdim if (PP) { 80239462Sdim CurrentPreprocessor = PP; 81243830Sdim this->LangOpts = &LangOpts; 82243830Sdim setSourceManager(PP->getSourceManager()); 83239462Sdim const_cast<Preprocessor*>(PP)->addCommentHandler(this); 84239462Sdim#ifndef NDEBUG 85243830Sdim // Debug build tracks parsed files. 86243830Sdim VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager); 87239462Sdim const_cast<Preprocessor*>(PP)->addPPCallbacks(V); 88239462Sdim#endif 89239462Sdim } 90239462Sdim } 91226586Sdim 92239462Sdim assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); 93226586Sdim PrimaryClient->BeginSourceFile(LangOpts, PP); 94226586Sdim} 95226586Sdim 96226586Sdimvoid VerifyDiagnosticConsumer::EndSourceFile() { 97239462Sdim assert(ActiveSourceFiles && "No active source files!"); 98226586Sdim PrimaryClient->EndSourceFile(); 99226586Sdim 100239462Sdim // Detach comment handler once last active source file completed. 101239462Sdim if (--ActiveSourceFiles == 0) { 102239462Sdim if (CurrentPreprocessor) 103239462Sdim const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); 104239462Sdim 105239462Sdim // Check diagnostics once last file completed. 106239462Sdim CheckDiagnostics(); 107239462Sdim CurrentPreprocessor = 0; 108243830Sdim LangOpts = 0; 109239462Sdim } 110226586Sdim} 111226586Sdim 112226586Sdimvoid VerifyDiagnosticConsumer::HandleDiagnostic( 113226586Sdim DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 114251662Sdim if (Info.hasSourceManager()) { 115251662Sdim // If this diagnostic is for a different source manager, ignore it. 116251662Sdim if (SrcManager && &Info.getSourceManager() != SrcManager) 117251662Sdim return; 118251662Sdim 119243830Sdim setSourceManager(Info.getSourceManager()); 120251662Sdim } 121243830Sdim 122239462Sdim#ifndef NDEBUG 123243830Sdim // Debug build tracks unparsed files for possible 124243830Sdim // unparsed expected-* directives. 125243830Sdim if (SrcManager) { 126243830Sdim SourceLocation Loc = Info.getLocation(); 127243830Sdim if (Loc.isValid()) { 128243830Sdim ParsedStatus PS = IsUnparsed; 129243830Sdim 130243830Sdim Loc = SrcManager->getExpansionLoc(Loc); 131243830Sdim FileID FID = SrcManager->getFileID(Loc); 132243830Sdim 133243830Sdim const FileEntry *FE = SrcManager->getFileEntryForID(FID); 134243830Sdim if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { 135243830Sdim // If the file is a modules header file it shall not be parsed 136243830Sdim // for expected-* directives. 137243830Sdim HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); 138243830Sdim if (HS.findModuleForHeader(FE)) 139243830Sdim PS = IsUnparsedNoDirectives; 140243830Sdim } 141243830Sdim 142243830Sdim UpdateParsedFileStatus(*SrcManager, FID, PS); 143243830Sdim } 144226586Sdim } 145239462Sdim#endif 146243830Sdim 147226586Sdim // Send the diagnostic to the buffer, we will check it once we reach the end 148226586Sdim // of the source file (or are destructed). 149226586Sdim Buffer->HandleDiagnostic(DiagLevel, Info); 150226586Sdim} 151226586Sdim 152226586Sdim//===----------------------------------------------------------------------===// 153226586Sdim// Checking diagnostics implementation. 154226586Sdim//===----------------------------------------------------------------------===// 155226586Sdim 156226586Sdimtypedef TextDiagnosticBuffer::DiagList DiagList; 157226586Sdimtypedef TextDiagnosticBuffer::const_iterator const_diag_iterator; 158226586Sdim 159226586Sdimnamespace { 160226586Sdim 161226586Sdim/// StandardDirective - Directive with string matching. 162226586Sdim/// 163226586Sdimclass StandardDirective : public Directive { 164226586Sdimpublic: 165239462Sdim StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 166239462Sdim StringRef Text, unsigned Min, unsigned Max) 167239462Sdim : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { } 168226586Sdim 169226586Sdim virtual bool isValid(std::string &Error) { 170226586Sdim // all strings are considered valid; even empty ones 171226586Sdim return true; 172226586Sdim } 173226586Sdim 174239462Sdim virtual bool match(StringRef S) { 175239462Sdim return S.find(Text) != StringRef::npos; 176226586Sdim } 177226586Sdim}; 178226586Sdim 179226586Sdim/// RegexDirective - Directive with regular-expression matching. 180226586Sdim/// 181226586Sdimclass RegexDirective : public Directive { 182226586Sdimpublic: 183239462Sdim RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 184239462Sdim StringRef Text, unsigned Min, unsigned Max) 185239462Sdim : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { } 186226586Sdim 187226586Sdim virtual bool isValid(std::string &Error) { 188226586Sdim if (Regex.isValid(Error)) 189226586Sdim return true; 190226586Sdim return false; 191226586Sdim } 192226586Sdim 193239462Sdim virtual bool match(StringRef S) { 194226586Sdim return Regex.match(S); 195226586Sdim } 196226586Sdim 197226586Sdimprivate: 198226586Sdim llvm::Regex Regex; 199226586Sdim}; 200226586Sdim 201226586Sdimclass ParseHelper 202226586Sdim{ 203226586Sdimpublic: 204239462Sdim ParseHelper(StringRef S) 205239462Sdim : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { } 206226586Sdim 207226586Sdim // Return true if string literal is next. 208226586Sdim bool Next(StringRef S) { 209226586Sdim P = C; 210226586Sdim PEnd = C + S.size(); 211226586Sdim if (PEnd > End) 212226586Sdim return false; 213226586Sdim return !memcmp(P, S.data(), S.size()); 214226586Sdim } 215226586Sdim 216226586Sdim // Return true if number is next. 217226586Sdim // Output N only if number is next. 218226586Sdim bool Next(unsigned &N) { 219226586Sdim unsigned TMP = 0; 220226586Sdim P = C; 221226586Sdim for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { 222226586Sdim TMP *= 10; 223226586Sdim TMP += P[0] - '0'; 224226586Sdim } 225226586Sdim if (P == C) 226226586Sdim return false; 227226586Sdim PEnd = P; 228226586Sdim N = TMP; 229226586Sdim return true; 230226586Sdim } 231226586Sdim 232226586Sdim // Return true if string literal is found. 233226586Sdim // When true, P marks begin-position of S in content. 234243830Sdim bool Search(StringRef S, bool EnsureStartOfWord = false) { 235243830Sdim do { 236243830Sdim P = std::search(C, End, S.begin(), S.end()); 237243830Sdim PEnd = P + S.size(); 238243830Sdim if (P == End) 239243830Sdim break; 240243830Sdim if (!EnsureStartOfWord 241243830Sdim // Check if string literal starts a new word. 242249423Sdim || P == Begin || isWhitespace(P[-1]) 243243830Sdim // Or it could be preceeded by the start of a comment. 244243830Sdim || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') 245243830Sdim && P[-2] == '/')) 246243830Sdim return true; 247243830Sdim // Otherwise, skip and search again. 248243830Sdim } while (Advance()); 249243830Sdim return false; 250226586Sdim } 251226586Sdim 252226586Sdim // Advance 1-past previous next/search. 253226586Sdim // Behavior is undefined if previous next/search failed. 254226586Sdim bool Advance() { 255226586Sdim C = PEnd; 256226586Sdim return C < End; 257226586Sdim } 258226586Sdim 259226586Sdim // Skip zero or more whitespace. 260226586Sdim void SkipWhitespace() { 261249423Sdim for (; C < End && isWhitespace(*C); ++C) 262226586Sdim ; 263226586Sdim } 264226586Sdim 265226586Sdim // Return true if EOF reached. 266226586Sdim bool Done() { 267226586Sdim return !(C < End); 268226586Sdim } 269226586Sdim 270226586Sdim const char * const Begin; // beginning of expected content 271226586Sdim const char * const End; // end of expected content (1-past) 272226586Sdim const char *C; // position of next char in content 273226586Sdim const char *P; 274226586Sdim 275226586Sdimprivate: 276226586Sdim const char *PEnd; // previous next/search subject end (1-past) 277226586Sdim}; 278226586Sdim 279226586Sdim} // namespace anonymous 280226586Sdim 281226586Sdim/// ParseDirective - Go through the comment and see if it indicates expected 282226586Sdim/// diagnostics. If so, then put them in the appropriate directive list. 283226586Sdim/// 284239462Sdim/// Returns true if any valid directives were found. 285239462Sdimstatic bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, 286251662Sdim Preprocessor *PP, SourceLocation Pos, 287243830Sdim VerifyDiagnosticConsumer::DirectiveStatus &Status) { 288251662Sdim DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); 289251662Sdim 290226586Sdim // A single comment may contain multiple directives. 291239462Sdim bool FoundDirective = false; 292239462Sdim for (ParseHelper PH(S); !PH.Done();) { 293239462Sdim // Search for token: expected 294243830Sdim if (!PH.Search("expected", true)) 295226586Sdim break; 296226586Sdim PH.Advance(); 297226586Sdim 298239462Sdim // Next token: - 299226586Sdim if (!PH.Next("-")) 300226586Sdim continue; 301226586Sdim PH.Advance(); 302226586Sdim 303239462Sdim // Next token: { error | warning | note } 304226586Sdim DirectiveList* DL = NULL; 305226586Sdim if (PH.Next("error")) 306239462Sdim DL = ED ? &ED->Errors : NULL; 307226586Sdim else if (PH.Next("warning")) 308239462Sdim DL = ED ? &ED->Warnings : NULL; 309226586Sdim else if (PH.Next("note")) 310239462Sdim DL = ED ? &ED->Notes : NULL; 311243830Sdim else if (PH.Next("no-diagnostics")) { 312243830Sdim if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) 313243830Sdim Diags.Report(Pos, diag::err_verify_invalid_no_diags) 314243830Sdim << /*IsExpectedNoDiagnostics=*/true; 315243830Sdim else 316243830Sdim Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; 317226586Sdim continue; 318243830Sdim } else 319243830Sdim continue; 320226586Sdim PH.Advance(); 321226586Sdim 322243830Sdim if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { 323243830Sdim Diags.Report(Pos, diag::err_verify_invalid_no_diags) 324243830Sdim << /*IsExpectedNoDiagnostics=*/false; 325243830Sdim continue; 326243830Sdim } 327243830Sdim Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; 328243830Sdim 329239462Sdim // If a directive has been found but we're not interested 330239462Sdim // in storing the directive information, return now. 331239462Sdim if (!DL) 332239462Sdim return true; 333239462Sdim 334239462Sdim // Default directive kind. 335226586Sdim bool RegexKind = false; 336226586Sdim const char* KindStr = "string"; 337226586Sdim 338239462Sdim // Next optional token: - 339226586Sdim if (PH.Next("-re")) { 340226586Sdim PH.Advance(); 341226586Sdim RegexKind = true; 342226586Sdim KindStr = "regex"; 343226586Sdim } 344226586Sdim 345239462Sdim // Next optional token: @ 346239462Sdim SourceLocation ExpectedLoc; 347239462Sdim if (!PH.Next("@")) { 348239462Sdim ExpectedLoc = Pos; 349239462Sdim } else { 350239462Sdim PH.Advance(); 351239462Sdim unsigned Line = 0; 352239462Sdim bool FoundPlus = PH.Next("+"); 353239462Sdim if (FoundPlus || PH.Next("-")) { 354239462Sdim // Relative to current line. 355239462Sdim PH.Advance(); 356239462Sdim bool Invalid = false; 357239462Sdim unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); 358239462Sdim if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { 359239462Sdim if (FoundPlus) ExpectedLine += Line; 360239462Sdim else ExpectedLine -= Line; 361239462Sdim ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); 362239462Sdim } 363251662Sdim } else if (PH.Next(Line)) { 364239462Sdim // Absolute line number. 365251662Sdim if (Line > 0) 366251662Sdim ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); 367251662Sdim } else if (PP && PH.Search(":")) { 368251662Sdim // Specific source file. 369251662Sdim StringRef Filename(PH.C, PH.P-PH.C); 370251662Sdim PH.Advance(); 371251662Sdim 372251662Sdim // Lookup file via Preprocessor, like a #include. 373251662Sdim const DirectoryLookup *CurDir; 374263508Sdim const FileEntry *FE = PP->LookupFile(Pos, Filename, false, NULL, CurDir, 375251662Sdim NULL, NULL, 0); 376251662Sdim if (!FE) { 377251662Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 378251662Sdim diag::err_verify_missing_file) << Filename << KindStr; 379251662Sdim continue; 380251662Sdim } 381251662Sdim 382251662Sdim if (SM.translateFile(FE).isInvalid()) 383251662Sdim SM.createFileID(FE, Pos, SrcMgr::C_User); 384251662Sdim 385239462Sdim if (PH.Next(Line) && Line > 0) 386251662Sdim ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); 387239462Sdim } 388239462Sdim 389239462Sdim if (ExpectedLoc.isInvalid()) { 390239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 391239462Sdim diag::err_verify_missing_line) << KindStr; 392239462Sdim continue; 393239462Sdim } 394239462Sdim PH.Advance(); 395239462Sdim } 396239462Sdim 397239462Sdim // Skip optional whitespace. 398226586Sdim PH.SkipWhitespace(); 399226586Sdim 400239462Sdim // Next optional token: positive integer or a '+'. 401239462Sdim unsigned Min = 1; 402239462Sdim unsigned Max = 1; 403239462Sdim if (PH.Next(Min)) { 404226586Sdim PH.Advance(); 405239462Sdim // A positive integer can be followed by a '+' meaning min 406239462Sdim // or more, or by a '-' meaning a range from min to max. 407239462Sdim if (PH.Next("+")) { 408239462Sdim Max = Directive::MaxCount; 409239462Sdim PH.Advance(); 410239462Sdim } else if (PH.Next("-")) { 411239462Sdim PH.Advance(); 412239462Sdim if (!PH.Next(Max) || Max < Min) { 413239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 414239462Sdim diag::err_verify_invalid_range) << KindStr; 415239462Sdim continue; 416239462Sdim } 417239462Sdim PH.Advance(); 418239462Sdim } else { 419239462Sdim Max = Min; 420239462Sdim } 421239462Sdim } else if (PH.Next("+")) { 422239462Sdim // '+' on its own means "1 or more". 423239462Sdim Max = Directive::MaxCount; 424234353Sdim PH.Advance(); 425234353Sdim } 426226586Sdim 427239462Sdim // Skip optional whitespace. 428226586Sdim PH.SkipWhitespace(); 429226586Sdim 430239462Sdim // Next token: {{ 431226586Sdim if (!PH.Next("{{")) { 432239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 433239462Sdim diag::err_verify_missing_start) << KindStr; 434226586Sdim continue; 435226586Sdim } 436226586Sdim PH.Advance(); 437226586Sdim const char* const ContentBegin = PH.C; // mark content begin 438226586Sdim 439239462Sdim // Search for token: }} 440226586Sdim if (!PH.Search("}}")) { 441239462Sdim Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), 442239462Sdim diag::err_verify_missing_end) << KindStr; 443226586Sdim continue; 444226586Sdim } 445226586Sdim const char* const ContentEnd = PH.P; // mark content end 446226586Sdim PH.Advance(); 447226586Sdim 448239462Sdim // Build directive text; convert \n to newlines. 449226586Sdim std::string Text; 450226586Sdim StringRef NewlineStr = "\\n"; 451226586Sdim StringRef Content(ContentBegin, ContentEnd-ContentBegin); 452226586Sdim size_t CPos = 0; 453226586Sdim size_t FPos; 454226586Sdim while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 455226586Sdim Text += Content.substr(CPos, FPos-CPos); 456226586Sdim Text += '\n'; 457226586Sdim CPos = FPos + NewlineStr.size(); 458226586Sdim } 459226586Sdim if (Text.empty()) 460226586Sdim Text.assign(ContentBegin, ContentEnd); 461226586Sdim 462239462Sdim // Construct new directive. 463239462Sdim Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, 464239462Sdim Min, Max); 465226586Sdim std::string Error; 466239462Sdim if (D->isValid(Error)) { 467226586Sdim DL->push_back(D); 468239462Sdim FoundDirective = true; 469239462Sdim } else { 470239462Sdim Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), 471239462Sdim diag::err_verify_invalid_content) 472226586Sdim << KindStr << Error; 473226586Sdim } 474226586Sdim } 475239462Sdim 476239462Sdim return FoundDirective; 477226586Sdim} 478226586Sdim 479239462Sdim/// HandleComment - Hook into the preprocessor and extract comments containing 480239462Sdim/// expected errors and warnings. 481239462Sdimbool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, 482239462Sdim SourceRange Comment) { 483239462Sdim SourceManager &SM = PP.getSourceManager(); 484251662Sdim 485251662Sdim // If this comment is for a different source manager, ignore it. 486251662Sdim if (SrcManager && &SM != SrcManager) 487251662Sdim return false; 488251662Sdim 489239462Sdim SourceLocation CommentBegin = Comment.getBegin(); 490239462Sdim 491239462Sdim const char *CommentRaw = SM.getCharacterData(CommentBegin); 492239462Sdim StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); 493239462Sdim 494239462Sdim if (C.empty()) 495239462Sdim return false; 496239462Sdim 497239462Sdim // Fold any "\<EOL>" sequences 498239462Sdim size_t loc = C.find('\\'); 499239462Sdim if (loc == StringRef::npos) { 500251662Sdim ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); 501239462Sdim return false; 502239462Sdim } 503239462Sdim 504239462Sdim std::string C2; 505239462Sdim C2.reserve(C.size()); 506239462Sdim 507239462Sdim for (size_t last = 0;; loc = C.find('\\', last)) { 508239462Sdim if (loc == StringRef::npos || loc == C.size()) { 509239462Sdim C2 += C.substr(last); 510239462Sdim break; 511239462Sdim } 512239462Sdim C2 += C.substr(last, loc-last); 513239462Sdim last = loc + 1; 514239462Sdim 515239462Sdim if (C[last] == '\n' || C[last] == '\r') { 516239462Sdim ++last; 517239462Sdim 518239462Sdim // Escape \r\n or \n\r, but not \n\n. 519239462Sdim if (last < C.size()) 520239462Sdim if (C[last] == '\n' || C[last] == '\r') 521239462Sdim if (C[last] != C[last-1]) 522239462Sdim ++last; 523239462Sdim } else { 524239462Sdim // This was just a normal backslash. 525239462Sdim C2 += '\\'; 526239462Sdim } 527239462Sdim } 528239462Sdim 529239462Sdim if (!C2.empty()) 530251662Sdim ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); 531239462Sdim return false; 532239462Sdim} 533239462Sdim 534239462Sdim#ifndef NDEBUG 535239462Sdim/// \brief Lex the specified source file to determine whether it contains 536239462Sdim/// any expected-* directives. As a Lexer is used rather than a full-blown 537239462Sdim/// Preprocessor, directives inside skipped #if blocks will still be found. 538239462Sdim/// 539239462Sdim/// \return true if any directives were found. 540243830Sdimstatic bool findDirectives(SourceManager &SM, FileID FID, 541243830Sdim const LangOptions &LangOpts) { 542226586Sdim // Create a raw lexer to pull all the comments out of FID. 543226586Sdim if (FID.isInvalid()) 544239462Sdim return false; 545226586Sdim 546226586Sdim // Create a lexer to lex all the tokens of the main file in raw mode. 547226586Sdim const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 548243830Sdim Lexer RawLex(FID, FromFile, SM, LangOpts); 549226586Sdim 550226586Sdim // Return comments as tokens, this is how we find expected diagnostics. 551226586Sdim RawLex.SetCommentRetentionState(true); 552226586Sdim 553226586Sdim Token Tok; 554226586Sdim Tok.setKind(tok::comment); 555243830Sdim VerifyDiagnosticConsumer::DirectiveStatus Status = 556243830Sdim VerifyDiagnosticConsumer::HasNoDirectives; 557226586Sdim while (Tok.isNot(tok::eof)) { 558263508Sdim RawLex.LexFromRawLexer(Tok); 559226586Sdim if (!Tok.is(tok::comment)) continue; 560226586Sdim 561243830Sdim std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); 562226586Sdim if (Comment.empty()) continue; 563226586Sdim 564243830Sdim // Find first directive. 565251662Sdim if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status)) 566243830Sdim return true; 567239462Sdim } 568243830Sdim return false; 569226586Sdim} 570239462Sdim#endif // !NDEBUG 571226586Sdim 572239462Sdim/// \brief Takes a list of diagnostics that have been generated but not matched 573239462Sdim/// by an expected-* directive and produces a diagnostic to the user from this. 574239462Sdimstatic unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 575239462Sdim const_diag_iterator diag_begin, 576239462Sdim const_diag_iterator diag_end, 577239462Sdim const char *Kind) { 578226586Sdim if (diag_begin == diag_end) return 0; 579226586Sdim 580234353Sdim SmallString<256> Fmt; 581226586Sdim llvm::raw_svector_ostream OS(Fmt); 582226586Sdim for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 583226586Sdim if (I->first.isInvalid() || !SourceMgr) 584226586Sdim OS << "\n (frontend)"; 585251662Sdim else { 586251662Sdim OS << "\n "; 587251662Sdim if (const FileEntry *File = SourceMgr->getFileEntryForID( 588251662Sdim SourceMgr->getFileID(I->first))) 589251662Sdim OS << " File " << File->getName(); 590251662Sdim OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); 591251662Sdim } 592226586Sdim OS << ": " << I->second; 593226586Sdim } 594226586Sdim 595239462Sdim Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 596239462Sdim << Kind << /*Unexpected=*/true << OS.str(); 597226586Sdim return std::distance(diag_begin, diag_end); 598226586Sdim} 599226586Sdim 600239462Sdim/// \brief Takes a list of diagnostics that were expected to have been generated 601239462Sdim/// but were not and produces a diagnostic to the user from this. 602239462Sdimstatic unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 603239462Sdim DirectiveList &DL, const char *Kind) { 604226586Sdim if (DL.empty()) 605226586Sdim return 0; 606226586Sdim 607234353Sdim SmallString<256> Fmt; 608226586Sdim llvm::raw_svector_ostream OS(Fmt); 609226586Sdim for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { 610239462Sdim Directive &D = **I; 611251662Sdim OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc) 612251662Sdim << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 613239462Sdim if (D.DirectiveLoc != D.DiagnosticLoc) 614239462Sdim OS << " (directive at " 615251662Sdim << SourceMgr.getFilename(D.DirectiveLoc) << ':' 616251662Sdim << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; 617226586Sdim OS << ": " << D.Text; 618226586Sdim } 619226586Sdim 620239462Sdim Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() 621239462Sdim << Kind << /*Unexpected=*/false << OS.str(); 622226586Sdim return DL.size(); 623226586Sdim} 624226586Sdim 625251662Sdim/// \brief Determine whether two source locations come from the same file. 626251662Sdimstatic bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, 627251662Sdim SourceLocation DiagnosticLoc) { 628251662Sdim while (DiagnosticLoc.isMacroID()) 629251662Sdim DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); 630251662Sdim 631263508Sdim if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) 632251662Sdim return true; 633251662Sdim 634251662Sdim const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); 635263508Sdim if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) 636251662Sdim return true; 637251662Sdim 638251662Sdim return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); 639251662Sdim} 640251662Sdim 641226586Sdim/// CheckLists - Compare expected to seen diagnostic lists and return the 642226586Sdim/// the difference between them. 643226586Sdim/// 644226586Sdimstatic unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 645226586Sdim const char *Label, 646226586Sdim DirectiveList &Left, 647226586Sdim const_diag_iterator d2_begin, 648226586Sdim const_diag_iterator d2_end) { 649226586Sdim DirectiveList LeftOnly; 650226586Sdim DiagList Right(d2_begin, d2_end); 651226586Sdim 652226586Sdim for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { 653226586Sdim Directive& D = **I; 654239462Sdim unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); 655226586Sdim 656239462Sdim for (unsigned i = 0; i < D.Max; ++i) { 657226586Sdim DiagList::iterator II, IE; 658226586Sdim for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 659226586Sdim unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 660226586Sdim if (LineNo1 != LineNo2) 661226586Sdim continue; 662226586Sdim 663251662Sdim if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) 664251662Sdim continue; 665251662Sdim 666226586Sdim const std::string &RightText = II->second; 667239462Sdim if (D.match(RightText)) 668226586Sdim break; 669226586Sdim } 670226586Sdim if (II == IE) { 671226586Sdim // Not found. 672239462Sdim if (i >= D.Min) break; 673226586Sdim LeftOnly.push_back(*I); 674226586Sdim } else { 675226586Sdim // Found. The same cannot be found twice. 676226586Sdim Right.erase(II); 677226586Sdim } 678226586Sdim } 679226586Sdim } 680226586Sdim // Now all that's left in Right are those that were not matched. 681239462Sdim unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); 682239462Sdim num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); 683234353Sdim return num; 684226586Sdim} 685226586Sdim 686226586Sdim/// CheckResults - This compares the expected results to those that 687226586Sdim/// were actually reported. It emits any discrepencies. Return "true" if there 688226586Sdim/// were problems. Return "false" otherwise. 689226586Sdim/// 690226586Sdimstatic unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 691226586Sdim const TextDiagnosticBuffer &Buffer, 692226586Sdim ExpectedData &ED) { 693226586Sdim // We want to capture the delta between what was expected and what was 694226586Sdim // seen. 695226586Sdim // 696226586Sdim // Expected \ Seen - set expected but not seen 697226586Sdim // Seen \ Expected - set seen but not expected 698226586Sdim unsigned NumProblems = 0; 699226586Sdim 700226586Sdim // See if there are error mismatches. 701226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 702226586Sdim Buffer.err_begin(), Buffer.err_end()); 703226586Sdim 704226586Sdim // See if there are warning mismatches. 705226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 706226586Sdim Buffer.warn_begin(), Buffer.warn_end()); 707226586Sdim 708226586Sdim // See if there are note mismatches. 709226586Sdim NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 710226586Sdim Buffer.note_begin(), Buffer.note_end()); 711226586Sdim 712226586Sdim return NumProblems; 713226586Sdim} 714226586Sdim 715243830Sdimvoid VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, 716243830Sdim FileID FID, 717243830Sdim ParsedStatus PS) { 718243830Sdim // Check SourceManager hasn't changed. 719243830Sdim setSourceManager(SM); 720243830Sdim 721243830Sdim#ifndef NDEBUG 722243830Sdim if (FID.isInvalid()) 723243830Sdim return; 724243830Sdim 725243830Sdim const FileEntry *FE = SM.getFileEntryForID(FID); 726243830Sdim 727243830Sdim if (PS == IsParsed) { 728243830Sdim // Move the FileID from the unparsed set to the parsed set. 729243830Sdim UnparsedFiles.erase(FID); 730243830Sdim ParsedFiles.insert(std::make_pair(FID, FE)); 731243830Sdim } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { 732243830Sdim // Add the FileID to the unparsed set if we haven't seen it before. 733243830Sdim 734243830Sdim // Check for directives. 735243830Sdim bool FoundDirectives; 736243830Sdim if (PS == IsUnparsedNoDirectives) 737243830Sdim FoundDirectives = false; 738243830Sdim else 739243830Sdim FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); 740243830Sdim 741243830Sdim // Add the FileID to the unparsed set. 742243830Sdim UnparsedFiles.insert(std::make_pair(FID, 743243830Sdim UnparsedFileStatus(FE, FoundDirectives))); 744243830Sdim } 745243830Sdim#endif 746243830Sdim} 747243830Sdim 748226586Sdimvoid VerifyDiagnosticConsumer::CheckDiagnostics() { 749226586Sdim // Ensure any diagnostics go to the primary client. 750226586Sdim bool OwnsCurClient = Diags.ownsClient(); 751226586Sdim DiagnosticConsumer *CurClient = Diags.takeClient(); 752226586Sdim Diags.setClient(PrimaryClient, false); 753226586Sdim 754243830Sdim#ifndef NDEBUG 755243830Sdim // In a debug build, scan through any files that may have been missed 756243830Sdim // during parsing and issue a fatal error if directives are contained 757243830Sdim // within these files. If a fatal error occurs, this suggests that 758243830Sdim // this file is being parsed separately from the main file, in which 759243830Sdim // case consider moving the directives to the correct place, if this 760243830Sdim // is applicable. 761243830Sdim if (UnparsedFiles.size() > 0) { 762243830Sdim // Generate a cache of parsed FileEntry pointers for alias lookups. 763243830Sdim llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; 764243830Sdim for (ParsedFilesMap::iterator I = ParsedFiles.begin(), 765243830Sdim End = ParsedFiles.end(); I != End; ++I) { 766243830Sdim if (const FileEntry *FE = I->second) 767243830Sdim ParsedFileCache.insert(FE); 768243830Sdim } 769239462Sdim 770243830Sdim // Iterate through list of unparsed files. 771243830Sdim for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), 772243830Sdim End = UnparsedFiles.end(); I != End; ++I) { 773243830Sdim const UnparsedFileStatus &Status = I->second; 774243830Sdim const FileEntry *FE = Status.getFile(); 775243830Sdim 776243830Sdim // Skip files that have been parsed via an alias. 777243830Sdim if (FE && ParsedFileCache.count(FE)) 778239462Sdim continue; 779239462Sdim 780243830Sdim // Report a fatal error if this file contained directives. 781243830Sdim if (Status.foundDirectives()) { 782239462Sdim llvm::report_fatal_error(Twine("-verify directives found after rather" 783239462Sdim " than during normal parsing of ", 784243830Sdim StringRef(FE ? FE->getName() : "(unknown)"))); 785243830Sdim } 786226586Sdim } 787226586Sdim 788243830Sdim // UnparsedFiles has been processed now, so clear it. 789243830Sdim UnparsedFiles.clear(); 790243830Sdim } 791243830Sdim#endif // !NDEBUG 792243830Sdim 793243830Sdim if (SrcManager) { 794243830Sdim // Produce an error if no expected-* directives could be found in the 795243830Sdim // source file(s) processed. 796243830Sdim if (Status == HasNoDirectives) { 797243830Sdim Diags.Report(diag::err_verify_no_directives).setForceEmit(); 798243830Sdim ++NumErrors; 799243830Sdim Status = HasNoDirectivesReported; 800243830Sdim } 801243830Sdim 802226586Sdim // Check that the expected diagnostics occurred. 803243830Sdim NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); 804226586Sdim } else { 805239462Sdim NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(), 806239462Sdim Buffer->err_end(), "error") + 807239462Sdim PrintUnexpected(Diags, 0, Buffer->warn_begin(), 808239462Sdim Buffer->warn_end(), "warn") + 809239462Sdim PrintUnexpected(Diags, 0, Buffer->note_begin(), 810239462Sdim Buffer->note_end(), "note")); 811226586Sdim } 812226586Sdim 813226586Sdim Diags.takeClient(); 814226586Sdim Diags.setClient(CurClient, OwnsCurClient); 815226586Sdim 816226586Sdim // Reset the buffer, we have processed all the diagnostics in it. 817226586Sdim Buffer.reset(new TextDiagnosticBuffer()); 818239462Sdim ED.Errors.clear(); 819239462Sdim ED.Warnings.clear(); 820239462Sdim ED.Notes.clear(); 821226586Sdim} 822226586Sdim 823239462SdimDirective *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, 824239462Sdim SourceLocation DiagnosticLoc, StringRef Text, 825239462Sdim unsigned Min, unsigned Max) { 826226586Sdim if (RegexKind) 827239462Sdim return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); 828239462Sdim return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); 829226586Sdim} 830