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