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::MatchTypeCount: 149 llvm_unreachable_internal("unexpected match type"); 150 } 151 llvm_unreachable_internal("unexpected match type"); 152 } 153 154 static void DumpInputAnnotationHelp(raw_ostream &OS) { 155 OS << "The following description was requested by -dump-input=help to\n" 156 << "explain the input annotations printed by -dump-input=always and\n" 157 << "-dump-input=fail:\n\n"; 158 159 // Labels for input lines. 160 OS << " - "; 161 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; 162 OS << " labels line number L of the input file\n"; 163 164 // Labels for annotation lines. 165 OS << " - "; 166 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; 167 OS << " labels the match result for a pattern of type T from " 168 << "line L of\n" 169 << " the check file\n"; 170 171 // Markers on annotation lines. 172 OS << " - "; 173 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; 174 OS << " marks search range when no match is found\n"; 175 176 // Colors. 177 OS << " - colors "; 178 WithColor(OS, raw_ostream::RED, true) << "error"; 179 OS << "\n\n" 180 << "If you are not seeing color above or in input dumps, try: -color\n"; 181 } 182 183 /// An annotation for a single input line. 184 struct InputAnnotation { 185 /// The check file line (one-origin indexing) where the directive that 186 /// produced this annotation is located. 187 unsigned CheckLine; 188 /// The label for this annotation. 189 std::string Label; 190 /// What input line (one-origin indexing) this annotation marks. This might 191 /// be different from the starting line of the original diagnostic if this is 192 /// a non-initial fragment of a diagnostic that has been broken across 193 /// multiple lines. 194 unsigned InputLine; 195 /// The column range (one-origin indexing, open end) in which to to mark the 196 /// input line. If InputEndCol is UINT_MAX, treat it as the last column 197 /// before the newline. 198 unsigned InputStartCol, InputEndCol; 199 /// The marker to use. 200 MarkerStyle Marker; 201 }; 202 203 /// Get an abbreviation for the check type. 204 std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { 205 switch (Ty) { 206 case Check::CheckPlain: 207 if (Ty.getCount() > 1) 208 return "count"; 209 return "check"; 210 case Check::CheckNext: 211 return "next"; 212 case Check::CheckSame: 213 return "same"; 214 case Check::CheckNot: 215 return "not"; 216 case Check::CheckDAG: 217 return "dag"; 218 case Check::CheckLabel: 219 return "label"; 220 case Check::CheckEmpty: 221 return "empty"; 222 case Check::CheckEOF: 223 return "eof"; 224 case Check::CheckBadNot: 225 return "bad-not"; 226 case Check::CheckBadCount: 227 return "bad-count"; 228 case Check::CheckNone: 229 llvm_unreachable("invalid FileCheckType"); 230 } 231 llvm_unreachable("unknown FileCheckType"); 232 } 233 234 static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags, 235 std::vector<InputAnnotation> &Annotations, 236 unsigned &LabelWidth) { 237 // What's the widest label? 238 LabelWidth = 0; 239 for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; 240 ++DiagItr) { 241 InputAnnotation A; 242 243 // Build label, which uniquely identifies this check result. 244 A.CheckLine = DiagItr->CheckLine; 245 llvm::raw_string_ostream Label(A.Label); 246 Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" 247 << DiagItr->CheckLine; 248 Label.flush(); 249 LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); 250 251 MarkerStyle Marker = GetMarker(DiagItr->MatchTy); 252 A.Marker = Marker; 253 254 // Compute the mark location, and break annotation into multiple 255 // annotations if it spans multiple lines. 256 A.InputLine = DiagItr->InputStartLine; 257 A.InputStartCol = DiagItr->InputStartCol; 258 if (DiagItr->InputStartLine == DiagItr->InputEndLine) { 259 // Sometimes ranges are empty in order to indicate a specific point, but 260 // that would mean nothing would be marked, so adjust the range to 261 // include the following character. 262 A.InputEndCol = 263 std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); 264 Annotations.push_back(A); 265 } else { 266 assert(DiagItr->InputStartLine < DiagItr->InputEndLine && 267 "expected input range not to be inverted"); 268 A.InputEndCol = UINT_MAX; 269 A.Marker.Note = ""; 270 Annotations.push_back(A); 271 for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; 272 L <= E; ++L) { 273 // If a range ends before the first column on a line, then it has no 274 // characters on that line, so there's nothing to render. 275 if (DiagItr->InputEndCol == 1 && L == E) { 276 Annotations.back().Marker.Note = Marker.Note; 277 break; 278 } 279 InputAnnotation B; 280 B.CheckLine = A.CheckLine; 281 B.Label = A.Label; 282 B.InputLine = L; 283 B.Marker = Marker; 284 B.Marker.Lead = '~'; 285 B.InputStartCol = 1; 286 if (L != E) { 287 B.InputEndCol = UINT_MAX; 288 B.Marker.Note = ""; 289 } else 290 B.InputEndCol = DiagItr->InputEndCol; 291 Annotations.push_back(B); 292 } 293 } 294 } 295 } 296 297 static void DumpAnnotatedInput( 298 raw_ostream &OS, StringRef InputFileText, 299 std::vector<InputAnnotation> &Annotations, unsigned LabelWidth) { 300 OS << "Full input was:\n<<<<<<\n"; 301 302 // Sort annotations. 303 // 304 // First, sort in the order of input lines to make it easier to find relevant 305 // annotations while iterating input lines in the implementation below. 306 // FileCheck diagnostics are not always reported and recorded in the order of 307 // input lines due to, for example, CHECK-DAG and CHECK-NOT. 308 // 309 // Second, for annotations for the same input line, sort in the order of the 310 // FileCheck directive's line in the check file (where there's at most one 311 // directive per line). The rationale of this choice is that, for any input 312 // line, this sort establishes a total order of annotations that, with 313 // respect to match results, is consistent across multiple lines, thus 314 // making match results easier to track from one line to the next when they 315 // span multiple lines. 316 std::sort(Annotations.begin(), Annotations.end(), 317 [](const InputAnnotation &A, const InputAnnotation &B) { 318 if (A.InputLine != B.InputLine) 319 return A.InputLine < B.InputLine; 320 return A.CheckLine < B.CheckLine; 321 }); 322 323 // Compute the width of the label column. 324 const unsigned char *InputFilePtr = InputFileText.bytes_begin(), 325 *InputFileEnd = InputFileText.bytes_end(); 326 unsigned LineCount = InputFileText.count('\n'); 327 if (InputFileEnd[-1] != '\n') 328 ++LineCount; 329 unsigned LineNoWidth = log10(LineCount) + 1; 330 // +3 below adds spaces (1) to the left of the (right-aligned) line numbers 331 // on input lines and (2) to the right of the (left-aligned) labels on 332 // annotation lines so that input lines and annotation lines are more 333 // visually distinct. For example, the spaces on the annotation lines ensure 334 // that input line numbers and check directive line numbers never align 335 // horizontally. Those line numbers might not even be for the same file. 336 // One space would be enough to achieve that, but more makes it even easier 337 // to see. 338 LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; 339 340 // Print annotated input lines. 341 auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); 342 for (unsigned Line = 1; 343 InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; 344 ++Line) { 345 const unsigned char *InputFileLine = InputFilePtr; 346 347 // Print right-aligned line number. 348 WithColor(OS, raw_ostream::BLACK, true) 349 << format_decimal(Line, LabelWidth) << ": "; 350 351 // Print numbered line. 352 bool Newline = false; 353 while (InputFilePtr != InputFileEnd && !Newline) { 354 if (*InputFilePtr == '\n') 355 Newline = true; 356 else 357 OS << *InputFilePtr; 358 ++InputFilePtr; 359 } 360 OS << '\n'; 361 unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; 362 363 // Print any annotations. 364 while (AnnotationItr != AnnotationEnd && 365 AnnotationItr->InputLine == Line) { 366 WithColor COS(OS, AnnotationItr->Marker.Color, true); 367 // The two spaces below are where the ": " appears on input lines. 368 COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; 369 unsigned Col; 370 for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) 371 COS << ' '; 372 COS << AnnotationItr->Marker.Lead; 373 // If InputEndCol=UINT_MAX, stop at InputLineWidth. 374 for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; 375 ++Col) 376 COS << '~'; 377 const std::string &Note = AnnotationItr->Marker.Note; 378 if (!Note.empty()) { 379 // Put the note at the end of the input line. If we were to instead 380 // put the note right after the marker, subsequent annotations for the 381 // same input line might appear to mark this note instead of the input 382 // line. 383 for (; Col <= InputLineWidth; ++Col) 384 COS << ' '; 385 COS << ' ' << Note; 386 } 387 COS << '\n'; 388 ++AnnotationItr; 389 } 390 } 391 392 OS << ">>>>>>\n"; 393 } 394 395 int main(int argc, char **argv) { 396 // Enable use of ANSI color codes because FileCheck is using them to 397 // highlight text. 398 llvm::sys::Process::UseANSIEscapeCodes(true); 399 400 InitLLVM X(argc, argv); 401 cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, 402 "FILECHECK_OPTS"); 403 if (DumpInput == DumpInputHelp) { 404 DumpInputAnnotationHelp(outs()); 405 return 0; 406 } 407 if (CheckFilename.empty()) { 408 errs() << "<check-file> not specified\n"; 409 return 2; 410 } 411 412 FileCheckRequest Req; 413 for (auto Prefix : CheckPrefixes) 414 Req.CheckPrefixes.push_back(Prefix); 415 416 for (auto CheckNot : ImplicitCheckNot) 417 Req.ImplicitCheckNot.push_back(CheckNot); 418 419 for (auto G : GlobalDefines) 420 Req.GlobalDefines.push_back(G); 421 422 Req.AllowEmptyInput = AllowEmptyInput; 423 Req.EnableVarScope = EnableVarScope; 424 Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; 425 Req.Verbose = Verbose; 426 Req.VerboseVerbose = VerboseVerbose; 427 Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; 428 Req.MatchFullLines = MatchFullLines; 429 430 if (VerboseVerbose) 431 Req.Verbose = true; 432 433 FileCheck FC(Req); 434 if (!FC.ValidateCheckPrefixes()) { 435 errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " 436 "start with a letter and contain only alphanumeric characters, " 437 "hyphens and underscores\n"; 438 return 2; 439 } 440 441 Regex PrefixRE = FC.buildCheckPrefixRegex(); 442 std::string REError; 443 if (!PrefixRE.isValid(REError)) { 444 errs() << "Unable to combine check-prefix strings into a prefix regular " 445 "expression! This is likely a bug in FileCheck's verification of " 446 "the check-prefix strings. Regular expression parsing failed " 447 "with the following error: " 448 << REError << "\n"; 449 return 2; 450 } 451 452 SourceMgr SM; 453 454 // Read the expected strings from the check file. 455 ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr = 456 MemoryBuffer::getFileOrSTDIN(CheckFilename); 457 if (std::error_code EC = CheckFileOrErr.getError()) { 458 errs() << "Could not open check file '" << CheckFilename 459 << "': " << EC.message() << '\n'; 460 return 2; 461 } 462 MemoryBuffer &CheckFile = *CheckFileOrErr.get(); 463 464 SmallString<4096> CheckFileBuffer; 465 StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); 466 467 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 468 CheckFileText, CheckFile.getBufferIdentifier()), 469 SMLoc()); 470 471 std::vector<FileCheckString> CheckStrings; 472 if (FC.ReadCheckFile(SM, CheckFileText, PrefixRE, CheckStrings)) 473 return 2; 474 475 // Open the file to check and add it to SourceMgr. 476 ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr = 477 MemoryBuffer::getFileOrSTDIN(InputFilename); 478 if (std::error_code EC = InputFileOrErr.getError()) { 479 errs() << "Could not open input file '" << InputFilename 480 << "': " << EC.message() << '\n'; 481 return 2; 482 } 483 MemoryBuffer &InputFile = *InputFileOrErr.get(); 484 485 if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { 486 errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; 487 DumpCommandLine(argc, argv); 488 return 2; 489 } 490 491 SmallString<4096> InputFileBuffer; 492 StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); 493 494 SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( 495 InputFileText, InputFile.getBufferIdentifier()), 496 SMLoc()); 497 498 if (DumpInput == DumpInputDefault) 499 DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; 500 501 std::vector<FileCheckDiag> Diags; 502 int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, 503 DumpInput == DumpInputNever ? nullptr : &Diags) 504 ? EXIT_SUCCESS 505 : 1; 506 if (DumpInput == DumpInputAlways || 507 (ExitCode == 1 && DumpInput == DumpInputFail)) { 508 errs() << "\n" 509 << "Input file: " 510 << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue()) 511 << "\n" 512 << "Check file: " << CheckFilename << "\n" 513 << "\n" 514 << "-dump-input=help describes the format of the following dump.\n" 515 << "\n"; 516 std::vector<InputAnnotation> Annotations; 517 unsigned LabelWidth; 518 BuildInputAnnotations(Diags, Annotations, LabelWidth); 519 DumpAnnotatedInput(errs(), InputFileText, Annotations, LabelWidth); 520 } 521 522 return ExitCode; 523 } 524