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