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