1 //===- FileCheck.cpp - Check that File's Contents match what is expected --===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // FileCheck does a line-by line check of a file that validates whether it 10 // contains the expected content. This is useful for regression tests etc. 11 // 12 // This program exits with an exit status of 2 on error, exit status of 0 if 13 // the file matched the expected contents, and exit status of 1 if it did not 14 // contain the expected contents. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "llvm/FileCheck/FileCheck.h" 19 #include "llvm/Support/CommandLine.h" 20 #include "llvm/Support/InitLLVM.h" 21 #include "llvm/Support/Process.h" 22 #include "llvm/Support/WithColor.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include <cmath> 25 using namespace llvm; 26 27 static cl::extrahelp FileCheckOptsEnv( 28 "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n" 29 "from the command line.\n"); 30 31 static cl::opt<std::string> 32 CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional); 33 34 static cl::opt<std::string> 35 InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), 36 cl::init("-"), cl::value_desc("filename")); 37 38 static cl::list<std::string> CheckPrefixes( 39 "check-prefix", 40 cl::desc("Prefix to use from check file (defaults to 'CHECK')")); 41 static cl::alias CheckPrefixesAlias( 42 "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, 43 cl::NotHidden, 44 cl::desc( 45 "Alias for -check-prefix permitting multiple comma separated values")); 46 47 static cl::list<std::string> CommentPrefixes( 48 "comment-prefixes", cl::CommaSeparated, cl::Hidden, 49 cl::desc("Comma-separated list of comment prefixes to use from check file\n" 50 "(defaults to 'COM,RUN'). Please avoid using this feature in\n" 51 "LLVM's LIT-based test suites, which should be easier to\n" 52 "maintain if they all follow a consistent comment style. This\n" 53 "feature is meant for non-LIT test suites using FileCheck.")); 54 55 static cl::opt<bool> NoCanonicalizeWhiteSpace( 56 "strict-whitespace", 57 cl::desc("Do not treat all horizontal whitespace as equivalent")); 58 59 static cl::opt<bool> IgnoreCase( 60 "ignore-case", 61 cl::desc("Use case-insensitive matching")); 62 63 static cl::list<std::string> ImplicitCheckNot( 64 "implicit-check-not", 65 cl::desc("Add an implicit negative check with this pattern to every\n" 66 "positive check. This can be used to ensure that no instances of\n" 67 "this pattern occur which are not matched by a positive pattern"), 68 cl::value_desc("pattern")); 69 70 static cl::list<std::string> 71 GlobalDefines("D", cl::AlwaysPrefix, 72 cl::desc("Define a variable to be used in capture patterns."), 73 cl::value_desc("VAR=VALUE")); 74 75 static cl::opt<bool> AllowEmptyInput( 76 "allow-empty", cl::init(false), 77 cl::desc("Allow the input file to be empty. This is useful when making\n" 78 "checks that some error message does not occur, for example.")); 79 80 static cl::opt<bool> AllowUnusedPrefixes( 81 "allow-unused-prefixes", cl::init(false), cl::ZeroOrMore, 82 cl::desc("Allow prefixes to be specified but not appear in the test.")); 83 84 static cl::opt<bool> MatchFullLines( 85 "match-full-lines", cl::init(false), 86 cl::desc("Require all positive matches to cover an entire input line.\n" 87 "Allows leading and trailing whitespace if --strict-whitespace\n" 88 "is not also passed.")); 89 90 static cl::opt<bool> EnableVarScope( 91 "enable-var-scope", cl::init(false), 92 cl::desc("Enables scope for regex variables. Variables with names that\n" 93 "do not start with '$' will be reset at the beginning of\n" 94 "each CHECK-LABEL block.")); 95 96 static cl::opt<bool> AllowDeprecatedDagOverlap( 97 "allow-deprecated-dag-overlap", cl::init(false), 98 cl::desc("Enable overlapping among matches in a group of consecutive\n" 99 "CHECK-DAG directives. This option is deprecated and is only\n" 100 "provided for convenience as old tests are migrated to the new\n" 101 "non-overlapping CHECK-DAG implementation.\n")); 102 103 static cl::opt<bool> Verbose( 104 "v", cl::init(false), cl::ZeroOrMore, 105 cl::desc("Print directive pattern matches, or add them to the input dump\n" 106 "if enabled.\n")); 107 108 static cl::opt<bool> VerboseVerbose( 109 "vv", cl::init(false), cl::ZeroOrMore, 110 cl::desc("Print information helpful in diagnosing internal FileCheck\n" 111 "issues, or add it to the input dump if enabled. Implies\n" 112 "-v.\n")); 113 114 // The order of DumpInputValue members affects their precedence, as documented 115 // for -dump-input below. 116 enum DumpInputValue { 117 DumpInputNever, 118 DumpInputFail, 119 DumpInputAlways, 120 DumpInputHelp 121 }; 122 123 static cl::list<DumpInputValue> DumpInputs( 124 "dump-input", 125 cl::desc("Dump input to stderr, adding annotations representing\n" 126 "currently enabled diagnostics. When there are multiple\n" 127 "occurrences of this option, the <value> that appears earliest\n" 128 "in the list below has precedence. The default is 'fail'.\n"), 129 cl::value_desc("mode"), 130 cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"), 131 clEnumValN(DumpInputAlways, "always", "Always dump input"), 132 clEnumValN(DumpInputFail, "fail", "Dump input on failure"), 133 clEnumValN(DumpInputNever, "never", "Never dump input"))); 134 135 // The order of DumpInputFilterValue members affects their precedence, as 136 // documented for -dump-input-filter below. 137 enum DumpInputFilterValue { 138 DumpInputFilterError, 139 DumpInputFilterAnnotation, 140 DumpInputFilterAnnotationFull, 141 DumpInputFilterAll 142 }; 143 144 static cl::list<DumpInputFilterValue> DumpInputFilters( 145 "dump-input-filter", 146 cl::desc("In the dump requested by -dump-input, print only input lines of\n" 147 "kind <value> plus any context specified by -dump-input-context.\n" 148 "When there are multiple occurrences of this option, the <value>\n" 149 "that appears earliest in the list below has precedence. The\n" 150 "default is 'error' when -dump-input=fail, and it's 'all' when\n" 151 "-dump-input=always.\n"), 152 cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"), 153 clEnumValN(DumpInputFilterAnnotationFull, "annotation-full", 154 "Input lines with annotations"), 155 clEnumValN(DumpInputFilterAnnotation, "annotation", 156 "Input lines with starting points of annotations"), 157 clEnumValN(DumpInputFilterError, "error", 158 "Input lines with starting points of error " 159 "annotations"))); 160 161 static cl::list<unsigned> DumpInputContexts( 162 "dump-input-context", cl::value_desc("N"), 163 cl::desc("In the dump requested by -dump-input, print <N> input lines\n" 164 "before and <N> input lines after any lines specified by\n" 165 "-dump-input-filter. When there are multiple occurrences of\n" 166 "this option, the largest specified <N> has precedence. The\n" 167 "default is 5.\n")); 168 169 typedef cl::list<std::string>::const_iterator prefix_iterator; 170 171 172 173 174 175 176 177 static void DumpCommandLine(int argc, char **argv) { 178 errs() << "FileCheck command line: "; 179 for (int I = 0; I < argc; I++) 180 errs() << " " << argv[I]; 181 errs() << "\n"; 182 } 183 184 struct MarkerStyle { 185 /// The starting char (before tildes) for marking the line. 186 char Lead; 187 /// What color to use for this annotation. 188 raw_ostream::Colors Color; 189 /// A note to follow the marker, or empty string if none. 190 std::string Note; 191 /// Does this marker indicate inclusion by -dump-input-filter=error? 192 bool FiltersAsError; 193 MarkerStyle() {} 194 MarkerStyle(char Lead, raw_ostream::Colors Color, 195 const std::string &Note = "", bool FiltersAsError = false) 196 : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) { 197 assert((!FiltersAsError || !Note.empty()) && 198 "expected error diagnostic to have note"); 199 } 200 }; 201 202 static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { 203 switch (MatchTy) { 204 case FileCheckDiag::MatchFoundAndExpected: 205 return MarkerStyle('^', raw_ostream::GREEN); 206 case FileCheckDiag::MatchFoundButExcluded: 207 return MarkerStyle('!', raw_ostream::RED, "error: no match expected", 208 /*FiltersAsError=*/true); 209 case FileCheckDiag::MatchFoundButWrongLine: 210 return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line", 211 /*FiltersAsError=*/true); 212 case FileCheckDiag::MatchFoundButDiscarded: 213 return MarkerStyle('!', raw_ostream::CYAN, 214 "discard: overlaps earlier match"); 215 case FileCheckDiag::MatchNoneAndExcluded: 216 return MarkerStyle('X', raw_ostream::GREEN); 217 case FileCheckDiag::MatchNoneButExpected: 218 return MarkerStyle('X', raw_ostream::RED, "error: no match found", 219 /*FiltersAsError=*/true); 220 case FileCheckDiag::MatchFuzzy: 221 return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match", 222 /*FiltersAsError=*/true); 223 } 224 llvm_unreachable_internal("unexpected match type"); 225 } 226 227 static void DumpInputAnnotationHelp(raw_ostream &OS) { 228 OS << "The following description was requested by -dump-input=help to\n" 229 << "explain the input dump printed by FileCheck.\n" 230 << "\n" 231 << "Related command-line options:\n" 232 << "\n" 233 << " - -dump-input=<value> enables or disables the input dump\n" 234 << " - -dump-input-filter=<value> filters the input lines\n" 235 << " - -dump-input-context=<N> adjusts the context of filtered lines\n" 236 << " - -v and -vv add more annotations\n" 237 << " - -color forces colors to be enabled both in the dump and below\n" 238 << " - -help documents the above options in more detail\n" 239 << "\n" 240 << "These options can also be set via FILECHECK_OPTS. For example, for\n" 241 << "maximum debugging output on failures:\n" 242 << "\n" 243 << " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n" 244 << "\n" 245 << "Input dump annotation format:\n" 246 << "\n"; 247 248 // Labels for input lines. 249 OS << " - "; 250 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; 251 OS << " labels line number L of the input file\n" 252 << " An extra space is added after each input line to represent" 253 << " the\n" 254 << " newline character\n"; 255 256 // Labels for annotation lines. 257 OS << " - "; 258 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; 259 OS << " labels the only match result for either (1) a pattern of type T" 260 << " from\n" 261 << " line L of the check file if L is an integer or (2) the" 262 << " I-th implicit\n" 263 << " pattern if L is \"imp\" followed by an integer " 264 << "I (index origin one)\n"; 265 OS << " - "; 266 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; 267 OS << " labels the Nth match result for such a pattern\n"; 268 269 // Markers on annotation lines. 270 OS << " - "; 271 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; 272 OS << " marks good match (reported if -v)\n" 273 << " - "; 274 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; 275 OS << " marks bad match, such as:\n" 276 << " - CHECK-NEXT on same line as previous match (error)\n" 277 << " - CHECK-NOT found (error)\n" 278 << " - CHECK-DAG overlapping match (discarded, reported if " 279 << "-vv)\n" 280 << " - "; 281 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; 282 OS << " marks search range when no match is found, such as:\n" 283 << " - CHECK-NEXT not found (error)\n" 284 << " - CHECK-NOT not found (success, reported if -vv)\n" 285 << " - CHECK-DAG not found after discarded matches (error)\n" 286 << " - "; 287 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; 288 OS << " marks fuzzy match when no match is found\n"; 289 290 // Elided lines. 291 OS << " - "; 292 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "..."; 293 OS << " indicates elided input lines and annotations, as specified by\n" 294 << " -dump-input-filter and -dump-input-context\n"; 295 296 // Colors. 297 OS << " - colors "; 298 WithColor(OS, raw_ostream::GREEN, true) << "success"; 299 OS << ", "; 300 WithColor(OS, raw_ostream::RED, true) << "error"; 301 OS << ", "; 302 WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; 303 OS << ", "; 304 WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; 305 OS << ", "; 306 WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; 307 OS << "\n"; 308 } 309 310 /// An annotation for a single input line. 311 struct InputAnnotation { 312 /// The index of the match result across all checks 313 unsigned DiagIndex; 314 /// The label for this annotation. 315 std::string Label; 316 /// Is this the initial fragment of a diagnostic that has been broken across 317 /// multiple lines? 318 bool IsFirstLine; 319 /// What input line (one-origin indexing) this annotation marks. This might 320 /// be different from the starting line of the original diagnostic if 321 /// !IsFirstLine. 322 unsigned InputLine; 323 /// The column range (one-origin indexing, open end) in which to mark the 324 /// input line. If InputEndCol is UINT_MAX, treat it as the last column 325 /// before the newline. 326 unsigned InputStartCol, InputEndCol; 327 /// The marker to use. 328 MarkerStyle Marker; 329 /// Whether this annotation represents a good match for an expected pattern. 330 bool FoundAndExpectedMatch; 331 }; 332 333 /// Get an abbreviation for the check type. 334 static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { 335 switch (Ty) { 336 case Check::CheckPlain: 337 if (Ty.getCount() > 1) 338 return "count"; 339 return "check"; 340 case Check::CheckNext: 341 return "next"; 342 case Check::CheckSame: 343 return "same"; 344 case Check::CheckNot: 345 return "not"; 346 case Check::CheckDAG: 347 return "dag"; 348 case Check::CheckLabel: 349 return "label"; 350 case Check::CheckEmpty: 351 return "empty"; 352 case Check::CheckComment: 353 return "com"; 354 case Check::CheckEOF: 355 return "eof"; 356 case Check::CheckBadNot: 357 return "bad-not"; 358 case Check::CheckBadCount: 359 return "bad-count"; 360 case Check::CheckNone: 361 llvm_unreachable("invalid FileCheckType"); 362 } 363 llvm_unreachable("unknown FileCheckType"); 364 } 365 366 static void 367 BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID, 368 const std::pair<unsigned, unsigned> &ImpPatBufferIDRange, 369 const std::vector<FileCheckDiag> &Diags, 370 std::vector<InputAnnotation> &Annotations, 371 unsigned &LabelWidth) { 372 // How many diagnostics have we seen so far? 373 unsigned DiagCount = 0; 374 // How many diagnostics has the current check seen so far? 375 unsigned CheckDiagCount = 0; 376 // What's the widest label? 377 LabelWidth = 0; 378 for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; 379 ++DiagItr) { 380 InputAnnotation A; 381 A.DiagIndex = DiagCount++; 382 383 // Build label, which uniquely identifies this check result. 384 unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc); 385 auto CheckLineAndCol = 386 SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID); 387 llvm::raw_string_ostream Label(A.Label); 388 Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"; 389 if (CheckBufferID == CheckFileBufferID) 390 Label << CheckLineAndCol.first; 391 else if (ImpPatBufferIDRange.first <= CheckBufferID && 392 CheckBufferID < ImpPatBufferIDRange.second) 393 Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1); 394 else 395 llvm_unreachable("expected diagnostic's check location to be either in " 396 "the check file or for an implicit pattern"); 397 unsigned CheckDiagIndex = UINT_MAX; 398 auto DiagNext = std::next(DiagItr); 399 if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && 400 DiagItr->CheckLoc == DiagNext->CheckLoc) 401 CheckDiagIndex = CheckDiagCount++; 402 else if (CheckDiagCount) { 403 CheckDiagIndex = CheckDiagCount; 404 CheckDiagCount = 0; 405 } 406 if (CheckDiagIndex != UINT_MAX) 407 Label << "'" << CheckDiagIndex; 408 Label.flush(); 409 LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); 410 411 A.Marker = GetMarker(DiagItr->MatchTy); 412 if (!DiagItr->Note.empty()) { 413 A.Marker.Note = DiagItr->Note; 414 // It's less confusing if notes that don't actually have ranges don't have 415 // markers. For example, a marker for 'with "VAR" equal to "5"' would 416 // seem to indicate where "VAR" matches, but the location we actually have 417 // for the marker simply points to the start of the match/search range for 418 // the full pattern of which the substitution is potentially just one 419 // component. 420 if (DiagItr->InputStartLine == DiagItr->InputEndLine && 421 DiagItr->InputStartCol == DiagItr->InputEndCol) 422 A.Marker.Lead = ' '; 423 } 424 A.FoundAndExpectedMatch = 425 DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; 426 427 // Compute the mark location, and break annotation into multiple 428 // annotations if it spans multiple lines. 429 A.IsFirstLine = true; 430 A.InputLine = DiagItr->InputStartLine; 431 A.InputStartCol = DiagItr->InputStartCol; 432 if (DiagItr->InputStartLine == DiagItr->InputEndLine) { 433 // Sometimes ranges are empty in order to indicate a specific point, but 434 // that would mean nothing would be marked, so adjust the range to 435 // include the following character. 436 A.InputEndCol = 437 std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); 438 Annotations.push_back(A); 439 } else { 440 assert(DiagItr->InputStartLine < DiagItr->InputEndLine && 441 "expected input range not to be inverted"); 442 A.InputEndCol = UINT_MAX; 443 Annotations.push_back(A); 444 for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; 445 L <= E; ++L) { 446 // If a range ends before the first column on a line, then it has no 447 // characters on that line, so there's nothing to render. 448 if (DiagItr->InputEndCol == 1 && L == E) 449 break; 450 InputAnnotation B; 451 B.DiagIndex = A.DiagIndex; 452 B.Label = A.Label; 453 B.IsFirstLine = false; 454 B.InputLine = L; 455 B.Marker = A.Marker; 456 B.Marker.Lead = '~'; 457 B.Marker.Note = ""; 458 B.InputStartCol = 1; 459 if (L != E) 460 B.InputEndCol = UINT_MAX; 461 else 462 B.InputEndCol = DiagItr->InputEndCol; 463 B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; 464 Annotations.push_back(B); 465 } 466 } 467 } 468 } 469 470 static unsigned FindInputLineInFilter( 471 DumpInputFilterValue DumpInputFilter, unsigned CurInputLine, 472 const std::vector<InputAnnotation>::iterator &AnnotationBeg, 473 const std::vector<InputAnnotation>::iterator &AnnotationEnd) { 474 if (DumpInputFilter == DumpInputFilterAll) 475 return CurInputLine; 476 for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd; 477 ++AnnotationItr) { 478 switch (DumpInputFilter) { 479 case DumpInputFilterAll: 480 llvm_unreachable("unexpected DumpInputFilterAll"); 481 break; 482 case DumpInputFilterAnnotationFull: 483 return AnnotationItr->InputLine; 484 case DumpInputFilterAnnotation: 485 if (AnnotationItr->IsFirstLine) 486 return AnnotationItr->InputLine; 487 break; 488 case DumpInputFilterError: 489 if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError) 490 return AnnotationItr->InputLine; 491 break; 492 } 493 } 494 return UINT_MAX; 495 } 496 497 /// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would 498 /// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either 499 /// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing. 500 static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines, 501 unsigned LabelWidth) { 502 if (ElidedLines.empty()) 503 return; 504 unsigned EllipsisLines = 3; 505 if (EllipsisLines < StringRef(ElidedLines).count('\n')) { 506 for (unsigned i = 0; i < EllipsisLines; ++i) { 507 WithColor(OS, raw_ostream::BLACK, /*Bold=*/true) 508 << right_justify(".", LabelWidth); 509 OS << '\n'; 510 } 511 } else 512 OS << ElidedLines; 513 ElidedLines.clear(); 514 } 515 516 static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, 517 DumpInputFilterValue DumpInputFilter, 518 unsigned DumpInputContext, 519 StringRef InputFileText, 520 std::vector<InputAnnotation> &Annotations, 521 unsigned LabelWidth) { 522 OS << "Input was:\n<<<<<<\n"; 523 524 // Sort annotations. 525 llvm::sort(Annotations, 526 [](const InputAnnotation &A, const InputAnnotation &B) { 527 // 1. Sort annotations in the order of the input lines. 528 // 529 // This makes it easier to find relevant annotations while 530 // iterating input lines in the implementation below. FileCheck 531 // does not always produce diagnostics in the order of input 532 // lines due to, for example, CHECK-DAG and CHECK-NOT. 533 if (A.InputLine != B.InputLine) 534 return A.InputLine < B.InputLine; 535 // 2. Sort annotations in the temporal order FileCheck produced 536 // their associated diagnostics. 537 // 538 // This sort offers several benefits: 539 // 540 // A. On a single input line, the order of annotations reflects 541 // the FileCheck logic for processing directives/patterns. 542 // This can be helpful in understanding cases in which the 543 // order of the associated directives/patterns in the check 544 // file or on the command line either (i) does not match the 545 // temporal order in which FileCheck looks for matches for the 546 // directives/patterns (due to, for example, CHECK-LABEL, 547 // CHECK-NOT, or `--implicit-check-not`) or (ii) does match 548 // that order but does not match the order of those 549 // diagnostics along an input line (due to, for example, 550 // CHECK-DAG). 551 // 552 // On the other hand, because our presentation format presents 553 // input lines in order, there's no clear way to offer the 554 // same benefit across input lines. For consistency, it might 555 // then seem worthwhile to have annotations on a single line 556 // also sorted in input order (that is, by input column). 557 // However, in practice, this appears to be more confusing 558 // than helpful. Perhaps it's intuitive to expect annotations 559 // to be listed in the temporal order in which they were 560 // produced except in cases the presentation format obviously 561 // and inherently cannot support it (that is, across input 562 // lines). 563 // 564 // B. When diagnostics' annotations are split among multiple 565 // input lines, the user must track them from one input line 566 // to the next. One property of the sort chosen here is that 567 // it facilitates the user in this regard by ensuring the 568 // following: when comparing any two input lines, a 569 // diagnostic's annotations are sorted in the same position 570 // relative to all other diagnostics' annotations. 571 return A.DiagIndex < B.DiagIndex; 572 }); 573 574 // Compute the width of the label column. 575 const unsigned char *InputFilePtr = InputFileText.bytes_begin(), 576 *InputFileEnd = InputFileText.bytes_end(); 577 unsigned LineCount = InputFileText.count('\n'); 578 if (InputFileEnd[-1] != '\n') 579 ++LineCount; 580 unsigned LineNoWidth = std::log10(LineCount) + 1; 581 // +3 below adds spaces (1) to the left of the (right-aligned) line numbers 582 // on input lines and (2) to the right of the (left-aligned) labels on 583 // annotation lines so that input lines and annotation lines are more 584 // visually distinct. For example, the spaces on the annotation lines ensure 585 // that input line numbers and check directive line numbers never align 586 // horizontally. Those line numbers might not even be for the same file. 587 // One space would be enough to achieve that, but more makes it even easier 588 // to see. 589 LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; 590 591 // Print annotated input lines. 592 unsigned PrevLineInFilter = 0; // 0 means none so far 593 unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none 594 std::string ElidedLines; 595 raw_string_ostream ElidedLinesOS(ElidedLines); 596 ColorMode TheColorMode = 597 WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable; 598 if (TheColorMode == ColorMode::Enable) 599 ElidedLinesOS.enable_colors(true); 600 auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); 601 for (unsigned Line = 1; 602 InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; 603 ++Line) { 604 const unsigned char *InputFileLine = InputFilePtr; 605 606 // Compute the previous and next line included by the filter. 607 if (NextLineInFilter < Line) 608 NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line, 609 AnnotationItr, AnnotationEnd); 610 assert(NextLineInFilter && "expected NextLineInFilter to be computed"); 611 if (NextLineInFilter == Line) 612 PrevLineInFilter = Line; 613 614 // Elide this input line and its annotations if it's not within the 615 // context specified by -dump-input-context of an input line included by 616 // -dump-input-filter. However, in case the resulting ellipsis would occupy 617 // more lines than the input lines and annotations it elides, buffer the 618 // elided lines and annotations so we can print them instead. 619 raw_ostream *LineOS = &OS; 620 if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) && 621 (NextLineInFilter == UINT_MAX || 622 Line + DumpInputContext < NextLineInFilter)) 623 LineOS = &ElidedLinesOS; 624 else { 625 LineOS = &OS; 626 DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); 627 } 628 629 // Print right-aligned line number. 630 WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false, 631 TheColorMode) 632 << format_decimal(Line, LabelWidth) << ": "; 633 634 // For the case where -v and colors are enabled, find the annotations for 635 // good matches for expected patterns in order to highlight everything 636 // else in the line. There are no such annotations if -v is disabled. 637 std::vector<InputAnnotation> FoundAndExpectedMatches; 638 if (Req.Verbose && TheColorMode == ColorMode::Enable) { 639 for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; 640 ++I) { 641 if (I->FoundAndExpectedMatch) 642 FoundAndExpectedMatches.push_back(*I); 643 } 644 } 645 646 // Print numbered line with highlighting where there are no matches for 647 // expected patterns. 648 bool Newline = false; 649 { 650 WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false, 651 /*BG=*/false, TheColorMode); 652 bool InMatch = false; 653 if (Req.Verbose) 654 COS.changeColor(raw_ostream::CYAN, true, true); 655 for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { 656 bool WasInMatch = InMatch; 657 InMatch = false; 658 for (auto M : FoundAndExpectedMatches) { 659 if (M.InputStartCol <= Col && Col < M.InputEndCol) { 660 InMatch = true; 661 break; 662 } 663 } 664 if (!WasInMatch && InMatch) 665 COS.resetColor(); 666 else if (WasInMatch && !InMatch) 667 COS.changeColor(raw_ostream::CYAN, true, true); 668 if (*InputFilePtr == '\n') { 669 Newline = true; 670 COS << ' '; 671 } else 672 COS << *InputFilePtr; 673 ++InputFilePtr; 674 } 675 } 676 *LineOS << '\n'; 677 unsigned InputLineWidth = InputFilePtr - InputFileLine; 678 679 // Print any annotations. 680 while (AnnotationItr != AnnotationEnd && 681 AnnotationItr->InputLine == Line) { 682 WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true, 683 /*BG=*/false, TheColorMode); 684 // The two spaces below are where the ": " appears on input lines. 685 COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; 686 unsigned Col; 687 for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) 688 COS << ' '; 689 COS << AnnotationItr->Marker.Lead; 690 // If InputEndCol=UINT_MAX, stop at InputLineWidth. 691 for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; 692 ++Col) 693 COS << '~'; 694 const std::string &Note = AnnotationItr->Marker.Note; 695 if (!Note.empty()) { 696 // Put the note at the end of the input line. If we were to instead 697 // put the note right after the marker, subsequent annotations for the 698 // same input line might appear to mark this note instead of the input 699 // line. 700 for (; Col <= InputLineWidth; ++Col) 701 COS << ' '; 702 COS << ' ' << Note; 703 } 704 COS << '\n'; 705 ++AnnotationItr; 706 } 707 } 708 DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); 709 710 OS << ">>>>>>\n"; 711 } 712 713 int main(int argc, char **argv) { 714 // Enable use of ANSI color codes because FileCheck is using them to 715 // highlight text. 716 llvm::sys::Process::UseANSIEscapeCodes(true); 717 718 InitLLVM X(argc, argv); 719 cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, 720 "FILECHECK_OPTS"); 721 722 // Select -dump-input* values. The -help documentation specifies the default 723 // value and which value to choose if an option is specified multiple times. 724 // In the latter case, the general rule of thumb is to choose the value that 725 // provides the most information. 726 DumpInputValue DumpInput = 727 DumpInputs.empty() 728 ? DumpInputFail 729 : *std::max_element(DumpInputs.begin(), DumpInputs.end()); 730 DumpInputFilterValue DumpInputFilter; 731 if (DumpInputFilters.empty()) 732 DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll 733 : DumpInputFilterError; 734 else 735 DumpInputFilter = 736 *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end()); 737 unsigned DumpInputContext = DumpInputContexts.empty() 738 ? 5 739 : *std::max_element(DumpInputContexts.begin(), 740 DumpInputContexts.end()); 741 742 if (DumpInput == DumpInputHelp) { 743 DumpInputAnnotationHelp(outs()); 744 return 0; 745 } 746 if (CheckFilename.empty()) { 747 errs() << "<check-file> not specified\n"; 748 return 2; 749 } 750 751 FileCheckRequest Req; 752 append_range(Req.CheckPrefixes, CheckPrefixes); 753 754 append_range(Req.CommentPrefixes, CommentPrefixes); 755 756 append_range(Req.ImplicitCheckNot, ImplicitCheckNot); 757 758 bool GlobalDefineError = false; 759 for (StringRef G : GlobalDefines) { 760 size_t EqIdx = G.find('='); 761 if (EqIdx == std::string::npos) { 762 errs() << "Missing equal sign in command-line definition '-D" << G 763 << "'\n"; 764 GlobalDefineError = true; 765 continue; 766 } 767 if (EqIdx == 0) { 768 errs() << "Missing variable name in command-line definition '-D" << G 769 << "'\n"; 770 GlobalDefineError = true; 771 continue; 772 } 773 Req.GlobalDefines.push_back(G); 774 } 775 if (GlobalDefineError) 776 return 2; 777 778 Req.AllowEmptyInput = AllowEmptyInput; 779 Req.AllowUnusedPrefixes = AllowUnusedPrefixes; 780 Req.EnableVarScope = EnableVarScope; 781 Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; 782 Req.Verbose = Verbose; 783 Req.VerboseVerbose = VerboseVerbose; 784 Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; 785 Req.MatchFullLines = MatchFullLines; 786 Req.IgnoreCase = IgnoreCase; 787 788 if (VerboseVerbose) 789 Req.Verbose = true; 790 791 FileCheck FC(Req); 792 if (!FC.ValidateCheckPrefixes()) 793 return 2; 794 795 Regex PrefixRE = FC.buildCheckPrefixRegex(); 796 std::string REError; 797 if (!PrefixRE.isValid(REError)) { 798 errs() << "Unable to combine check-prefix strings into a prefix regular " 799 "expression! This is likely a bug in FileCheck's verification of " 800 "the check-prefix strings. Regular expression parsing failed " 801 "with the following error: " 802 << REError << "\n"; 803 return 2; 804 } 805 806 SourceMgr SM; 807 808 // Read the expected strings from the check file. 809 ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr = 810 MemoryBuffer::getFileOrSTDIN(CheckFilename); 811 if (std::error_code EC = CheckFileOrErr.getError()) { 812 errs() << "Could not open check file '" << CheckFilename 813 << "': " << EC.message() << '\n'; 814 return 2; 815 } 816 MemoryBuffer &CheckFile = *CheckFileOrErr.get(); 817 818 SmallString<4096> CheckFileBuffer; 819 StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); 820 821 unsigned CheckFileBufferID = 822 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 823 CheckFileText, CheckFile.getBufferIdentifier()), 824 SMLoc()); 825 826 std::pair<unsigned, unsigned> ImpPatBufferIDRange; 827 if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange)) 828 return 2; 829 830 // Open the file to check and add it to SourceMgr. 831 ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr = 832 MemoryBuffer::getFileOrSTDIN(InputFilename); 833 if (InputFilename == "-") 834 InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages 835 if (std::error_code EC = InputFileOrErr.getError()) { 836 errs() << "Could not open input file '" << InputFilename 837 << "': " << EC.message() << '\n'; 838 return 2; 839 } 840 MemoryBuffer &InputFile = *InputFileOrErr.get(); 841 842 if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { 843 errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; 844 DumpCommandLine(argc, argv); 845 return 2; 846 } 847 848 SmallString<4096> InputFileBuffer; 849 StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); 850 851 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 852 InputFileText, InputFile.getBufferIdentifier()), 853 SMLoc()); 854 855 std::vector<FileCheckDiag> Diags; 856 int ExitCode = FC.checkInput(SM, InputFileText, 857 DumpInput == DumpInputNever ? nullptr : &Diags) 858 ? EXIT_SUCCESS 859 : 1; 860 if (DumpInput == DumpInputAlways || 861 (ExitCode == 1 && DumpInput == DumpInputFail)) { 862 errs() << "\n" 863 << "Input file: " << InputFilename << "\n" 864 << "Check file: " << CheckFilename << "\n" 865 << "\n" 866 << "-dump-input=help explains the following input dump.\n" 867 << "\n"; 868 std::vector<InputAnnotation> Annotations; 869 unsigned LabelWidth; 870 BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags, 871 Annotations, LabelWidth); 872 DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext, 873 InputFileText, Annotations, LabelWidth); 874 } 875 876 return ExitCode; 877 } 878