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/Frontend/FrontendDiagnostic.h" 16 #include "clang/Frontend/TextDiagnosticBuffer.h" 17 #include "clang/Lex/Preprocessor.h" 18 #include "llvm/ADT/SmallString.h" 19 #include "llvm/Support/Regex.h" 20 #include "llvm/Support/raw_ostream.h" 21 using namespace clang; 22 23 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags) 24 : Diags(_Diags), PrimaryClient(Diags.getClient()), 25 OwnsPrimaryClient(Diags.ownsClient()), 26 Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0) 27 { 28 Diags.takeClient(); 29 } 30 31 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { 32 CheckDiagnostics(); 33 Diags.takeClient(); 34 if (OwnsPrimaryClient) 35 delete PrimaryClient; 36 } 37 38 // DiagnosticConsumer interface. 39 40 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, 41 const Preprocessor *PP) { 42 // FIXME: Const hack, we screw up the preprocessor but in practice its ok 43 // because it doesn't get reused. It would be better if we could make a copy 44 // though. 45 CurrentPreprocessor = const_cast<Preprocessor*>(PP); 46 47 PrimaryClient->BeginSourceFile(LangOpts, PP); 48 } 49 50 void VerifyDiagnosticConsumer::EndSourceFile() { 51 CheckDiagnostics(); 52 53 PrimaryClient->EndSourceFile(); 54 55 CurrentPreprocessor = 0; 56 } 57 58 void VerifyDiagnosticConsumer::HandleDiagnostic( 59 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { 60 if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) { 61 const SourceManager &SM = Info.getSourceManager(); 62 FirstErrorFID = SM.getFileID(Info.getLocation()); 63 } 64 // Send the diagnostic to the buffer, we will check it once we reach the end 65 // of the source file (or are destructed). 66 Buffer->HandleDiagnostic(DiagLevel, Info); 67 } 68 69 //===----------------------------------------------------------------------===// 70 // Checking diagnostics implementation. 71 //===----------------------------------------------------------------------===// 72 73 typedef TextDiagnosticBuffer::DiagList DiagList; 74 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; 75 76 namespace { 77 78 /// Directive - Abstract class representing a parsed verify directive. 79 /// 80 class Directive { 81 public: 82 static Directive* Create(bool RegexKind, const SourceLocation &Location, 83 const std::string &Text, unsigned Count); 84 public: 85 SourceLocation Location; 86 const std::string Text; 87 unsigned Count; 88 89 virtual ~Directive() { } 90 91 // Returns true if directive text is valid. 92 // Otherwise returns false and populates E. 93 virtual bool isValid(std::string &Error) = 0; 94 95 // Returns true on match. 96 virtual bool Match(const std::string &S) = 0; 97 98 protected: 99 Directive(const SourceLocation &Location, const std::string &Text, 100 unsigned Count) 101 : Location(Location), Text(Text), Count(Count) { } 102 103 private: 104 Directive(const Directive&); // DO NOT IMPLEMENT 105 void operator=(const Directive&); // DO NOT IMPLEMENT 106 }; 107 108 /// StandardDirective - Directive with string matching. 109 /// 110 class StandardDirective : public Directive { 111 public: 112 StandardDirective(const SourceLocation &Location, const std::string &Text, 113 unsigned Count) 114 : Directive(Location, Text, Count) { } 115 116 virtual bool isValid(std::string &Error) { 117 // all strings are considered valid; even empty ones 118 return true; 119 } 120 121 virtual bool Match(const std::string &S) { 122 return S.find(Text) != std::string::npos || 123 Text.find(S) != std::string::npos; 124 } 125 }; 126 127 /// RegexDirective - Directive with regular-expression matching. 128 /// 129 class RegexDirective : public Directive { 130 public: 131 RegexDirective(const SourceLocation &Location, const std::string &Text, 132 unsigned Count) 133 : Directive(Location, Text, Count), Regex(Text) { } 134 135 virtual bool isValid(std::string &Error) { 136 if (Regex.isValid(Error)) 137 return true; 138 return false; 139 } 140 141 virtual bool Match(const std::string &S) { 142 return Regex.match(S); 143 } 144 145 private: 146 llvm::Regex Regex; 147 }; 148 149 typedef std::vector<Directive*> DirectiveList; 150 151 /// ExpectedData - owns directive objects and deletes on destructor. 152 /// 153 struct ExpectedData { 154 DirectiveList Errors; 155 DirectiveList Warnings; 156 DirectiveList Notes; 157 158 ~ExpectedData() { 159 DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 }; 160 for (DirectiveList **PL = Lists; *PL; ++PL) { 161 DirectiveList * const L = *PL; 162 for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I) 163 delete *I; 164 } 165 } 166 }; 167 168 class ParseHelper 169 { 170 public: 171 ParseHelper(const char *Begin, const char *End) 172 : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { } 173 174 // Return true if string literal is next. 175 bool Next(StringRef S) { 176 P = C; 177 PEnd = C + S.size(); 178 if (PEnd > End) 179 return false; 180 return !memcmp(P, S.data(), S.size()); 181 } 182 183 // Return true if number is next. 184 // Output N only if number is next. 185 bool Next(unsigned &N) { 186 unsigned TMP = 0; 187 P = C; 188 for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { 189 TMP *= 10; 190 TMP += P[0] - '0'; 191 } 192 if (P == C) 193 return false; 194 PEnd = P; 195 N = TMP; 196 return true; 197 } 198 199 // Return true if string literal is found. 200 // When true, P marks begin-position of S in content. 201 bool Search(StringRef S) { 202 P = std::search(C, End, S.begin(), S.end()); 203 PEnd = P + S.size(); 204 return P != End; 205 } 206 207 // Advance 1-past previous next/search. 208 // Behavior is undefined if previous next/search failed. 209 bool Advance() { 210 C = PEnd; 211 return C < End; 212 } 213 214 // Skip zero or more whitespace. 215 void SkipWhitespace() { 216 for (; C < End && isspace(*C); ++C) 217 ; 218 } 219 220 // Return true if EOF reached. 221 bool Done() { 222 return !(C < End); 223 } 224 225 const char * const Begin; // beginning of expected content 226 const char * const End; // end of expected content (1-past) 227 const char *C; // position of next char in content 228 const char *P; 229 230 private: 231 const char *PEnd; // previous next/search subject end (1-past) 232 }; 233 234 } // namespace anonymous 235 236 /// ParseDirective - Go through the comment and see if it indicates expected 237 /// diagnostics. If so, then put them in the appropriate directive list. 238 /// 239 static void ParseDirective(const char *CommentStart, unsigned CommentLen, 240 ExpectedData &ED, Preprocessor &PP, 241 SourceLocation Pos) { 242 // A single comment may contain multiple directives. 243 for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) { 244 // search for token: expected 245 if (!PH.Search("expected")) 246 break; 247 PH.Advance(); 248 249 // next token: - 250 if (!PH.Next("-")) 251 continue; 252 PH.Advance(); 253 254 // next token: { error | warning | note } 255 DirectiveList* DL = NULL; 256 if (PH.Next("error")) 257 DL = &ED.Errors; 258 else if (PH.Next("warning")) 259 DL = &ED.Warnings; 260 else if (PH.Next("note")) 261 DL = &ED.Notes; 262 else 263 continue; 264 PH.Advance(); 265 266 // default directive kind 267 bool RegexKind = false; 268 const char* KindStr = "string"; 269 270 // next optional token: - 271 if (PH.Next("-re")) { 272 PH.Advance(); 273 RegexKind = true; 274 KindStr = "regex"; 275 } 276 277 // skip optional whitespace 278 PH.SkipWhitespace(); 279 280 // next optional token: positive integer 281 unsigned Count = 1; 282 if (PH.Next(Count)) 283 PH.Advance(); 284 285 // skip optional whitespace 286 PH.SkipWhitespace(); 287 288 // next token: {{ 289 if (!PH.Next("{{")) { 290 PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin), 291 diag::err_verify_missing_start) << KindStr; 292 continue; 293 } 294 PH.Advance(); 295 const char* const ContentBegin = PH.C; // mark content begin 296 297 // search for token: }} 298 if (!PH.Search("}}")) { 299 PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin), 300 diag::err_verify_missing_end) << KindStr; 301 continue; 302 } 303 const char* const ContentEnd = PH.P; // mark content end 304 PH.Advance(); 305 306 // build directive text; convert \n to newlines 307 std::string Text; 308 StringRef NewlineStr = "\\n"; 309 StringRef Content(ContentBegin, ContentEnd-ContentBegin); 310 size_t CPos = 0; 311 size_t FPos; 312 while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { 313 Text += Content.substr(CPos, FPos-CPos); 314 Text += '\n'; 315 CPos = FPos + NewlineStr.size(); 316 } 317 if (Text.empty()) 318 Text.assign(ContentBegin, ContentEnd); 319 320 // construct new directive 321 Directive *D = Directive::Create(RegexKind, Pos, Text, Count); 322 std::string Error; 323 if (D->isValid(Error)) 324 DL->push_back(D); 325 else { 326 PP.Diag(Pos.getLocWithOffset(ContentBegin-PH.Begin), 327 diag::err_verify_invalid_content) 328 << KindStr << Error; 329 } 330 } 331 } 332 333 /// FindExpectedDiags - Lex the main source file to find all of the 334 // expected errors and warnings. 335 static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { 336 // Create a raw lexer to pull all the comments out of FID. 337 if (FID.isInvalid()) 338 return; 339 340 SourceManager& SM = PP.getSourceManager(); 341 // Create a lexer to lex all the tokens of the main file in raw mode. 342 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 343 Lexer RawLex(FID, FromFile, SM, PP.getLangOptions()); 344 345 // Return comments as tokens, this is how we find expected diagnostics. 346 RawLex.SetCommentRetentionState(true); 347 348 Token Tok; 349 Tok.setKind(tok::comment); 350 while (Tok.isNot(tok::eof)) { 351 RawLex.Lex(Tok); 352 if (!Tok.is(tok::comment)) continue; 353 354 std::string Comment = PP.getSpelling(Tok); 355 if (Comment.empty()) continue; 356 357 // Find all expected errors/warnings/notes. 358 ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation()); 359 }; 360 } 361 362 /// PrintProblem - This takes a diagnostic map of the delta between expected and 363 /// seen diagnostics. If there's anything in it, then something unexpected 364 /// happened. Print the map out in a nice format and return "true". If the map 365 /// is empty and we're not going to print things, then return "false". 366 /// 367 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 368 const_diag_iterator diag_begin, 369 const_diag_iterator diag_end, 370 const char *Kind, bool Expected) { 371 if (diag_begin == diag_end) return 0; 372 373 llvm::SmallString<256> Fmt; 374 llvm::raw_svector_ostream OS(Fmt); 375 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { 376 if (I->first.isInvalid() || !SourceMgr) 377 OS << "\n (frontend)"; 378 else 379 OS << "\n Line " << SourceMgr->getPresumedLineNumber(I->first); 380 OS << ": " << I->second; 381 } 382 383 Diags.Report(diag::err_verify_inconsistent_diags) 384 << Kind << !Expected << OS.str(); 385 return std::distance(diag_begin, diag_end); 386 } 387 388 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, 389 DirectiveList &DL, const char *Kind, 390 bool Expected) { 391 if (DL.empty()) 392 return 0; 393 394 llvm::SmallString<256> Fmt; 395 llvm::raw_svector_ostream OS(Fmt); 396 for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { 397 Directive& D = **I; 398 if (D.Location.isInvalid() || !SourceMgr) 399 OS << "\n (frontend)"; 400 else 401 OS << "\n Line " << SourceMgr->getPresumedLineNumber(D.Location); 402 OS << ": " << D.Text; 403 } 404 405 Diags.Report(diag::err_verify_inconsistent_diags) 406 << Kind << !Expected << OS.str(); 407 return DL.size(); 408 } 409 410 /// CheckLists - Compare expected to seen diagnostic lists and return the 411 /// the difference between them. 412 /// 413 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 414 const char *Label, 415 DirectiveList &Left, 416 const_diag_iterator d2_begin, 417 const_diag_iterator d2_end) { 418 DirectiveList LeftOnly; 419 DiagList Right(d2_begin, d2_end); 420 421 for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { 422 Directive& D = **I; 423 unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location); 424 425 for (unsigned i = 0; i < D.Count; ++i) { 426 DiagList::iterator II, IE; 427 for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { 428 unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); 429 if (LineNo1 != LineNo2) 430 continue; 431 432 const std::string &RightText = II->second; 433 if (D.Match(RightText)) 434 break; 435 } 436 if (II == IE) { 437 // Not found. 438 LeftOnly.push_back(*I); 439 } else { 440 // Found. The same cannot be found twice. 441 Right.erase(II); 442 } 443 } 444 } 445 // Now all that's left in Right are those that were not matched. 446 447 return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) + 448 PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), 449 Label, false)); 450 } 451 452 /// CheckResults - This compares the expected results to those that 453 /// were actually reported. It emits any discrepencies. Return "true" if there 454 /// were problems. Return "false" otherwise. 455 /// 456 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 457 const TextDiagnosticBuffer &Buffer, 458 ExpectedData &ED) { 459 // We want to capture the delta between what was expected and what was 460 // seen. 461 // 462 // Expected \ Seen - set expected but not seen 463 // Seen \ Expected - set seen but not expected 464 unsigned NumProblems = 0; 465 466 // See if there are error mismatches. 467 NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, 468 Buffer.err_begin(), Buffer.err_end()); 469 470 // See if there are warning mismatches. 471 NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, 472 Buffer.warn_begin(), Buffer.warn_end()); 473 474 // See if there are note mismatches. 475 NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, 476 Buffer.note_begin(), Buffer.note_end()); 477 478 return NumProblems; 479 } 480 481 void VerifyDiagnosticConsumer::CheckDiagnostics() { 482 ExpectedData ED; 483 484 // Ensure any diagnostics go to the primary client. 485 bool OwnsCurClient = Diags.ownsClient(); 486 DiagnosticConsumer *CurClient = Diags.takeClient(); 487 Diags.setClient(PrimaryClient, false); 488 489 // If we have a preprocessor, scan the source for expected diagnostic 490 // markers. If not then any diagnostics are unexpected. 491 if (CurrentPreprocessor) { 492 SourceManager &SM = CurrentPreprocessor->getSourceManager(); 493 // Extract expected-error strings from main file. 494 FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID()); 495 // Only check for expectations in other diagnostic locations 496 // if they are not the main file (via ID or FileEntry) - the main 497 // file has already been looked at, and its expectations must not 498 // be added twice. 499 if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID() 500 && (!SM.getFileEntryForID(FirstErrorFID) 501 || (SM.getFileEntryForID(FirstErrorFID) != 502 SM.getFileEntryForID(SM.getMainFileID())))) { 503 FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID); 504 FirstErrorFID = FileID(); 505 } 506 507 // Check that the expected diagnostics occurred. 508 NumErrors += CheckResults(Diags, SM, *Buffer, ED); 509 } else { 510 NumErrors += (PrintProblem(Diags, 0, 511 Buffer->err_begin(), Buffer->err_end(), 512 "error", false) + 513 PrintProblem(Diags, 0, 514 Buffer->warn_begin(), Buffer->warn_end(), 515 "warn", false) + 516 PrintProblem(Diags, 0, 517 Buffer->note_begin(), Buffer->note_end(), 518 "note", false)); 519 } 520 521 Diags.takeClient(); 522 Diags.setClient(CurClient, OwnsCurClient); 523 524 // Reset the buffer, we have processed all the diagnostics in it. 525 Buffer.reset(new TextDiagnosticBuffer()); 526 } 527 528 DiagnosticConsumer * 529 VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const { 530 if (!Diags.getClient()) 531 Diags.setClient(PrimaryClient->clone(Diags)); 532 533 return new VerifyDiagnosticConsumer(Diags); 534 } 535 536 Directive* Directive::Create(bool RegexKind, const SourceLocation &Location, 537 const std::string &Text, unsigned Count) { 538 if (RegexKind) 539 return new RegexDirective(Location, Text, Count); 540 return new StandardDirective(Location, Text, Count); 541 } 542