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