1 //===- FileCheck.cpp - Check that File's Contents match what is expected --===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // FileCheck does a line-by line check of a file that validates whether it 11 // contains the expected content. This is useful for regression tests etc. 12 // 13 // This program exits with an exit status of 2 on error, exit status of 0 if 14 // the file matched the expected contents, and exit status of 1 if it did not 15 // contain the expected contents. 16 // 17 //===----------------------------------------------------------------------===// 18 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 "llvm/Support/FileCheck.h" 25 using namespace llvm; 26 27 static cl::opt<std::string> 28 CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional); 29 30 static cl::opt<std::string> 31 InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), 32 cl::init("-"), cl::value_desc("filename")); 33 34 static cl::list<std::string> CheckPrefixes( 35 "check-prefix", 36 cl::desc("Prefix to use from check file (defaults to 'CHECK')")); 37 static cl::alias CheckPrefixesAlias( 38 "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, 39 cl::NotHidden, 40 cl::desc( 41 "Alias for -check-prefix permitting multiple comma separated values")); 42 43 static cl::opt<bool> NoCanonicalizeWhiteSpace( 44 "strict-whitespace", 45 cl::desc("Do not treat all horizontal whitespace as equivalent")); 46 47 static cl::list<std::string> ImplicitCheckNot( 48 "implicit-check-not", 49 cl::desc("Add an implicit negative check with this pattern to every\n" 50 "positive check. This can be used to ensure that no instances of\n" 51 "this pattern occur which are not matched by a positive pattern"), 52 cl::value_desc("pattern")); 53 54 static cl::list<std::string> GlobalDefines("D", cl::Prefix, 55 cl::desc("Define a variable to be used in capture patterns."), 56 cl::value_desc("VAR=VALUE")); 57 58 static cl::opt<bool> AllowEmptyInput( 59 "allow-empty", cl::init(false), 60 cl::desc("Allow the input file to be empty. This is useful when making\n" 61 "checks that some error message does not occur, for example.")); 62 63 static cl::opt<bool> MatchFullLines( 64 "match-full-lines", cl::init(false), 65 cl::desc("Require all positive matches to cover an entire input line.\n" 66 "Allows leading and trailing whitespace if --strict-whitespace\n" 67 "is not also passed.")); 68 69 static cl::opt<bool> EnableVarScope( 70 "enable-var-scope", cl::init(false), 71 cl::desc("Enables scope for regex variables. Variables with names that\n" 72 "do not start with '$' will be reset at the beginning of\n" 73 "each CHECK-LABEL block.")); 74 75 static cl::opt<bool> AllowDeprecatedDagOverlap( 76 "allow-deprecated-dag-overlap", cl::init(false), 77 cl::desc("Enable overlapping among matches in a group of consecutive\n" 78 "CHECK-DAG directives. This option is deprecated and is only\n" 79 "provided for convenience as old tests are migrated to the new\n" 80 "non-overlapping CHECK-DAG implementation.\n")); 81 82 static cl::opt<bool> Verbose("v", cl::init(false), 83 cl::desc("Print directive pattern matches.\n")); 84 85 static cl::opt<bool> VerboseVerbose( 86 "vv", cl::init(false), 87 cl::desc("Print information helpful in diagnosing internal FileCheck\n" 88 "issues. Implies -v.\n")); 89 static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; 90 91 static cl::opt<bool> DumpInputOnFailure( 92 "dump-input-on-failure", cl::init(std::getenv(DumpInputEnv)), 93 cl::desc("Dump original input to stderr before failing.\n" 94 "The value can be also controlled using\n" 95 "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" 96 "This option is deprecated in favor of -dump-input=fail.\n")); 97 98 enum DumpInputValue { 99 DumpInputDefault, 100 DumpInputHelp, 101 DumpInputNever, 102 DumpInputFail, 103 DumpInputAlways 104 }; 105 106 static cl::opt<DumpInputValue> DumpInput( 107 "dump-input", cl::init(DumpInputDefault), 108 cl::desc("Dump input to stderr, adding annotations representing\n" 109 " currently enabled diagnostics\n"), 110 cl::value_desc("mode"), 111 cl::values(clEnumValN(DumpInputHelp, "help", 112 "Explain dump format and quit"), 113 clEnumValN(DumpInputNever, "never", "Never dump input"), 114 clEnumValN(DumpInputFail, "fail", "Dump input on failure"), 115 clEnumValN(DumpInputAlways, "always", "Always dump input"))); 116 117 typedef cl::list<std::string>::const_iterator prefix_iterator; 118 119 120 121 122 123 124 125 static void DumpCommandLine(int argc, char **argv) { 126 errs() << "FileCheck command line: "; 127 for (int I = 0; I < argc; I++) 128 errs() << " " << argv[I]; 129 errs() << "\n"; 130 } 131 132 struct MarkerStyle { 133 /// The starting char (before tildes) for marking the line. 134 char Lead; 135 /// What color to use for this annotation. 136 raw_ostream::Colors Color; 137 /// A note to follow the marker, or empty string if none. 138 std::string Note; 139 MarkerStyle() {} 140 MarkerStyle(char Lead, raw_ostream::Colors Color, const std::string &Note) 141 : Lead(Lead), Color(Color), Note(Note) {} 142 }; 143 144 static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { 145 switch (MatchTy) { 146 case FileCheckDiag::MatchNoneButExpected: 147 return MarkerStyle('X', raw_ostream::RED, "error: no match found"); 148 case FileCheckDiag::MatchFuzzy: 149 return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); 150 case FileCheckDiag::MatchTypeCount: 151 llvm_unreachable_internal("unexpected match type"); 152 } 153 llvm_unreachable_internal("unexpected match type"); 154 } 155 156 static void DumpInputAnnotationHelp(raw_ostream &OS) { 157 OS << "The following description was requested by -dump-input=help to\n" 158 << "explain the input annotations printed by -dump-input=always and\n" 159 << "-dump-input=fail:\n\n"; 160 161 // Labels for input lines. 162 OS << " - "; 163 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; 164 OS << " labels line number L of the input file\n"; 165 166 // Labels for annotation lines. 167 OS << " - "; 168 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; 169 OS << " labels the only match result for a pattern of type T from " 170 << "line L of\n" 171 << " the check file\n"; 172 OS << " - "; 173 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; 174 OS << " labels the Nth match result for a pattern of type T from line " 175 << "L of\n" 176 << " the check file\n"; 177 178 // Markers on annotation lines. 179 OS << " - "; 180 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; 181 OS << " marks search range when no match is found\n" 182 << " - "; 183 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; 184 OS << " marks fuzzy match when no match is found\n"; 185 186 // Colors. 187 OS << " - colors "; 188 WithColor(OS, raw_ostream::RED, true) << "error"; 189 OS << ", "; 190 WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; 191 OS << "\n\n" 192 << "If you are not seeing color above or in input dumps, try: -color\n"; 193 } 194 195 /// An annotation for a single input line. 196 struct InputAnnotation { 197 /// The check file line (one-origin indexing) where the directive that 198 /// produced this annotation is located. 199 unsigned CheckLine; 200 /// The index of the match result for this check. 201 unsigned CheckDiagIndex; 202 /// The label for this annotation. 203 std::string Label; 204 /// What input line (one-origin indexing) this annotation marks. This might 205 /// be different from the starting line of the original diagnostic if this is 206 /// a non-initial fragment of a diagnostic that has been broken across 207 /// multiple lines. 208 unsigned InputLine; 209 /// The column range (one-origin indexing, open end) in which to to mark the 210 /// input line. If InputEndCol is UINT_MAX, treat it as the last column 211 /// before the newline. 212 unsigned InputStartCol, InputEndCol; 213 /// The marker to use. 214 MarkerStyle Marker; 215 }; 216 217 /// Get an abbreviation for the check type. 218 std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { 219 switch (Ty) { 220 case Check::CheckPlain: 221 if (Ty.getCount() > 1) 222 return "count"; 223 return "check"; 224 case Check::CheckNext: 225 return "next"; 226 case Check::CheckSame: 227 return "same"; 228 case Check::CheckNot: 229 return "not"; 230 case Check::CheckDAG: 231 return "dag"; 232 case Check::CheckLabel: 233 return "label"; 234 case Check::CheckEmpty: 235 return "empty"; 236 case Check::CheckEOF: 237 return "eof"; 238 case Check::CheckBadNot: 239 return "bad-not"; 240 case Check::CheckBadCount: 241 return "bad-count"; 242 case Check::CheckNone: 243 llvm_unreachable("invalid FileCheckType"); 244 } 245 llvm_unreachable("unknown FileCheckType"); 246 } 247 248 static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags, 249 std::vector<InputAnnotation> &Annotations, 250 unsigned &LabelWidth) { 251 // How many diagnostics has the current check seen so far? 252 unsigned CheckDiagCount = 0; 253 // What's the widest label? 254 LabelWidth = 0; 255 for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; 256 ++DiagItr) { 257 InputAnnotation A; 258 259 // Build label, which uniquely identifies this check result. 260 A.CheckLine = DiagItr->CheckLine; 261 llvm::raw_string_ostream Label(A.Label); 262 Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" 263 << DiagItr->CheckLine; 264 A.CheckDiagIndex = UINT_MAX; 265 auto DiagNext = std::next(DiagItr); 266 if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && 267 DiagItr->CheckLine == DiagNext->CheckLine) 268 A.CheckDiagIndex = CheckDiagCount++; 269 else if (CheckDiagCount) { 270 A.CheckDiagIndex = CheckDiagCount; 271 CheckDiagCount = 0; 272 } 273 if (A.CheckDiagIndex != UINT_MAX) 274 Label << "'" << A.CheckDiagIndex; 275 else 276 A.CheckDiagIndex = 0; 277 Label.flush(); 278 LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); 279 280 MarkerStyle Marker = GetMarker(DiagItr->MatchTy); 281 A.Marker = Marker; 282 283 // Compute the mark location, and break annotation into multiple 284 // annotations if it spans multiple lines. 285 A.InputLine = DiagItr->InputStartLine; 286 A.InputStartCol = DiagItr->InputStartCol; 287 if (DiagItr->InputStartLine == DiagItr->InputEndLine) { 288 // Sometimes ranges are empty in order to indicate a specific point, but 289 // that would mean nothing would be marked, so adjust the range to 290 // include the following character. 291 A.InputEndCol = 292 std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); 293 Annotations.push_back(A); 294 } else { 295 assert(DiagItr->InputStartLine < DiagItr->InputEndLine && 296 "expected input range not to be inverted"); 297 A.InputEndCol = UINT_MAX; 298 A.Marker.Note = ""; 299 Annotations.push_back(A); 300 for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; 301 L <= E; ++L) { 302 // If a range ends before the first column on a line, then it has no 303 // characters on that line, so there's nothing to render. 304 if (DiagItr->InputEndCol == 1 && L == E) { 305 Annotations.back().Marker.Note = Marker.Note; 306 break; 307 } 308 InputAnnotation B; 309 B.CheckLine = A.CheckLine; 310 B.CheckDiagIndex = A.CheckDiagIndex; 311 B.Label = A.Label; 312 B.InputLine = L; 313 B.Marker = Marker; 314 B.Marker.Lead = '~'; 315 B.InputStartCol = 1; 316 if (L != E) { 317 B.InputEndCol = UINT_MAX; 318 B.Marker.Note = ""; 319 } else 320 B.InputEndCol = DiagItr->InputEndCol; 321 Annotations.push_back(B); 322 } 323 } 324 } 325 } 326 327 static void DumpAnnotatedInput( 328 raw_ostream &OS, StringRef InputFileText, 329 std::vector<InputAnnotation> &Annotations, unsigned LabelWidth) { 330 OS << "Full input was:\n<<<<<<\n"; 331 332 // Sort annotations. 333 // 334 // First, sort in the order of input lines to make it easier to find relevant 335 // annotations while iterating input lines in the implementation below. 336 // FileCheck diagnostics are not always reported and recorded in the order of 337 // input lines due to, for example, CHECK-DAG and CHECK-NOT. 338 // 339 // Second, for annotations for the same input line, sort in the order of the 340 // FileCheck directive's line in the check file (where there's at most one 341 // directive per line) and then by the index of the match result for that 342 // directive. The rationale of this choice is that, for any input line, this 343 // sort establishes a total order of annotations that, with respect to match 344 // results, is consistent across multiple lines, thus making match results 345 // easier to track from one line to the next when they span multiple lines. 346 std::sort(Annotations.begin(), Annotations.end(), 347 [](const InputAnnotation &A, const InputAnnotation &B) { 348 if (A.InputLine != B.InputLine) 349 return A.InputLine < B.InputLine; 350 if (A.CheckLine != B.CheckLine) 351 return A.CheckLine < B.CheckLine; 352 assert(A.CheckDiagIndex != B.CheckDiagIndex && 353 "expected diagnostic indices to be unique within a " 354 " check line"); 355 return A.CheckDiagIndex < B.CheckDiagIndex; 356 }); 357 358 // Compute the width of the label column. 359 const unsigned char *InputFilePtr = InputFileText.bytes_begin(), 360 *InputFileEnd = InputFileText.bytes_end(); 361 unsigned LineCount = InputFileText.count('\n'); 362 if (InputFileEnd[-1] != '\n') 363 ++LineCount; 364 unsigned LineNoWidth = log10(LineCount) + 1; 365 // +3 below adds spaces (1) to the left of the (right-aligned) line numbers 366 // on input lines and (2) to the right of the (left-aligned) labels on 367 // annotation lines so that input lines and annotation lines are more 368 // visually distinct. For example, the spaces on the annotation lines ensure 369 // that input line numbers and check directive line numbers never align 370 // horizontally. Those line numbers might not even be for the same file. 371 // One space would be enough to achieve that, but more makes it even easier 372 // to see. 373 LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; 374 375 // Print annotated input lines. 376 auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); 377 for (unsigned Line = 1; 378 InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; 379 ++Line) { 380 const unsigned char *InputFileLine = InputFilePtr; 381 382 // Print right-aligned line number. 383 WithColor(OS, raw_ostream::BLACK, true) 384 << format_decimal(Line, LabelWidth) << ": "; 385 386 // Print numbered line. 387 bool Newline = false; 388 while (InputFilePtr != InputFileEnd && !Newline) { 389 if (*InputFilePtr == '\n') 390 Newline = true; 391 else 392 OS << *InputFilePtr; 393 ++InputFilePtr; 394 } 395 OS << '\n'; 396 unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; 397 398 // Print any annotations. 399 while (AnnotationItr != AnnotationEnd && 400 AnnotationItr->InputLine == Line) { 401 WithColor COS(OS, AnnotationItr->Marker.Color, true); 402 // The two spaces below are where the ": " appears on input lines. 403 COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; 404 unsigned Col; 405 for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) 406 COS << ' '; 407 COS << AnnotationItr->Marker.Lead; 408 // If InputEndCol=UINT_MAX, stop at InputLineWidth. 409 for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; 410 ++Col) 411 COS << '~'; 412 const std::string &Note = AnnotationItr->Marker.Note; 413 if (!Note.empty()) { 414 // Put the note at the end of the input line. If we were to instead 415 // put the note right after the marker, subsequent annotations for the 416 // same input line might appear to mark this note instead of the input 417 // line. 418 for (; Col <= InputLineWidth; ++Col) 419 COS << ' '; 420 COS << ' ' << Note; 421 } 422 COS << '\n'; 423 ++AnnotationItr; 424 } 425 } 426 427 OS << ">>>>>>\n"; 428 } 429 430 int main(int argc, char **argv) { 431 // Enable use of ANSI color codes because FileCheck is using them to 432 // highlight text. 433 llvm::sys::Process::UseANSIEscapeCodes(true); 434 435 InitLLVM X(argc, argv); 436 cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, 437 "FILECHECK_OPTS"); 438 if (DumpInput == DumpInputHelp) { 439 DumpInputAnnotationHelp(outs()); 440 return 0; 441 } 442 if (CheckFilename.empty()) { 443 errs() << "<check-file> not specified\n"; 444 return 2; 445 } 446 447 FileCheckRequest Req; 448 for (auto Prefix : CheckPrefixes) 449 Req.CheckPrefixes.push_back(Prefix); 450 451 for (auto CheckNot : ImplicitCheckNot) 452 Req.ImplicitCheckNot.push_back(CheckNot); 453 454 for (auto G : GlobalDefines) 455 Req.GlobalDefines.push_back(G); 456 457 Req.AllowEmptyInput = AllowEmptyInput; 458 Req.EnableVarScope = EnableVarScope; 459 Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; 460 Req.Verbose = Verbose; 461 Req.VerboseVerbose = VerboseVerbose; 462 Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; 463 Req.MatchFullLines = MatchFullLines; 464 465 if (VerboseVerbose) 466 Req.Verbose = true; 467 468 FileCheck FC(Req); 469 if (!FC.ValidateCheckPrefixes()) { 470 errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " 471 "start with a letter and contain only alphanumeric characters, " 472 "hyphens and underscores\n"; 473 return 2; 474 } 475 476 Regex PrefixRE = FC.buildCheckPrefixRegex(); 477 std::string REError; 478 if (!PrefixRE.isValid(REError)) { 479 errs() << "Unable to combine check-prefix strings into a prefix regular " 480 "expression! This is likely a bug in FileCheck's verification of " 481 "the check-prefix strings. Regular expression parsing failed " 482 "with the following error: " 483 << REError << "\n"; 484 return 2; 485 } 486 487 SourceMgr SM; 488 489 // Read the expected strings from the check file. 490 ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr = 491 MemoryBuffer::getFileOrSTDIN(CheckFilename); 492 if (std::error_code EC = CheckFileOrErr.getError()) { 493 errs() << "Could not open check file '" << CheckFilename 494 << "': " << EC.message() << '\n'; 495 return 2; 496 } 497 MemoryBuffer &CheckFile = *CheckFileOrErr.get(); 498 499 SmallString<4096> CheckFileBuffer; 500 StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); 501 502 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 503 CheckFileText, CheckFile.getBufferIdentifier()), 504 SMLoc()); 505 506 std::vector<FileCheckString> CheckStrings; 507 if (FC.ReadCheckFile(SM, CheckFileText, PrefixRE, CheckStrings)) 508 return 2; 509 510 // Open the file to check and add it to SourceMgr. 511 ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr = 512 MemoryBuffer::getFileOrSTDIN(InputFilename); 513 if (std::error_code EC = InputFileOrErr.getError()) { 514 errs() << "Could not open input file '" << InputFilename 515 << "': " << EC.message() << '\n'; 516 return 2; 517 } 518 MemoryBuffer &InputFile = *InputFileOrErr.get(); 519 520 if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { 521 errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; 522 DumpCommandLine(argc, argv); 523 return 2; 524 } 525 526 SmallString<4096> InputFileBuffer; 527 StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); 528 529 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 530 InputFileText, InputFile.getBufferIdentifier()), 531 SMLoc()); 532 533 if (DumpInput == DumpInputDefault) 534 DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; 535 536 std::vector<FileCheckDiag> Diags; 537 int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, 538 DumpInput == DumpInputNever ? nullptr : &Diags) 539 ? EXIT_SUCCESS 540 : 1; 541 if (DumpInput == DumpInputAlways || 542 (ExitCode == 1 && DumpInput == DumpInputFail)) { 543 errs() << "\n" 544 << "Input file: " 545 << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue()) 546 << "\n" 547 << "Check file: " << CheckFilename << "\n" 548 << "\n" 549 << "-dump-input=help describes the format of the following dump.\n" 550 << "\n"; 551 std::vector<InputAnnotation> Annotations; 552 unsigned LabelWidth; 553 BuildInputAnnotations(Diags, Annotations, LabelWidth); 554 DumpAnnotatedInput(errs(), InputFileText, Annotations, LabelWidth); 555 } 556 557 return ExitCode; 558 } 559