| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | #include "clang/Frontend/VerifyDiagnosticConsumer.h" |
| 14 | #include "clang/Basic/CharInfo.h" |
| 15 | #include "clang/Basic/Diagnostic.h" |
| 16 | #include "clang/Basic/DiagnosticOptions.h" |
| 17 | #include "clang/Basic/FileManager.h" |
| 18 | #include "clang/Basic/LLVM.h" |
| 19 | #include "clang/Basic/SourceLocation.h" |
| 20 | #include "clang/Basic/SourceManager.h" |
| 21 | #include "clang/Basic/TokenKinds.h" |
| 22 | #include "clang/Frontend/FrontendDiagnostic.h" |
| 23 | #include "clang/Frontend/TextDiagnosticBuffer.h" |
| 24 | #include "clang/Lex/HeaderSearch.h" |
| 25 | #include "clang/Lex/Lexer.h" |
| 26 | #include "clang/Lex/PPCallbacks.h" |
| 27 | #include "clang/Lex/Preprocessor.h" |
| 28 | #include "clang/Lex/Token.h" |
| 29 | #include "llvm/ADT/STLExtras.h" |
| 30 | #include "llvm/ADT/SmallPtrSet.h" |
| 31 | #include "llvm/ADT/SmallString.h" |
| 32 | #include "llvm/ADT/StringRef.h" |
| 33 | #include "llvm/ADT/Twine.h" |
| 34 | #include "llvm/Support/ErrorHandling.h" |
| 35 | #include "llvm/Support/Regex.h" |
| 36 | #include "llvm/Support/raw_ostream.h" |
| 37 | #include <algorithm> |
| 38 | #include <cassert> |
| 39 | #include <cstddef> |
| 40 | #include <cstring> |
| 41 | #include <iterator> |
| 42 | #include <memory> |
| 43 | #include <string> |
| 44 | #include <utility> |
| 45 | #include <vector> |
| 46 | |
| 47 | using namespace clang; |
| 48 | |
| 49 | using Directive = VerifyDiagnosticConsumer::Directive; |
| 50 | using DirectiveList = VerifyDiagnosticConsumer::DirectiveList; |
| 51 | using ExpectedData = VerifyDiagnosticConsumer::ExpectedData; |
| 52 | |
| 53 | VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) |
| 54 | : Diags(Diags_), PrimaryClient(Diags.getClient()), |
| 55 | PrimaryClientOwner(Diags.takeClient()), |
| 56 | Buffer(new TextDiagnosticBuffer()), Status(HasNoDirectives) { |
| 57 | if (Diags.hasSourceManager()) |
| 58 | setSourceManager(Diags.getSourceManager()); |
| 59 | } |
| 60 | |
| 61 | VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { |
| 62 | (0) . __assert_fail ("!ActiveSourceFiles && \"Incomplete parsing of source files!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 62, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); |
| 63 | (0) . __assert_fail ("!CurrentPreprocessor && \"CurrentPreprocessor should be invalid!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 63, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); |
| 64 | SrcManager = nullptr; |
| 65 | CheckDiagnostics(); |
| 66 | (0) . __assert_fail ("!Diags.ownsClient() && \"The VerifyDiagnosticConsumer takes over ownership of the client!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 67, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!Diags.ownsClient() && |
| 67 | (0) . __assert_fail ("!Diags.ownsClient() && \"The VerifyDiagnosticConsumer takes over ownership of the client!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 67, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "The VerifyDiagnosticConsumer takes over ownership of the client!"); |
| 68 | } |
| 69 | |
| 70 | #ifndef NDEBUG |
| 71 | |
| 72 | namespace { |
| 73 | |
| 74 | class VerifyFileTracker : public PPCallbacks { |
| 75 | VerifyDiagnosticConsumer &Verify; |
| 76 | SourceManager &SM; |
| 77 | |
| 78 | public: |
| 79 | VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) |
| 80 | : Verify(Verify), SM(SM) {} |
| 81 | |
| 82 | |
| 83 | |
| 84 | void FileChanged(SourceLocation Loc, FileChangeReason Reason, |
| 85 | SrcMgr::CharacteristicKind FileType, |
| 86 | FileID PrevFID) override { |
| 87 | Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), |
| 88 | VerifyDiagnosticConsumer::IsParsed); |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | } |
| 93 | |
| 94 | #endif |
| 95 | |
| 96 | |
| 97 | |
| 98 | void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, |
| 99 | const Preprocessor *PP) { |
| 100 | |
| 101 | if (++ActiveSourceFiles == 1) { |
| 102 | if (PP) { |
| 103 | CurrentPreprocessor = PP; |
| 104 | this->LangOpts = &LangOpts; |
| 105 | setSourceManager(PP->getSourceManager()); |
| 106 | const_cast<Preprocessor *>(PP)->addCommentHandler(this); |
| 107 | #ifndef NDEBUG |
| 108 | |
| 109 | const_cast<Preprocessor *>(PP)->addPPCallbacks( |
| 110 | llvm::make_unique<VerifyFileTracker>(*this, *SrcManager)); |
| 111 | #endif |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | (0) . __assert_fail ("(!PP || CurrentPreprocessor == PP) && \"Preprocessor changed!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 115, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); |
| 116 | PrimaryClient->BeginSourceFile(LangOpts, PP); |
| 117 | } |
| 118 | |
| 119 | void VerifyDiagnosticConsumer::EndSourceFile() { |
| 120 | (0) . __assert_fail ("ActiveSourceFiles && \"No active source files!\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 120, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(ActiveSourceFiles && "No active source files!"); |
| 121 | PrimaryClient->EndSourceFile(); |
| 122 | |
| 123 | |
| 124 | if (--ActiveSourceFiles == 0) { |
| 125 | if (CurrentPreprocessor) |
| 126 | const_cast<Preprocessor *>(CurrentPreprocessor)-> |
| 127 | removeCommentHandler(this); |
| 128 | |
| 129 | |
| 130 | CheckDiagnostics(); |
| 131 | CurrentPreprocessor = nullptr; |
| 132 | LangOpts = nullptr; |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | void VerifyDiagnosticConsumer::HandleDiagnostic( |
| 137 | DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { |
| 138 | if (Info.hasSourceManager()) { |
| 139 | |
| 140 | if (SrcManager && &Info.getSourceManager() != SrcManager) |
| 141 | return; |
| 142 | |
| 143 | setSourceManager(Info.getSourceManager()); |
| 144 | } |
| 145 | |
| 146 | #ifndef NDEBUG |
| 147 | |
| 148 | |
| 149 | if (SrcManager) { |
| 150 | SourceLocation Loc = Info.getLocation(); |
| 151 | if (Loc.isValid()) { |
| 152 | ParsedStatus PS = IsUnparsed; |
| 153 | |
| 154 | Loc = SrcManager->getExpansionLoc(Loc); |
| 155 | FileID FID = SrcManager->getFileID(Loc); |
| 156 | |
| 157 | const FileEntry *FE = SrcManager->getFileEntryForID(FID); |
| 158 | if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { |
| 159 | |
| 160 | |
| 161 | HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); |
| 162 | if (HS.findModuleForHeader(FE)) |
| 163 | PS = IsUnparsedNoDirectives; |
| 164 | } |
| 165 | |
| 166 | UpdateParsedFileStatus(*SrcManager, FID, PS); |
| 167 | } |
| 168 | } |
| 169 | #endif |
| 170 | |
| 171 | |
| 172 | |
| 173 | Buffer->HandleDiagnostic(DiagLevel, Info); |
| 174 | } |
| 175 | |
| 176 | |
| 177 | |
| 178 | |
| 179 | |
| 180 | using DiagList = TextDiagnosticBuffer::DiagList; |
| 181 | using const_diag_iterator = TextDiagnosticBuffer::const_iterator; |
| 182 | |
| 183 | namespace { |
| 184 | |
| 185 | |
| 186 | class StandardDirective : public Directive { |
| 187 | public: |
| 188 | StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, |
| 189 | bool MatchAnyLine, StringRef Text, unsigned Min, |
| 190 | unsigned Max) |
| 191 | : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {} |
| 192 | |
| 193 | bool isValid(std::string &Error) override { |
| 194 | |
| 195 | return true; |
| 196 | } |
| 197 | |
| 198 | bool match(StringRef S) override { |
| 199 | return S.find(Text) != StringRef::npos; |
| 200 | } |
| 201 | }; |
| 202 | |
| 203 | |
| 204 | class RegexDirective : public Directive { |
| 205 | public: |
| 206 | RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, |
| 207 | bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, |
| 208 | StringRef RegexStr) |
| 209 | : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), |
| 210 | Regex(RegexStr) {} |
| 211 | |
| 212 | bool isValid(std::string &Error) override { |
| 213 | return Regex.isValid(Error); |
| 214 | } |
| 215 | |
| 216 | bool match(StringRef S) override { |
| 217 | return Regex.match(S); |
| 218 | } |
| 219 | |
| 220 | private: |
| 221 | llvm::Regex Regex; |
| 222 | }; |
| 223 | |
| 224 | class ParseHelper |
| 225 | { |
| 226 | public: |
| 227 | ParseHelper(StringRef S) |
| 228 | : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {} |
| 229 | |
| 230 | |
| 231 | bool Next(StringRef S) { |
| 232 | P = C; |
| 233 | PEnd = C + S.size(); |
| 234 | if (PEnd > End) |
| 235 | return false; |
| 236 | return memcmp(P, S.data(), S.size()) == 0; |
| 237 | } |
| 238 | |
| 239 | |
| 240 | |
| 241 | bool Next(unsigned &N) { |
| 242 | unsigned TMP = 0; |
| 243 | P = C; |
| 244 | for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { |
| 245 | TMP *= 10; |
| 246 | TMP += P[0] - '0'; |
| 247 | } |
| 248 | if (P == C) |
| 249 | return false; |
| 250 | PEnd = P; |
| 251 | N = TMP; |
| 252 | return true; |
| 253 | } |
| 254 | |
| 255 | |
| 256 | |
| 257 | |
| 258 | |
| 259 | |
| 260 | |
| 261 | |
| 262 | |
| 263 | |
| 264 | bool Search(StringRef S, bool EnsureStartOfWord = false, |
| 265 | bool FinishDirectiveToken = false) { |
| 266 | do { |
| 267 | if (!S.empty()) { |
| 268 | P = std::search(C, End, S.begin(), S.end()); |
| 269 | PEnd = P + S.size(); |
| 270 | } |
| 271 | else { |
| 272 | P = C; |
| 273 | while (P != End && !isLetter(*P)) |
| 274 | ++P; |
| 275 | PEnd = P + 1; |
| 276 | } |
| 277 | if (P == End) |
| 278 | break; |
| 279 | |
| 280 | if (EnsureStartOfWord |
| 281 | |
| 282 | && !(P == Begin || isWhitespace(P[-1]) |
| 283 | |
| 284 | || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') |
| 285 | && P[-2] == '/'))) |
| 286 | continue; |
| 287 | if (FinishDirectiveToken) { |
| 288 | while (PEnd != End && (isAlphanumeric(*PEnd) |
| 289 | || *PEnd == '-' || *PEnd == '_')) |
| 290 | ++PEnd; |
| 291 | |
| 292 | |
| 293 | |
| 294 | |
| 295 | |
| 296 | (0) . __assert_fail ("isLetter(*P) && \"-verify prefix must start with a letter\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp", 296, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(isLetter(*P) && "-verify prefix must start with a letter"); |
| 297 | while (isDigit(PEnd[-1]) || PEnd[-1] == '-') |
| 298 | --PEnd; |
| 299 | } |
| 300 | return true; |
| 301 | } while (Advance()); |
| 302 | return false; |
| 303 | } |
| 304 | |
| 305 | |
| 306 | |
| 307 | bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { |
| 308 | unsigned Depth = 1; |
| 309 | P = C; |
| 310 | while (P < End) { |
| 311 | StringRef S(P, End - P); |
| 312 | if (S.startswith(OpenBrace)) { |
| 313 | ++Depth; |
| 314 | P += OpenBrace.size(); |
| 315 | } else if (S.startswith(CloseBrace)) { |
| 316 | --Depth; |
| 317 | if (Depth == 0) { |
| 318 | PEnd = P + CloseBrace.size(); |
| 319 | return true; |
| 320 | } |
| 321 | P += CloseBrace.size(); |
| 322 | } else { |
| 323 | ++P; |
| 324 | } |
| 325 | } |
| 326 | return false; |
| 327 | } |
| 328 | |
| 329 | |
| 330 | |
| 331 | bool Advance() { |
| 332 | C = PEnd; |
| 333 | return C < End; |
| 334 | } |
| 335 | |
| 336 | |
| 337 | void SkipWhitespace() { |
| 338 | for (; C < End && isWhitespace(*C); ++C) |
| 339 | ; |
| 340 | } |
| 341 | |
| 342 | |
| 343 | bool Done() { |
| 344 | return !(C < End); |
| 345 | } |
| 346 | |
| 347 | |
| 348 | const char * const Begin; |
| 349 | |
| 350 | |
| 351 | const char * const End; |
| 352 | |
| 353 | |
| 354 | const char *C; |
| 355 | |
| 356 | const char *P; |
| 357 | |
| 358 | private: |
| 359 | |
| 360 | const char *PEnd = nullptr; |
| 361 | }; |
| 362 | |
| 363 | } |
| 364 | |
| 365 | |
| 366 | |
| 367 | |
| 368 | |
| 369 | static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, |
| 370 | Preprocessor *PP, SourceLocation Pos, |
| 371 | VerifyDiagnosticConsumer::DirectiveStatus &Status) { |
| 372 | DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); |
| 373 | |
| 374 | |
| 375 | bool FoundDirective = false; |
| 376 | for (ParseHelper PH(S); !PH.Done();) { |
| 377 | |
| 378 | |
| 379 | |
| 380 | const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; |
| 381 | if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) |
| 382 | : PH.Search("", true, true))) |
| 383 | break; |
| 384 | PH.Advance(); |
| 385 | |
| 386 | |
| 387 | bool RegexKind = false; |
| 388 | const char* KindStr = "string"; |
| 389 | |
| 390 | |
| 391 | |
| 392 | |
| 393 | |
| 394 | |
| 395 | StringRef DToken(PH.P, PH.C - PH.P); |
| 396 | |
| 397 | |
| 398 | if (DToken.endswith("-re")) { |
| 399 | RegexKind = true; |
| 400 | KindStr = "regex"; |
| 401 | DToken = DToken.substr(0, DToken.size()-3); |
| 402 | } |
| 403 | |
| 404 | |
| 405 | DirectiveList *DL = nullptr; |
| 406 | bool NoDiag = false; |
| 407 | StringRef DType; |
| 408 | if (DToken.endswith(DType="-error")) |
| 409 | DL = ED ? &ED->Errors : nullptr; |
| 410 | else if (DToken.endswith(DType="-warning")) |
| 411 | DL = ED ? &ED->Warnings : nullptr; |
| 412 | else if (DToken.endswith(DType="-remark")) |
| 413 | DL = ED ? &ED->Remarks : nullptr; |
| 414 | else if (DToken.endswith(DType="-note")) |
| 415 | DL = ED ? &ED->Notes : nullptr; |
| 416 | else if (DToken.endswith(DType="-no-diagnostics")) { |
| 417 | NoDiag = true; |
| 418 | if (RegexKind) |
| 419 | continue; |
| 420 | } |
| 421 | else |
| 422 | continue; |
| 423 | DToken = DToken.substr(0, DToken.size()-DType.size()); |
| 424 | |
| 425 | |
| 426 | |
| 427 | |
| 428 | if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) |
| 429 | continue; |
| 430 | |
| 431 | if (NoDiag) { |
| 432 | if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) |
| 433 | Diags.Report(Pos, diag::err_verify_invalid_no_diags) |
| 434 | << ; |
| 435 | else |
| 436 | Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; |
| 437 | continue; |
| 438 | } |
| 439 | if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { |
| 440 | Diags.Report(Pos, diag::err_verify_invalid_no_diags) |
| 441 | << ; |
| 442 | continue; |
| 443 | } |
| 444 | Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; |
| 445 | |
| 446 | |
| 447 | |
| 448 | if (!DL) |
| 449 | return true; |
| 450 | |
| 451 | |
| 452 | SourceLocation ExpectedLoc; |
| 453 | bool MatchAnyLine = false; |
| 454 | if (!PH.Next("@")) { |
| 455 | ExpectedLoc = Pos; |
| 456 | } else { |
| 457 | PH.Advance(); |
| 458 | unsigned Line = 0; |
| 459 | bool FoundPlus = PH.Next("+"); |
| 460 | if (FoundPlus || PH.Next("-")) { |
| 461 | |
| 462 | PH.Advance(); |
| 463 | bool Invalid = false; |
| 464 | unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); |
| 465 | if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { |
| 466 | if (FoundPlus) ExpectedLine += Line; |
| 467 | else ExpectedLine -= Line; |
| 468 | ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); |
| 469 | } |
| 470 | } else if (PH.Next(Line)) { |
| 471 | |
| 472 | if (Line > 0) |
| 473 | ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); |
| 474 | } else if (PP && PH.Search(":")) { |
| 475 | |
| 476 | StringRef Filename(PH.C, PH.P-PH.C); |
| 477 | PH.Advance(); |
| 478 | |
| 479 | |
| 480 | const DirectoryLookup *CurDir; |
| 481 | const FileEntry *FE = |
| 482 | PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, |
| 483 | nullptr, nullptr, nullptr, nullptr, nullptr); |
| 484 | if (!FE) { |
| 485 | Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), |
| 486 | diag::err_verify_missing_file) << Filename << KindStr; |
| 487 | continue; |
| 488 | } |
| 489 | |
| 490 | if (SM.translateFile(FE).isInvalid()) |
| 491 | SM.createFileID(FE, Pos, SrcMgr::C_User); |
| 492 | |
| 493 | if (PH.Next(Line) && Line > 0) |
| 494 | ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); |
| 495 | else if (PH.Next("*")) { |
| 496 | MatchAnyLine = true; |
| 497 | ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); |
| 498 | } |
| 499 | } else if (PH.Next("*")) { |
| 500 | MatchAnyLine = true; |
| 501 | ExpectedLoc = SourceLocation(); |
| 502 | } |
| 503 | |
| 504 | if (ExpectedLoc.isInvalid() && !MatchAnyLine) { |
| 505 | Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), |
| 506 | diag::err_verify_missing_line) << KindStr; |
| 507 | continue; |
| 508 | } |
| 509 | PH.Advance(); |
| 510 | } |
| 511 | |
| 512 | |
| 513 | PH.SkipWhitespace(); |
| 514 | |
| 515 | |
| 516 | unsigned Min = 1; |
| 517 | unsigned Max = 1; |
| 518 | if (PH.Next(Min)) { |
| 519 | PH.Advance(); |
| 520 | |
| 521 | |
| 522 | if (PH.Next("+")) { |
| 523 | Max = Directive::MaxCount; |
| 524 | PH.Advance(); |
| 525 | } else if (PH.Next("-")) { |
| 526 | PH.Advance(); |
| 527 | if (!PH.Next(Max) || Max < Min) { |
| 528 | Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), |
| 529 | diag::err_verify_invalid_range) << KindStr; |
| 530 | continue; |
| 531 | } |
| 532 | PH.Advance(); |
| 533 | } else { |
| 534 | Max = Min; |
| 535 | } |
| 536 | } else if (PH.Next("+")) { |
| 537 | |
| 538 | Max = Directive::MaxCount; |
| 539 | PH.Advance(); |
| 540 | } |
| 541 | |
| 542 | |
| 543 | PH.SkipWhitespace(); |
| 544 | |
| 545 | |
| 546 | if (!PH.Next("{{")) { |
| 547 | Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), |
| 548 | diag::err_verify_missing_start) << KindStr; |
| 549 | continue; |
| 550 | } |
| 551 | PH.Advance(); |
| 552 | const char* const ContentBegin = PH.C; |
| 553 | |
| 554 | |
| 555 | if (!PH.SearchClosingBrace("{{", "}}")) { |
| 556 | Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), |
| 557 | diag::err_verify_missing_end) << KindStr; |
| 558 | continue; |
| 559 | } |
| 560 | const char* const ContentEnd = PH.P; |
| 561 | PH.Advance(); |
| 562 | |
| 563 | |
| 564 | std::string Text; |
| 565 | StringRef NewlineStr = "\\n"; |
| 566 | StringRef Content(ContentBegin, ContentEnd-ContentBegin); |
| 567 | size_t CPos = 0; |
| 568 | size_t FPos; |
| 569 | while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { |
| 570 | Text += Content.substr(CPos, FPos-CPos); |
| 571 | Text += '\n'; |
| 572 | CPos = FPos + NewlineStr.size(); |
| 573 | } |
| 574 | if (Text.empty()) |
| 575 | Text.assign(ContentBegin, ContentEnd); |
| 576 | |
| 577 | |
| 578 | if (RegexKind && Text.find("{{") == StringRef::npos) { |
| 579 | Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), |
| 580 | diag::err_verify_missing_regex) << Text; |
| 581 | return false; |
| 582 | } |
| 583 | |
| 584 | |
| 585 | std::unique_ptr<Directive> D = Directive::create( |
| 586 | RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); |
| 587 | |
| 588 | std::string Error; |
| 589 | if (D->isValid(Error)) { |
| 590 | DL->push_back(std::move(D)); |
| 591 | FoundDirective = true; |
| 592 | } else { |
| 593 | Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), |
| 594 | diag::err_verify_invalid_content) |
| 595 | << KindStr << Error; |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | return FoundDirective; |
| 600 | } |
| 601 | |
| 602 | |
| 603 | |
| 604 | bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, |
| 605 | SourceRange ) { |
| 606 | SourceManager &SM = PP.getSourceManager(); |
| 607 | |
| 608 | |
| 609 | if (SrcManager && &SM != SrcManager) |
| 610 | return false; |
| 611 | |
| 612 | SourceLocation = Comment.getBegin(); |
| 613 | |
| 614 | const char * = SM.getCharacterData(CommentBegin); |
| 615 | StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); |
| 616 | |
| 617 | if (C.empty()) |
| 618 | return false; |
| 619 | |
| 620 | |
| 621 | size_t loc = C.find('\\'); |
| 622 | if (loc == StringRef::npos) { |
| 623 | ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); |
| 624 | return false; |
| 625 | } |
| 626 | |
| 627 | std::string C2; |
| 628 | C2.reserve(C.size()); |
| 629 | |
| 630 | for (size_t last = 0;; loc = C.find('\\', last)) { |
| 631 | if (loc == StringRef::npos || loc == C.size()) { |
| 632 | C2 += C.substr(last); |
| 633 | break; |
| 634 | } |
| 635 | C2 += C.substr(last, loc-last); |
| 636 | last = loc + 1; |
| 637 | |
| 638 | if (C[last] == '\n' || C[last] == '\r') { |
| 639 | ++last; |
| 640 | |
| 641 | |
| 642 | if (last < C.size()) |
| 643 | if (C[last] == '\n' || C[last] == '\r') |
| 644 | if (C[last] != C[last-1]) |
| 645 | ++last; |
| 646 | } else { |
| 647 | |
| 648 | C2 += '\\'; |
| 649 | } |
| 650 | } |
| 651 | |
| 652 | if (!C2.empty()) |
| 653 | ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); |
| 654 | return false; |
| 655 | } |
| 656 | |
| 657 | #ifndef NDEBUG |
| 658 | |
| 659 | |
| 660 | |
| 661 | |
| 662 | |
| 663 | static bool findDirectives(SourceManager &SM, FileID FID, |
| 664 | const LangOptions &LangOpts) { |
| 665 | |
| 666 | if (FID.isInvalid()) |
| 667 | return false; |
| 668 | |
| 669 | |
| 670 | const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); |
| 671 | Lexer RawLex(FID, FromFile, SM, LangOpts); |
| 672 | |
| 673 | |
| 674 | RawLex.SetCommentRetentionState(true); |
| 675 | |
| 676 | Token Tok; |
| 677 | Tok.setKind(tok::comment); |
| 678 | VerifyDiagnosticConsumer::DirectiveStatus Status = |
| 679 | VerifyDiagnosticConsumer::HasNoDirectives; |
| 680 | while (Tok.isNot(tok::eof)) { |
| 681 | RawLex.LexFromRawLexer(Tok); |
| 682 | if (!Tok.is(tok::comment)) continue; |
| 683 | |
| 684 | std::string = RawLex.getSpelling(Tok, SM, LangOpts); |
| 685 | if (Comment.empty()) continue; |
| 686 | |
| 687 | |
| 688 | if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), |
| 689 | Status)) |
| 690 | return true; |
| 691 | } |
| 692 | return false; |
| 693 | } |
| 694 | #endif |
| 695 | |
| 696 | |
| 697 | |
| 698 | static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, |
| 699 | const_diag_iterator diag_begin, |
| 700 | const_diag_iterator diag_end, |
| 701 | const char *Kind) { |
| 702 | if (diag_begin == diag_end) return 0; |
| 703 | |
| 704 | SmallString<256> Fmt; |
| 705 | llvm::raw_svector_ostream OS(Fmt); |
| 706 | for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { |
| 707 | if (I->first.isInvalid() || !SourceMgr) |
| 708 | OS << "\n (frontend)"; |
| 709 | else { |
| 710 | OS << "\n "; |
| 711 | if (const FileEntry *File = SourceMgr->getFileEntryForID( |
| 712 | SourceMgr->getFileID(I->first))) |
| 713 | OS << " File " << File->getName(); |
| 714 | OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); |
| 715 | } |
| 716 | OS << ": " << I->second; |
| 717 | } |
| 718 | |
| 719 | Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() |
| 720 | << Kind << << OS.str(); |
| 721 | return std::distance(diag_begin, diag_end); |
| 722 | } |
| 723 | |
| 724 | |
| 725 | |
| 726 | static unsigned PrintExpected(DiagnosticsEngine &Diags, |
| 727 | SourceManager &SourceMgr, |
| 728 | std::vector<Directive *> &DL, const char *Kind) { |
| 729 | if (DL.empty()) |
| 730 | return 0; |
| 731 | |
| 732 | SmallString<256> Fmt; |
| 733 | llvm::raw_svector_ostream OS(Fmt); |
| 734 | for (const auto *D : DL) { |
| 735 | if (D->DiagnosticLoc.isInvalid()) |
| 736 | OS << "\n File *"; |
| 737 | else |
| 738 | OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc); |
| 739 | if (D->MatchAnyLine) |
| 740 | OS << " Line *"; |
| 741 | else |
| 742 | OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc); |
| 743 | if (D->DirectiveLoc != D->DiagnosticLoc) |
| 744 | OS << " (directive at " |
| 745 | << SourceMgr.getFilename(D->DirectiveLoc) << ':' |
| 746 | << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')'; |
| 747 | OS << ": " << D->Text; |
| 748 | } |
| 749 | |
| 750 | Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() |
| 751 | << Kind << << OS.str(); |
| 752 | return DL.size(); |
| 753 | } |
| 754 | |
| 755 | |
| 756 | static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, |
| 757 | SourceLocation DiagnosticLoc) { |
| 758 | while (DiagnosticLoc.isMacroID()) |
| 759 | DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); |
| 760 | |
| 761 | if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) |
| 762 | return true; |
| 763 | |
| 764 | const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); |
| 765 | if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) |
| 766 | return true; |
| 767 | |
| 768 | return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); |
| 769 | } |
| 770 | |
| 771 | |
| 772 | |
| 773 | static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, |
| 774 | const char *Label, |
| 775 | DirectiveList &Left, |
| 776 | const_diag_iterator d2_begin, |
| 777 | const_diag_iterator d2_end, |
| 778 | bool IgnoreUnexpected) { |
| 779 | std::vector<Directive *> LeftOnly; |
| 780 | DiagList Right(d2_begin, d2_end); |
| 781 | |
| 782 | for (auto &Owner : Left) { |
| 783 | Directive &D = *Owner; |
| 784 | unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); |
| 785 | |
| 786 | for (unsigned i = 0; i < D.Max; ++i) { |
| 787 | DiagList::iterator II, IE; |
| 788 | for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { |
| 789 | if (!D.MatchAnyLine) { |
| 790 | unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); |
| 791 | if (LineNo1 != LineNo2) |
| 792 | continue; |
| 793 | } |
| 794 | |
| 795 | if (!D.DiagnosticLoc.isInvalid() && |
| 796 | !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) |
| 797 | continue; |
| 798 | |
| 799 | const std::string &RightText = II->second; |
| 800 | if (D.match(RightText)) |
| 801 | break; |
| 802 | } |
| 803 | if (II == IE) { |
| 804 | |
| 805 | if (i >= D.Min) break; |
| 806 | LeftOnly.push_back(&D); |
| 807 | } else { |
| 808 | |
| 809 | Right.erase(II); |
| 810 | } |
| 811 | } |
| 812 | } |
| 813 | |
| 814 | unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); |
| 815 | if (!IgnoreUnexpected) |
| 816 | num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); |
| 817 | return num; |
| 818 | } |
| 819 | |
| 820 | |
| 821 | |
| 822 | |
| 823 | static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, |
| 824 | const TextDiagnosticBuffer &Buffer, |
| 825 | ExpectedData &ED) { |
| 826 | |
| 827 | |
| 828 | |
| 829 | |
| 830 | |
| 831 | unsigned NumProblems = 0; |
| 832 | |
| 833 | const DiagnosticLevelMask DiagMask = |
| 834 | Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); |
| 835 | |
| 836 | |
| 837 | NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, |
| 838 | Buffer.err_begin(), Buffer.err_end(), |
| 839 | bool(DiagnosticLevelMask::Error & DiagMask)); |
| 840 | |
| 841 | |
| 842 | NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, |
| 843 | Buffer.warn_begin(), Buffer.warn_end(), |
| 844 | bool(DiagnosticLevelMask::Warning & DiagMask)); |
| 845 | |
| 846 | |
| 847 | NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, |
| 848 | Buffer.remark_begin(), Buffer.remark_end(), |
| 849 | bool(DiagnosticLevelMask::Remark & DiagMask)); |
| 850 | |
| 851 | |
| 852 | NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, |
| 853 | Buffer.note_begin(), Buffer.note_end(), |
| 854 | bool(DiagnosticLevelMask::Note & DiagMask)); |
| 855 | |
| 856 | return NumProblems; |
| 857 | } |
| 858 | |
| 859 | void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, |
| 860 | FileID FID, |
| 861 | ParsedStatus PS) { |
| 862 | |
| 863 | setSourceManager(SM); |
| 864 | |
| 865 | #ifndef NDEBUG |
| 866 | if (FID.isInvalid()) |
| 867 | return; |
| 868 | |
| 869 | const FileEntry *FE = SM.getFileEntryForID(FID); |
| 870 | |
| 871 | if (PS == IsParsed) { |
| 872 | |
| 873 | UnparsedFiles.erase(FID); |
| 874 | ParsedFiles.insert(std::make_pair(FID, FE)); |
| 875 | } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { |
| 876 | |
| 877 | |
| 878 | |
| 879 | bool FoundDirectives; |
| 880 | if (PS == IsUnparsedNoDirectives) |
| 881 | FoundDirectives = false; |
| 882 | else |
| 883 | FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); |
| 884 | |
| 885 | |
| 886 | UnparsedFiles.insert(std::make_pair(FID, |
| 887 | UnparsedFileStatus(FE, FoundDirectives))); |
| 888 | } |
| 889 | #endif |
| 890 | } |
| 891 | |
| 892 | void VerifyDiagnosticConsumer::CheckDiagnostics() { |
| 893 | |
| 894 | DiagnosticConsumer *CurClient = Diags.getClient(); |
| 895 | std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); |
| 896 | Diags.setClient(PrimaryClient, false); |
| 897 | |
| 898 | #ifndef NDEBUG |
| 899 | |
| 900 | |
| 901 | |
| 902 | |
| 903 | |
| 904 | |
| 905 | if (!UnparsedFiles.empty()) { |
| 906 | |
| 907 | llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; |
| 908 | for (const auto &I : ParsedFiles) |
| 909 | if (const FileEntry *FE = I.second) |
| 910 | ParsedFileCache.insert(FE); |
| 911 | |
| 912 | |
| 913 | for (const auto &I : UnparsedFiles) { |
| 914 | const UnparsedFileStatus &Status = I.second; |
| 915 | const FileEntry *FE = Status.getFile(); |
| 916 | |
| 917 | |
| 918 | if (FE && ParsedFileCache.count(FE)) |
| 919 | continue; |
| 920 | |
| 921 | |
| 922 | if (Status.foundDirectives()) { |
| 923 | llvm::report_fatal_error(Twine("-verify directives found after rather" |
| 924 | " than during normal parsing of ", |
| 925 | StringRef(FE ? FE->getName() : "(unknown)"))); |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | |
| 930 | UnparsedFiles.clear(); |
| 931 | } |
| 932 | #endif |
| 933 | |
| 934 | if (SrcManager) { |
| 935 | |
| 936 | |
| 937 | if (Status == HasNoDirectives) { |
| 938 | Diags.Report(diag::err_verify_no_directives).setForceEmit(); |
| 939 | ++NumErrors; |
| 940 | Status = HasNoDirectivesReported; |
| 941 | } |
| 942 | |
| 943 | |
| 944 | NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); |
| 945 | } else { |
| 946 | const DiagnosticLevelMask DiagMask = |
| 947 | ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); |
| 948 | if (bool(DiagnosticLevelMask::Error & DiagMask)) |
| 949 | NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), |
| 950 | Buffer->err_end(), "error"); |
| 951 | if (bool(DiagnosticLevelMask::Warning & DiagMask)) |
| 952 | NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), |
| 953 | Buffer->warn_end(), "warn"); |
| 954 | if (bool(DiagnosticLevelMask::Remark & DiagMask)) |
| 955 | NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), |
| 956 | Buffer->remark_end(), "remark"); |
| 957 | if (bool(DiagnosticLevelMask::Note & DiagMask)) |
| 958 | NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), |
| 959 | Buffer->note_end(), "note"); |
| 960 | } |
| 961 | |
| 962 | Diags.setClient(CurClient, Owner.release() != nullptr); |
| 963 | |
| 964 | |
| 965 | Buffer.reset(new TextDiagnosticBuffer()); |
| 966 | ED.Reset(); |
| 967 | } |
| 968 | |
| 969 | std::unique_ptr<Directive> Directive::create(bool RegexKind, |
| 970 | SourceLocation DirectiveLoc, |
| 971 | SourceLocation DiagnosticLoc, |
| 972 | bool MatchAnyLine, StringRef Text, |
| 973 | unsigned Min, unsigned Max) { |
| 974 | if (!RegexKind) |
| 975 | return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, |
| 976 | MatchAnyLine, Text, Min, Max); |
| 977 | |
| 978 | |
| 979 | std::string RegexStr; |
| 980 | StringRef S = Text; |
| 981 | while (!S.empty()) { |
| 982 | if (S.startswith("{{")) { |
| 983 | S = S.drop_front(2); |
| 984 | size_t RegexMatchLength = S.find("}}"); |
| 985 | assert(RegexMatchLength != StringRef::npos); |
| 986 | |
| 987 | RegexStr += "("; |
| 988 | RegexStr.append(S.data(), RegexMatchLength); |
| 989 | RegexStr += ")"; |
| 990 | S = S.drop_front(RegexMatchLength + 2); |
| 991 | } else { |
| 992 | size_t VerbatimMatchLength = S.find("{{"); |
| 993 | if (VerbatimMatchLength == StringRef::npos) |
| 994 | VerbatimMatchLength = S.size(); |
| 995 | |
| 996 | RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); |
| 997 | S = S.drop_front(VerbatimMatchLength); |
| 998 | } |
| 999 | } |
| 1000 | |
| 1001 | return llvm::make_unique<RegexDirective>( |
| 1002 | DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); |
| 1003 | } |
| 1004 | |