xref: /llvm-project/llvm/utils/FileCheck/FileCheck.cpp (revision caf26b9437ae3e26d4c9c8680d3e755aafeda6ed)
1 //===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // FileCheck does a line-by line check of a file that validates whether it
10 // contains the expected content.  This is useful for regression tests etc.
11 //
12 // This program exits with an exit status of 2 on error, exit status of 0 if
13 // the file matched the expected contents, and exit status of 1 if it did not
14 // contain the expected contents.
15 //
16 //===----------------------------------------------------------------------===//
17 
18 #include "llvm/FileCheck/FileCheck.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/InitLLVM.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/Process.h"
23 #include "llvm/Support/SourceMgr.h"
24 #include "llvm/Support/WithColor.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include <cmath>
27 #include <map>
28 using namespace llvm;
29 
30 static cl::extrahelp FileCheckOptsEnv(
31     "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
32     "from the command line.\n");
33 
34 static cl::opt<std::string>
35     CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
36 
37 static cl::opt<std::string>
38     InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
39                   cl::init("-"), cl::value_desc("filename"));
40 
41 static cl::list<std::string> CheckPrefixes(
42     "check-prefix",
43     cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
44 static cl::alias CheckPrefixesAlias(
45     "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
46     cl::NotHidden,
47     cl::desc(
48         "Alias for -check-prefix permitting multiple comma separated values"));
49 
50 static cl::list<std::string> CommentPrefixes(
51     "comment-prefixes", cl::CommaSeparated, cl::Hidden,
52     cl::desc("Comma-separated list of comment prefixes to use from check file\n"
53              "(defaults to 'COM,RUN'). Please avoid using this feature in\n"
54              "LLVM's LIT-based test suites, which should be easier to\n"
55              "maintain if they all follow a consistent comment style. This\n"
56              "feature is meant for non-LIT test suites using FileCheck."));
57 
58 static cl::opt<bool> NoCanonicalizeWhiteSpace(
59     "strict-whitespace",
60     cl::desc("Do not treat all horizontal whitespace as equivalent"));
61 
62 static cl::opt<bool> IgnoreCase(
63     "ignore-case",
64     cl::desc("Use case-insensitive matching"));
65 
66 static cl::list<std::string> ImplicitCheckNot(
67     "implicit-check-not",
68     cl::desc("Add an implicit negative check with this pattern to every\n"
69              "positive check. This can be used to ensure that no instances of\n"
70              "this pattern occur which are not matched by a positive pattern"),
71     cl::value_desc("pattern"));
72 
73 static cl::list<std::string>
74     GlobalDefines("D", cl::AlwaysPrefix,
75                   cl::desc("Define a variable to be used in capture patterns."),
76                   cl::value_desc("VAR=VALUE"));
77 
78 static cl::opt<bool> AllowEmptyInput(
79     "allow-empty", cl::init(false),
80     cl::desc("Allow the input file to be empty. This is useful when making\n"
81              "checks that some error message does not occur, for example."));
82 
83 static cl::opt<bool> AllowUnusedPrefixes(
84     "allow-unused-prefixes",
85     cl::desc("Allow prefixes to be specified but not appear in the test."));
86 
87 static cl::opt<bool> MatchFullLines(
88     "match-full-lines", cl::init(false),
89     cl::desc("Require all positive matches to cover an entire input line.\n"
90              "Allows leading and trailing whitespace if --strict-whitespace\n"
91              "is not also passed."));
92 
93 static cl::opt<bool> EnableVarScope(
94     "enable-var-scope", cl::init(false),
95     cl::desc("Enables scope for regex variables. Variables with names that\n"
96              "do not start with '$' will be reset at the beginning of\n"
97              "each CHECK-LABEL block."));
98 
99 static cl::opt<bool> AllowDeprecatedDagOverlap(
100     "allow-deprecated-dag-overlap", cl::init(false),
101     cl::desc("Enable overlapping among matches in a group of consecutive\n"
102              "CHECK-DAG directives.  This option is deprecated and is only\n"
103              "provided for convenience as old tests are migrated to the new\n"
104              "non-overlapping CHECK-DAG implementation.\n"));
105 
106 static cl::opt<bool> Verbose(
107     "v",
108     cl::desc("Print directive pattern matches, or add them to the input dump\n"
109              "if enabled.\n"));
110 
111 static cl::opt<bool> VerboseVerbose(
112     "vv",
113     cl::desc("Print information helpful in diagnosing internal FileCheck\n"
114              "issues, or add it to the input dump if enabled.  Implies\n"
115              "-v.\n"));
116 
117 // The order of DumpInputValue members affects their precedence, as documented
118 // for -dump-input below.
119 enum DumpInputValue {
120   DumpInputNever,
121   DumpInputFail,
122   DumpInputAlways,
123   DumpInputHelp
124 };
125 
126 static cl::list<DumpInputValue> DumpInputs(
127     "dump-input",
128     cl::desc("Dump input to stderr, adding annotations representing\n"
129              "currently enabled diagnostics.  When there are multiple\n"
130              "occurrences of this option, the <value> that appears earliest\n"
131              "in the list below has precedence.  The default is 'fail'.\n"),
132     cl::value_desc("mode"),
133     cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"),
134                clEnumValN(DumpInputAlways, "always", "Always dump input"),
135                clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
136                clEnumValN(DumpInputNever, "never", "Never dump input")));
137 
138 // The order of DumpInputFilterValue members affects their precedence, as
139 // documented for -dump-input-filter below.
140 enum DumpInputFilterValue {
141   DumpInputFilterError,
142   DumpInputFilterAnnotation,
143   DumpInputFilterAnnotationFull,
144   DumpInputFilterAll
145 };
146 
147 static cl::list<DumpInputFilterValue> DumpInputFilters(
148     "dump-input-filter",
149     cl::desc("In the dump requested by -dump-input, print only input lines of\n"
150              "kind <value> plus any context specified by -dump-input-context.\n"
151              "When there are multiple occurrences of this option, the <value>\n"
152              "that appears earliest in the list below has precedence.  The\n"
153              "default is 'error' when -dump-input=fail, and it's 'all' when\n"
154              "-dump-input=always.\n"),
155     cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"),
156                clEnumValN(DumpInputFilterAnnotationFull, "annotation-full",
157                           "Input lines with annotations"),
158                clEnumValN(DumpInputFilterAnnotation, "annotation",
159                           "Input lines with starting points of annotations"),
160                clEnumValN(DumpInputFilterError, "error",
161                           "Input lines with starting points of error "
162                           "annotations")));
163 
164 static cl::list<unsigned> DumpInputContexts(
165     "dump-input-context", cl::value_desc("N"),
166     cl::desc("In the dump requested by -dump-input, print <N> input lines\n"
167              "before and <N> input lines after any lines specified by\n"
168              "-dump-input-filter.  When there are multiple occurrences of\n"
169              "this option, the largest specified <N> has precedence.  The\n"
170              "default is 5.\n"));
171 
172 typedef cl::list<std::string>::const_iterator prefix_iterator;
173 
174 
175 
176 
177 
178 
179 
DumpCommandLine(int argc,char ** argv)180 static void DumpCommandLine(int argc, char **argv) {
181   errs() << "FileCheck command line: ";
182   for (int I = 0; I < argc; I++)
183     errs() << " " << argv[I];
184   errs() << "\n";
185 }
186 
187 struct MarkerStyle {
188   /// The starting char (before tildes) for marking the line.
189   char Lead;
190   /// What color to use for this annotation.
191   raw_ostream::Colors Color;
192   /// A note to follow the marker, or empty string if none.
193   std::string Note;
194   /// Does this marker indicate inclusion by -dump-input-filter=error?
195   bool FiltersAsError;
MarkerStyleMarkerStyle196   MarkerStyle() {}
MarkerStyleMarkerStyle197   MarkerStyle(char Lead, raw_ostream::Colors Color,
198               const std::string &Note = "", bool FiltersAsError = false)
199       : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) {
200     assert((!FiltersAsError || !Note.empty()) &&
201            "expected error diagnostic to have note");
202   }
203 };
204 
GetMarker(FileCheckDiag::MatchType MatchTy)205 static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
206   switch (MatchTy) {
207   case FileCheckDiag::MatchFoundAndExpected:
208     return MarkerStyle('^', raw_ostream::GREEN);
209   case FileCheckDiag::MatchFoundButExcluded:
210     return MarkerStyle('!', raw_ostream::RED, "error: no match expected",
211                        /*FiltersAsError=*/true);
212   case FileCheckDiag::MatchFoundButWrongLine:
213     return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line",
214                        /*FiltersAsError=*/true);
215   case FileCheckDiag::MatchFoundButDiscarded:
216     return MarkerStyle('!', raw_ostream::CYAN,
217                        "discard: overlaps earlier match");
218   case FileCheckDiag::MatchFoundErrorNote:
219     // Note should always be overridden within the FileCheckDiag.
220     return MarkerStyle('!', raw_ostream::RED,
221                        "error: unknown error after match",
222                        /*FiltersAsError=*/true);
223   case FileCheckDiag::MatchNoneAndExcluded:
224     return MarkerStyle('X', raw_ostream::GREEN);
225   case FileCheckDiag::MatchNoneButExpected:
226     return MarkerStyle('X', raw_ostream::RED, "error: no match found",
227                        /*FiltersAsError=*/true);
228   case FileCheckDiag::MatchNoneForInvalidPattern:
229     return MarkerStyle('X', raw_ostream::RED,
230                        "error: match failed for invalid pattern",
231                        /*FiltersAsError=*/true);
232   case FileCheckDiag::MatchFuzzy:
233     return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
234                        /*FiltersAsError=*/true);
235   }
236   llvm_unreachable_internal("unexpected match type");
237 }
238 
DumpInputAnnotationHelp(raw_ostream & OS)239 static void DumpInputAnnotationHelp(raw_ostream &OS) {
240   OS << "The following description was requested by -dump-input=help to\n"
241      << "explain the input dump printed by FileCheck.\n"
242      << "\n"
243      << "Related command-line options:\n"
244      << "\n"
245      << "  - -dump-input=<value> enables or disables the input dump\n"
246      << "  - -dump-input-filter=<value> filters the input lines\n"
247      << "  - -dump-input-context=<N> adjusts the context of filtered lines\n"
248      << "  - -v and -vv add more annotations\n"
249      << "  - -color forces colors to be enabled both in the dump and below\n"
250      << "  - -help documents the above options in more detail\n"
251      << "\n"
252      << "These options can also be set via FILECHECK_OPTS.  For example, for\n"
253      << "maximum debugging output on failures:\n"
254      << "\n"
255      << "  $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
256      << "\n"
257      << "Input dump annotation format:\n"
258      << "\n";
259 
260   // Labels for input lines.
261   OS << "  - ";
262   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
263   OS << "     labels line number L of the input file\n"
264      << "           An extra space is added after each input line to represent"
265      << " the\n"
266      << "           newline character\n";
267 
268   // Labels for annotation lines.
269   OS << "  - ";
270   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
271   OS << "    labels the only match result for either (1) a pattern of type T"
272      << " from\n"
273      << "           line L of the check file if L is an integer or (2) the"
274      << " I-th implicit\n"
275      << "           pattern if L is \"imp\" followed by an integer "
276      << "I (index origin one)\n";
277   OS << "  - ";
278   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
279   OS << "  labels the Nth match result for such a pattern\n";
280 
281   // Markers on annotation lines.
282   OS << "  - ";
283   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
284   OS << "    marks good match (reported if -v)\n"
285      << "  - ";
286   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
287   OS << "    marks bad match, such as:\n"
288      << "           - CHECK-NEXT on same line as previous match (error)\n"
289      << "           - CHECK-NOT found (error)\n"
290      << "           - CHECK-DAG overlapping match (discarded, reported if "
291      << "-vv)\n"
292      << "  - ";
293   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
294   OS << "    marks search range when no match is found, such as:\n"
295      << "           - CHECK-NEXT not found (error)\n"
296      << "           - CHECK-NOT not found (success, reported if -vv)\n"
297      << "           - CHECK-DAG not found after discarded matches (error)\n"
298      << "  - ";
299   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
300   OS << "      marks fuzzy match when no match is found\n";
301 
302   // Elided lines.
303   OS << "  - ";
304   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
305   OS << "    indicates elided input lines and annotations, as specified by\n"
306      << "           -dump-input-filter and -dump-input-context\n";
307 
308   // Colors.
309   OS << "  - colors ";
310   WithColor(OS, raw_ostream::GREEN, true) << "success";
311   OS << ", ";
312   WithColor(OS, raw_ostream::RED, true) << "error";
313   OS << ", ";
314   WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
315   OS << ", ";
316   WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
317   OS << ", ";
318   WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
319   OS << "\n";
320 }
321 
322 /// An annotation for a single input line.
323 struct InputAnnotation {
324   /// The index of the match result across all checks
325   unsigned DiagIndex;
326   /// The label for this annotation.
327   std::string Label;
328   /// Is this the initial fragment of a diagnostic that has been broken across
329   /// multiple lines?
330   bool IsFirstLine;
331   /// What input line (one-origin indexing) this annotation marks.  This might
332   /// be different from the starting line of the original diagnostic if
333   /// !IsFirstLine.
334   unsigned InputLine;
335   /// The column range (one-origin indexing, open end) in which to mark the
336   /// input line.  If InputEndCol is UINT_MAX, treat it as the last column
337   /// before the newline.
338   unsigned InputStartCol, InputEndCol;
339   /// The marker to use.
340   MarkerStyle Marker;
341   /// Whether this annotation represents a good match for an expected pattern.
342   bool FoundAndExpectedMatch;
343 };
344 
345 /// Get an abbreviation for the check type.
GetCheckTypeAbbreviation(Check::FileCheckType Ty)346 static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
347   switch (Ty) {
348   case Check::CheckPlain:
349     if (Ty.getCount() > 1)
350       return "count";
351     return "check";
352   case Check::CheckNext:
353     return "next";
354   case Check::CheckSame:
355     return "same";
356   case Check::CheckNot:
357     return "not";
358   case Check::CheckDAG:
359     return "dag";
360   case Check::CheckLabel:
361     return "label";
362   case Check::CheckEmpty:
363     return "empty";
364   case Check::CheckComment:
365     return "com";
366   case Check::CheckEOF:
367     return "eof";
368   case Check::CheckBadNot:
369     return "bad-not";
370   case Check::CheckBadCount:
371     return "bad-count";
372   case Check::CheckMisspelled:
373     return "misspelled";
374   case Check::CheckNone:
375     llvm_unreachable("invalid FileCheckType");
376   }
377   llvm_unreachable("unknown FileCheckType");
378 }
379 
380 static void
BuildInputAnnotations(const SourceMgr & SM,unsigned CheckFileBufferID,const std::pair<unsigned,unsigned> & ImpPatBufferIDRange,const std::vector<FileCheckDiag> & Diags,std::vector<InputAnnotation> & Annotations,unsigned & LabelWidth)381 BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
382                       const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
383                       const std::vector<FileCheckDiag> &Diags,
384                       std::vector<InputAnnotation> &Annotations,
385                       unsigned &LabelWidth) {
386   struct CompareSMLoc {
387     bool operator()(const SMLoc &LHS, const SMLoc &RHS) const {
388       return LHS.getPointer() < RHS.getPointer();
389     }
390   };
391   // How many diagnostics does each pattern have?
392   std::map<SMLoc, unsigned, CompareSMLoc> DiagCountPerPattern;
393   for (const FileCheckDiag &Diag : Diags)
394     ++DiagCountPerPattern[Diag.CheckLoc];
395   // How many diagnostics have we seen so far per pattern?
396   std::map<SMLoc, unsigned, CompareSMLoc> DiagIndexPerPattern;
397   // How many total diagnostics have we seen so far?
398   unsigned DiagIndex = 0;
399   // What's the widest label?
400   LabelWidth = 0;
401   for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
402        ++DiagItr) {
403     InputAnnotation A;
404     A.DiagIndex = DiagIndex++;
405 
406     // Build label, which uniquely identifies this check result.
407     unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
408     auto CheckLineAndCol =
409         SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
410     llvm::raw_string_ostream Label(A.Label);
411     Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
412     if (CheckBufferID == CheckFileBufferID)
413       Label << CheckLineAndCol.first;
414     else if (ImpPatBufferIDRange.first <= CheckBufferID &&
415              CheckBufferID < ImpPatBufferIDRange.second)
416       Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
417     else
418       llvm_unreachable("expected diagnostic's check location to be either in "
419                        "the check file or for an implicit pattern");
420     if (DiagCountPerPattern[DiagItr->CheckLoc] > 1)
421       Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++;
422     LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
423 
424     A.Marker = GetMarker(DiagItr->MatchTy);
425     if (!DiagItr->Note.empty()) {
426       A.Marker.Note = DiagItr->Note;
427       // It's less confusing if notes that don't actually have ranges don't have
428       // markers.  For example, a marker for 'with "VAR" equal to "5"' would
429       // seem to indicate where "VAR" matches, but the location we actually have
430       // for the marker simply points to the start of the match/search range for
431       // the full pattern of which the substitution is potentially just one
432       // component.
433       if (DiagItr->InputStartLine == DiagItr->InputEndLine &&
434           DiagItr->InputStartCol == DiagItr->InputEndCol)
435         A.Marker.Lead = ' ';
436     }
437     if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
438       assert(!DiagItr->Note.empty() &&
439              "expected custom note for MatchFoundErrorNote");
440       A.Marker.Note = "error: " + A.Marker.Note;
441     }
442     A.FoundAndExpectedMatch =
443         DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
444 
445     // Compute the mark location, and break annotation into multiple
446     // annotations if it spans multiple lines.
447     A.IsFirstLine = true;
448     A.InputLine = DiagItr->InputStartLine;
449     A.InputStartCol = DiagItr->InputStartCol;
450     if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
451       // Sometimes ranges are empty in order to indicate a specific point, but
452       // that would mean nothing would be marked, so adjust the range to
453       // include the following character.
454       A.InputEndCol =
455           std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
456       Annotations.push_back(A);
457     } else {
458       assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
459              "expected input range not to be inverted");
460       A.InputEndCol = UINT_MAX;
461       Annotations.push_back(A);
462       for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
463            L <= E; ++L) {
464         // If a range ends before the first column on a line, then it has no
465         // characters on that line, so there's nothing to render.
466         if (DiagItr->InputEndCol == 1 && L == E)
467           break;
468         InputAnnotation B;
469         B.DiagIndex = A.DiagIndex;
470         B.Label = A.Label;
471         B.IsFirstLine = false;
472         B.InputLine = L;
473         B.Marker = A.Marker;
474         B.Marker.Lead = '~';
475         B.Marker.Note = "";
476         B.InputStartCol = 1;
477         if (L != E)
478           B.InputEndCol = UINT_MAX;
479         else
480           B.InputEndCol = DiagItr->InputEndCol;
481         B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
482         Annotations.push_back(B);
483       }
484     }
485   }
486 }
487 
FindInputLineInFilter(DumpInputFilterValue DumpInputFilter,unsigned CurInputLine,const std::vector<InputAnnotation>::iterator & AnnotationBeg,const std::vector<InputAnnotation>::iterator & AnnotationEnd)488 static unsigned FindInputLineInFilter(
489     DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
490     const std::vector<InputAnnotation>::iterator &AnnotationBeg,
491     const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
492   if (DumpInputFilter == DumpInputFilterAll)
493     return CurInputLine;
494   for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
495        ++AnnotationItr) {
496     switch (DumpInputFilter) {
497     case DumpInputFilterAll:
498       llvm_unreachable("unexpected DumpInputFilterAll");
499       break;
500     case DumpInputFilterAnnotationFull:
501       return AnnotationItr->InputLine;
502     case DumpInputFilterAnnotation:
503       if (AnnotationItr->IsFirstLine)
504         return AnnotationItr->InputLine;
505       break;
506     case DumpInputFilterError:
507       if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
508         return AnnotationItr->InputLine;
509       break;
510     }
511   }
512   return UINT_MAX;
513 }
514 
515 /// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would
516 /// occupy less lines than ElidedLines, but print ElidedLines otherwise.  Either
517 /// way, clear ElidedLines.  Thus, if ElidedLines is empty, do nothing.
DumpEllipsisOrElidedLines(raw_ostream & OS,std::string & ElidedLines,unsigned LabelWidth)518 static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
519                                       unsigned LabelWidth) {
520   if (ElidedLines.empty())
521     return;
522   unsigned EllipsisLines = 3;
523   if (EllipsisLines < StringRef(ElidedLines).count('\n')) {
524     for (unsigned i = 0; i < EllipsisLines; ++i) {
525       WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
526           << right_justify(".", LabelWidth);
527       OS << '\n';
528     }
529   } else
530     OS << ElidedLines;
531   ElidedLines.clear();
532 }
533 
DumpAnnotatedInput(raw_ostream & OS,const FileCheckRequest & Req,DumpInputFilterValue DumpInputFilter,unsigned DumpInputContext,StringRef InputFileText,std::vector<InputAnnotation> & Annotations,unsigned LabelWidth)534 static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
535                                DumpInputFilterValue DumpInputFilter,
536                                unsigned DumpInputContext,
537                                StringRef InputFileText,
538                                std::vector<InputAnnotation> &Annotations,
539                                unsigned LabelWidth) {
540   OS << "Input was:\n<<<<<<\n";
541 
542   // Sort annotations.
543   llvm::sort(Annotations,
544              [](const InputAnnotation &A, const InputAnnotation &B) {
545                // 1. Sort annotations in the order of the input lines.
546                //
547                // This makes it easier to find relevant annotations while
548                // iterating input lines in the implementation below.  FileCheck
549                // does not always produce diagnostics in the order of input
550                // lines due to, for example, CHECK-DAG and CHECK-NOT.
551                if (A.InputLine != B.InputLine)
552                  return A.InputLine < B.InputLine;
553                // 2. Sort annotations in the temporal order FileCheck produced
554                // their associated diagnostics.
555                //
556                // This sort offers several benefits:
557                //
558                // A. On a single input line, the order of annotations reflects
559                //    the FileCheck logic for processing directives/patterns.
560                //    This can be helpful in understanding cases in which the
561                //    order of the associated directives/patterns in the check
562                //    file or on the command line either (i) does not match the
563                //    temporal order in which FileCheck looks for matches for the
564                //    directives/patterns (due to, for example, CHECK-LABEL,
565                //    CHECK-NOT, or `--implicit-check-not`) or (ii) does match
566                //    that order but does not match the order of those
567                //    diagnostics along an input line (due to, for example,
568                //    CHECK-DAG).
569                //
570                //    On the other hand, because our presentation format presents
571                //    input lines in order, there's no clear way to offer the
572                //    same benefit across input lines.  For consistency, it might
573                //    then seem worthwhile to have annotations on a single line
574                //    also sorted in input order (that is, by input column).
575                //    However, in practice, this appears to be more confusing
576                //    than helpful.  Perhaps it's intuitive to expect annotations
577                //    to be listed in the temporal order in which they were
578                //    produced except in cases the presentation format obviously
579                //    and inherently cannot support it (that is, across input
580                //    lines).
581                //
582                // B. When diagnostics' annotations are split among multiple
583                //    input lines, the user must track them from one input line
584                //    to the next.  One property of the sort chosen here is that
585                //    it facilitates the user in this regard by ensuring the
586                //    following: when comparing any two input lines, a
587                //    diagnostic's annotations are sorted in the same position
588                //    relative to all other diagnostics' annotations.
589                return A.DiagIndex < B.DiagIndex;
590              });
591 
592   // Compute the width of the label column.
593   const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
594                       *InputFileEnd = InputFileText.bytes_end();
595   unsigned LineCount = InputFileText.count('\n');
596   if (InputFileEnd[-1] != '\n')
597     ++LineCount;
598   unsigned LineNoWidth = std::log10(LineCount) + 1;
599   // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
600   // on input lines and (2) to the right of the (left-aligned) labels on
601   // annotation lines so that input lines and annotation lines are more
602   // visually distinct.  For example, the spaces on the annotation lines ensure
603   // that input line numbers and check directive line numbers never align
604   // horizontally.  Those line numbers might not even be for the same file.
605   // One space would be enough to achieve that, but more makes it even easier
606   // to see.
607   LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
608 
609   // Print annotated input lines.
610   unsigned PrevLineInFilter = 0; // 0 means none so far
611   unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
612   std::string ElidedLines;
613   raw_string_ostream ElidedLinesOS(ElidedLines);
614   ColorMode TheColorMode =
615       WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
616   if (TheColorMode == ColorMode::Enable)
617     ElidedLinesOS.enable_colors(true);
618   auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
619   for (unsigned Line = 1;
620        InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
621        ++Line) {
622     const unsigned char *InputFileLine = InputFilePtr;
623 
624     // Compute the previous and next line included by the filter.
625     if (NextLineInFilter < Line)
626       NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
627                                                AnnotationItr, AnnotationEnd);
628     assert(NextLineInFilter && "expected NextLineInFilter to be computed");
629     if (NextLineInFilter == Line)
630       PrevLineInFilter = Line;
631 
632     // Elide this input line and its annotations if it's not within the
633     // context specified by -dump-input-context of an input line included by
634     // -dump-input-filter.  However, in case the resulting ellipsis would occupy
635     // more lines than the input lines and annotations it elides, buffer the
636     // elided lines and annotations so we can print them instead.
637     raw_ostream *LineOS;
638     if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
639         (NextLineInFilter == UINT_MAX ||
640          Line + DumpInputContext < NextLineInFilter))
641       LineOS = &ElidedLinesOS;
642     else {
643       LineOS = &OS;
644       DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidth);
645     }
646 
647     // Print right-aligned line number.
648     WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
649               TheColorMode)
650         << format_decimal(Line, LabelWidth) << ": ";
651 
652     // For the case where -v and colors are enabled, find the annotations for
653     // good matches for expected patterns in order to highlight everything
654     // else in the line.  There are no such annotations if -v is disabled.
655     std::vector<InputAnnotation> FoundAndExpectedMatches;
656     if (Req.Verbose && TheColorMode == ColorMode::Enable) {
657       for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
658            ++I) {
659         if (I->FoundAndExpectedMatch)
660           FoundAndExpectedMatches.push_back(*I);
661       }
662     }
663 
664     // Print numbered line with highlighting where there are no matches for
665     // expected patterns.
666     bool Newline = false;
667     {
668       WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
669                     /*BG=*/false, TheColorMode);
670       bool InMatch = false;
671       if (Req.Verbose)
672         COS.changeColor(raw_ostream::CYAN, true, true);
673       for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
674         bool WasInMatch = InMatch;
675         InMatch = false;
676         for (const InputAnnotation &M : FoundAndExpectedMatches) {
677           if (M.InputStartCol <= Col && Col < M.InputEndCol) {
678             InMatch = true;
679             break;
680           }
681         }
682         if (!WasInMatch && InMatch)
683           COS.resetColor();
684         else if (WasInMatch && !InMatch)
685           COS.changeColor(raw_ostream::CYAN, true, true);
686         if (*InputFilePtr == '\n') {
687           Newline = true;
688           COS << ' ';
689         } else
690           COS << *InputFilePtr;
691         ++InputFilePtr;
692       }
693     }
694     *LineOS << '\n';
695     unsigned InputLineWidth = InputFilePtr - InputFileLine;
696 
697     // Print any annotations.
698     while (AnnotationItr != AnnotationEnd &&
699            AnnotationItr->InputLine == Line) {
700       WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
701                     /*BG=*/false, TheColorMode);
702       // The two spaces below are where the ": " appears on input lines.
703       COS << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
704       unsigned Col;
705       for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
706         COS << ' ';
707       COS << AnnotationItr->Marker.Lead;
708       // If InputEndCol=UINT_MAX, stop at InputLineWidth.
709       for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
710            ++Col)
711         COS << '~';
712       const std::string &Note = AnnotationItr->Marker.Note;
713       if (!Note.empty()) {
714         // Put the note at the end of the input line.  If we were to instead
715         // put the note right after the marker, subsequent annotations for the
716         // same input line might appear to mark this note instead of the input
717         // line.
718         for (; Col <= InputLineWidth; ++Col)
719           COS << ' ';
720         COS << ' ' << Note;
721       }
722       COS << '\n';
723       ++AnnotationItr;
724     }
725   }
726   DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidth);
727 
728   OS << ">>>>>>\n";
729 }
730 
main(int argc,char ** argv)731 int main(int argc, char **argv) {
732   // Enable use of ANSI color codes because FileCheck is using them to
733   // highlight text.
734   llvm::sys::Process::UseANSIEscapeCodes(true);
735 
736   InitLLVM X(argc, argv);
737   cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
738                               "FILECHECK_OPTS");
739 
740   // Select -dump-input* values.  The -help documentation specifies the default
741   // value and which value to choose if an option is specified multiple times.
742   // In the latter case, the general rule of thumb is to choose the value that
743   // provides the most information.
744   DumpInputValue DumpInput =
745       DumpInputs.empty() ? DumpInputFail : *llvm::max_element(DumpInputs);
746   DumpInputFilterValue DumpInputFilter;
747   if (DumpInputFilters.empty())
748     DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
749                                                    : DumpInputFilterError;
750   else
751     DumpInputFilter = *llvm::max_element(DumpInputFilters);
752   unsigned DumpInputContext =
753       DumpInputContexts.empty() ? 5 : *llvm::max_element(DumpInputContexts);
754 
755   if (DumpInput == DumpInputHelp) {
756     DumpInputAnnotationHelp(outs());
757     return 0;
758   }
759   if (CheckFilename.empty()) {
760     errs() << "<check-file> not specified\n";
761     return 2;
762   }
763 
764   FileCheckRequest Req;
765   append_range(Req.CheckPrefixes, CheckPrefixes);
766 
767   append_range(Req.CommentPrefixes, CommentPrefixes);
768 
769   append_range(Req.ImplicitCheckNot, ImplicitCheckNot);
770 
771   bool GlobalDefineError = false;
772   for (StringRef G : GlobalDefines) {
773     size_t EqIdx = G.find('=');
774     if (EqIdx == std::string::npos) {
775       errs() << "Missing equal sign in command-line definition '-D" << G
776              << "'\n";
777       GlobalDefineError = true;
778       continue;
779     }
780     if (EqIdx == 0) {
781       errs() << "Missing variable name in command-line definition '-D" << G
782              << "'\n";
783       GlobalDefineError = true;
784       continue;
785     }
786     Req.GlobalDefines.push_back(G);
787   }
788   if (GlobalDefineError)
789     return 2;
790 
791   Req.AllowEmptyInput = AllowEmptyInput;
792   Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
793   Req.EnableVarScope = EnableVarScope;
794   Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
795   Req.Verbose = Verbose;
796   Req.VerboseVerbose = VerboseVerbose;
797   Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
798   Req.MatchFullLines = MatchFullLines;
799   Req.IgnoreCase = IgnoreCase;
800 
801   if (VerboseVerbose)
802     Req.Verbose = true;
803 
804   FileCheck FC(Req);
805   if (!FC.ValidateCheckPrefixes())
806     return 2;
807 
808   SourceMgr SM;
809 
810   // Read the expected strings from the check file.
811   ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
812       MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true);
813   if (std::error_code EC = CheckFileOrErr.getError()) {
814     errs() << "Could not open check file '" << CheckFilename
815            << "': " << EC.message() << '\n';
816     return 2;
817   }
818   MemoryBuffer &CheckFile = *CheckFileOrErr.get();
819 
820   SmallString<4096> CheckFileBuffer;
821   StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
822 
823   unsigned CheckFileBufferID =
824       SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
825                                 CheckFileText, CheckFile.getBufferIdentifier()),
826                             SMLoc());
827 
828   std::pair<unsigned, unsigned> ImpPatBufferIDRange;
829   if (FC.readCheckFile(SM, CheckFileText, &ImpPatBufferIDRange))
830     return 2;
831 
832   // Open the file to check and add it to SourceMgr.
833   ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
834       MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
835   if (InputFilename == "-")
836     InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
837   if (std::error_code EC = InputFileOrErr.getError()) {
838     errs() << "Could not open input file '" << InputFilename
839            << "': " << EC.message() << '\n';
840     return 2;
841   }
842   MemoryBuffer &InputFile = *InputFileOrErr.get();
843 
844   if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
845     errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
846     DumpCommandLine(argc, argv);
847     return 2;
848   }
849 
850   SmallString<4096> InputFileBuffer;
851   StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
852 
853   SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
854                             InputFileText, InputFile.getBufferIdentifier()),
855                         SMLoc());
856 
857   std::vector<FileCheckDiag> Diags;
858   int ExitCode = FC.checkInput(SM, InputFileText,
859                                DumpInput == DumpInputNever ? nullptr : &Diags)
860                      ? EXIT_SUCCESS
861                      : 1;
862   if (DumpInput == DumpInputAlways ||
863       (ExitCode == 1 && DumpInput == DumpInputFail)) {
864     errs() << "\n"
865            << "Input file: " << InputFilename << "\n"
866            << "Check file: " << CheckFilename << "\n"
867            << "\n"
868            << "-dump-input=help explains the following input dump.\n"
869            << "\n";
870     std::vector<InputAnnotation> Annotations;
871     unsigned LabelWidth;
872     BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
873                           Annotations, LabelWidth);
874     DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext,
875                        InputFileText, Annotations, LabelWidth);
876   }
877 
878   return ExitCode;
879 }
880