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