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