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