xref: /llvm-project/llvm/utils/FileCheck/FileCheck.cpp (revision 4f750f6ebc412869ce6bb28331313a9c9a9d9af7)
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(false), cl::ZeroOrMore,
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::MatchFoundErrorNote:
216     // Note should always be overridden within the FileCheckDiag.
217     return MarkerStyle('!', raw_ostream::RED,
218                        "error: unknown error after match",
219                        /*FiltersAsError=*/true);
220   case FileCheckDiag::MatchNoneAndExcluded:
221     return MarkerStyle('X', raw_ostream::GREEN);
222   case FileCheckDiag::MatchNoneButExpected:
223     return MarkerStyle('X', raw_ostream::RED, "error: no match found",
224                        /*FiltersAsError=*/true);
225   case FileCheckDiag::MatchNoneForInvalidPattern:
226     return MarkerStyle('X', raw_ostream::RED,
227                        "error: match failed for invalid pattern",
228                        /*FiltersAsError=*/true);
229   case FileCheckDiag::MatchFuzzy:
230     return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
231                        /*FiltersAsError=*/true);
232   }
233   llvm_unreachable_internal("unexpected match type");
234 }
235 
236 static void DumpInputAnnotationHelp(raw_ostream &OS) {
237   OS << "The following description was requested by -dump-input=help to\n"
238      << "explain the input dump printed by FileCheck.\n"
239      << "\n"
240      << "Related command-line options:\n"
241      << "\n"
242      << "  - -dump-input=<value> enables or disables the input dump\n"
243      << "  - -dump-input-filter=<value> filters the input lines\n"
244      << "  - -dump-input-context=<N> adjusts the context of filtered lines\n"
245      << "  - -v and -vv add more annotations\n"
246      << "  - -color forces colors to be enabled both in the dump and below\n"
247      << "  - -help documents the above options in more detail\n"
248      << "\n"
249      << "These options can also be set via FILECHECK_OPTS.  For example, for\n"
250      << "maximum debugging output on failures:\n"
251      << "\n"
252      << "  $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
253      << "\n"
254      << "Input dump annotation format:\n"
255      << "\n";
256 
257   // Labels for input lines.
258   OS << "  - ";
259   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
260   OS << "     labels line number L of the input file\n"
261      << "           An extra space is added after each input line to represent"
262      << " the\n"
263      << "           newline character\n";
264 
265   // Labels for annotation lines.
266   OS << "  - ";
267   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
268   OS << "    labels the only match result for either (1) a pattern of type T"
269      << " from\n"
270      << "           line L of the check file if L is an integer or (2) the"
271      << " I-th implicit\n"
272      << "           pattern if L is \"imp\" followed by an integer "
273      << "I (index origin one)\n";
274   OS << "  - ";
275   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
276   OS << "  labels the Nth match result for such a pattern\n";
277 
278   // Markers on annotation lines.
279   OS << "  - ";
280   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
281   OS << "    marks good match (reported if -v)\n"
282      << "  - ";
283   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
284   OS << "    marks bad match, such as:\n"
285      << "           - CHECK-NEXT on same line as previous match (error)\n"
286      << "           - CHECK-NOT found (error)\n"
287      << "           - CHECK-DAG overlapping match (discarded, reported if "
288      << "-vv)\n"
289      << "  - ";
290   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
291   OS << "    marks search range when no match is found, such as:\n"
292      << "           - CHECK-NEXT not found (error)\n"
293      << "           - CHECK-NOT not found (success, reported if -vv)\n"
294      << "           - CHECK-DAG not found after discarded matches (error)\n"
295      << "  - ";
296   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
297   OS << "      marks fuzzy match when no match is found\n";
298 
299   // Elided lines.
300   OS << "  - ";
301   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
302   OS << "    indicates elided input lines and annotations, as specified by\n"
303      << "           -dump-input-filter and -dump-input-context\n";
304 
305   // Colors.
306   OS << "  - colors ";
307   WithColor(OS, raw_ostream::GREEN, true) << "success";
308   OS << ", ";
309   WithColor(OS, raw_ostream::RED, true) << "error";
310   OS << ", ";
311   WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
312   OS << ", ";
313   WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
314   OS << ", ";
315   WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
316   OS << "\n";
317 }
318 
319 /// An annotation for a single input line.
320 struct InputAnnotation {
321   /// The index of the match result across all checks
322   unsigned DiagIndex;
323   /// The label for this annotation.
324   std::string Label;
325   /// Is this the initial fragment of a diagnostic that has been broken across
326   /// multiple lines?
327   bool IsFirstLine;
328   /// What input line (one-origin indexing) this annotation marks.  This might
329   /// be different from the starting line of the original diagnostic if
330   /// !IsFirstLine.
331   unsigned InputLine;
332   /// The column range (one-origin indexing, open end) in which to mark the
333   /// input line.  If InputEndCol is UINT_MAX, treat it as the last column
334   /// before the newline.
335   unsigned InputStartCol, InputEndCol;
336   /// The marker to use.
337   MarkerStyle Marker;
338   /// Whether this annotation represents a good match for an expected pattern.
339   bool FoundAndExpectedMatch;
340 };
341 
342 /// Get an abbreviation for the check type.
343 static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
344   switch (Ty) {
345   case Check::CheckPlain:
346     if (Ty.getCount() > 1)
347       return "count";
348     return "check";
349   case Check::CheckNext:
350     return "next";
351   case Check::CheckSame:
352     return "same";
353   case Check::CheckNot:
354     return "not";
355   case Check::CheckDAG:
356     return "dag";
357   case Check::CheckLabel:
358     return "label";
359   case Check::CheckEmpty:
360     return "empty";
361   case Check::CheckComment:
362     return "com";
363   case Check::CheckEOF:
364     return "eof";
365   case Check::CheckBadNot:
366     return "bad-not";
367   case Check::CheckBadCount:
368     return "bad-count";
369   case Check::CheckNone:
370     llvm_unreachable("invalid FileCheckType");
371   }
372   llvm_unreachable("unknown FileCheckType");
373 }
374 
375 static void
376 BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
377                       const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
378                       const std::vector<FileCheckDiag> &Diags,
379                       std::vector<InputAnnotation> &Annotations,
380                       unsigned &LabelWidth) {
381   // How many diagnostics have we seen so far?
382   unsigned DiagCount = 0;
383   // How many diagnostics has the current check seen so far?
384   unsigned CheckDiagCount = 0;
385   // What's the widest label?
386   LabelWidth = 0;
387   for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
388        ++DiagItr) {
389     InputAnnotation A;
390     A.DiagIndex = DiagCount++;
391 
392     // Build label, which uniquely identifies this check result.
393     unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
394     auto CheckLineAndCol =
395         SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
396     llvm::raw_string_ostream Label(A.Label);
397     Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
398     if (CheckBufferID == CheckFileBufferID)
399       Label << CheckLineAndCol.first;
400     else if (ImpPatBufferIDRange.first <= CheckBufferID &&
401              CheckBufferID < ImpPatBufferIDRange.second)
402       Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
403     else
404       llvm_unreachable("expected diagnostic's check location to be either in "
405                        "the check file or for an implicit pattern");
406     unsigned CheckDiagIndex = UINT_MAX;
407     auto DiagNext = std::next(DiagItr);
408     if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
409         DiagItr->CheckLoc == DiagNext->CheckLoc)
410       CheckDiagIndex = CheckDiagCount++;
411     else if (CheckDiagCount) {
412       CheckDiagIndex = CheckDiagCount;
413       CheckDiagCount = 0;
414     }
415     if (CheckDiagIndex != UINT_MAX)
416       Label << "'" << CheckDiagIndex;
417     Label.flush();
418     LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
419 
420     A.Marker = GetMarker(DiagItr->MatchTy);
421     if (!DiagItr->Note.empty()) {
422       A.Marker.Note = DiagItr->Note;
423       // It's less confusing if notes that don't actually have ranges don't have
424       // markers.  For example, a marker for 'with "VAR" equal to "5"' would
425       // seem to indicate where "VAR" matches, but the location we actually have
426       // for the marker simply points to the start of the match/search range for
427       // the full pattern of which the substitution is potentially just one
428       // component.
429       if (DiagItr->InputStartLine == DiagItr->InputEndLine &&
430           DiagItr->InputStartCol == DiagItr->InputEndCol)
431         A.Marker.Lead = ' ';
432     }
433     if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
434       assert(!DiagItr->Note.empty() &&
435              "expected custom note for MatchFoundErrorNote");
436       A.Marker.Note = "error: " + A.Marker.Note;
437     }
438     A.FoundAndExpectedMatch =
439         DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
440 
441     // Compute the mark location, and break annotation into multiple
442     // annotations if it spans multiple lines.
443     A.IsFirstLine = true;
444     A.InputLine = DiagItr->InputStartLine;
445     A.InputStartCol = DiagItr->InputStartCol;
446     if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
447       // Sometimes ranges are empty in order to indicate a specific point, but
448       // that would mean nothing would be marked, so adjust the range to
449       // include the following character.
450       A.InputEndCol =
451           std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
452       Annotations.push_back(A);
453     } else {
454       assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
455              "expected input range not to be inverted");
456       A.InputEndCol = UINT_MAX;
457       Annotations.push_back(A);
458       for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
459            L <= E; ++L) {
460         // If a range ends before the first column on a line, then it has no
461         // characters on that line, so there's nothing to render.
462         if (DiagItr->InputEndCol == 1 && L == E)
463           break;
464         InputAnnotation B;
465         B.DiagIndex = A.DiagIndex;
466         B.Label = A.Label;
467         B.IsFirstLine = false;
468         B.InputLine = L;
469         B.Marker = A.Marker;
470         B.Marker.Lead = '~';
471         B.Marker.Note = "";
472         B.InputStartCol = 1;
473         if (L != E)
474           B.InputEndCol = UINT_MAX;
475         else
476           B.InputEndCol = DiagItr->InputEndCol;
477         B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
478         Annotations.push_back(B);
479       }
480     }
481   }
482 }
483 
484 static unsigned FindInputLineInFilter(
485     DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
486     const std::vector<InputAnnotation>::iterator &AnnotationBeg,
487     const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
488   if (DumpInputFilter == DumpInputFilterAll)
489     return CurInputLine;
490   for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
491        ++AnnotationItr) {
492     switch (DumpInputFilter) {
493     case DumpInputFilterAll:
494       llvm_unreachable("unexpected DumpInputFilterAll");
495       break;
496     case DumpInputFilterAnnotationFull:
497       return AnnotationItr->InputLine;
498     case DumpInputFilterAnnotation:
499       if (AnnotationItr->IsFirstLine)
500         return AnnotationItr->InputLine;
501       break;
502     case DumpInputFilterError:
503       if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
504         return AnnotationItr->InputLine;
505       break;
506     }
507   }
508   return UINT_MAX;
509 }
510 
511 /// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would
512 /// occupy less lines than ElidedLines, but print ElidedLines otherwise.  Either
513 /// way, clear ElidedLines.  Thus, if ElidedLines is empty, do nothing.
514 static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
515                                       unsigned LabelWidth) {
516   if (ElidedLines.empty())
517     return;
518   unsigned EllipsisLines = 3;
519   if (EllipsisLines < StringRef(ElidedLines).count('\n')) {
520     for (unsigned i = 0; i < EllipsisLines; ++i) {
521       WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
522           << right_justify(".", LabelWidth);
523       OS << '\n';
524     }
525   } else
526     OS << ElidedLines;
527   ElidedLines.clear();
528 }
529 
530 static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
531                                DumpInputFilterValue DumpInputFilter,
532                                unsigned DumpInputContext,
533                                StringRef InputFileText,
534                                std::vector<InputAnnotation> &Annotations,
535                                unsigned LabelWidth) {
536   OS << "Input was:\n<<<<<<\n";
537 
538   // Sort annotations.
539   llvm::sort(Annotations,
540              [](const InputAnnotation &A, const InputAnnotation &B) {
541                // 1. Sort annotations in the order of the input lines.
542                //
543                // This makes it easier to find relevant annotations while
544                // iterating input lines in the implementation below.  FileCheck
545                // does not always produce diagnostics in the order of input
546                // lines due to, for example, CHECK-DAG and CHECK-NOT.
547                if (A.InputLine != B.InputLine)
548                  return A.InputLine < B.InputLine;
549                // 2. Sort annotations in the temporal order FileCheck produced
550                // their associated diagnostics.
551                //
552                // This sort offers several benefits:
553                //
554                // A. On a single input line, the order of annotations reflects
555                //    the FileCheck logic for processing directives/patterns.
556                //    This can be helpful in understanding cases in which the
557                //    order of the associated directives/patterns in the check
558                //    file or on the command line either (i) does not match the
559                //    temporal order in which FileCheck looks for matches for the
560                //    directives/patterns (due to, for example, CHECK-LABEL,
561                //    CHECK-NOT, or `--implicit-check-not`) or (ii) does match
562                //    that order but does not match the order of those
563                //    diagnostics along an input line (due to, for example,
564                //    CHECK-DAG).
565                //
566                //    On the other hand, because our presentation format presents
567                //    input lines in order, there's no clear way to offer the
568                //    same benefit across input lines.  For consistency, it might
569                //    then seem worthwhile to have annotations on a single line
570                //    also sorted in input order (that is, by input column).
571                //    However, in practice, this appears to be more confusing
572                //    than helpful.  Perhaps it's intuitive to expect annotations
573                //    to be listed in the temporal order in which they were
574                //    produced except in cases the presentation format obviously
575                //    and inherently cannot support it (that is, across input
576                //    lines).
577                //
578                // B. When diagnostics' annotations are split among multiple
579                //    input lines, the user must track them from one input line
580                //    to the next.  One property of the sort chosen here is that
581                //    it facilitates the user in this regard by ensuring the
582                //    following: when comparing any two input lines, a
583                //    diagnostic's annotations are sorted in the same position
584                //    relative to all other diagnostics' annotations.
585                return A.DiagIndex < B.DiagIndex;
586              });
587 
588   // Compute the width of the label column.
589   const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
590                       *InputFileEnd = InputFileText.bytes_end();
591   unsigned LineCount = InputFileText.count('\n');
592   if (InputFileEnd[-1] != '\n')
593     ++LineCount;
594   unsigned LineNoWidth = std::log10(LineCount) + 1;
595   // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
596   // on input lines and (2) to the right of the (left-aligned) labels on
597   // annotation lines so that input lines and annotation lines are more
598   // visually distinct.  For example, the spaces on the annotation lines ensure
599   // that input line numbers and check directive line numbers never align
600   // horizontally.  Those line numbers might not even be for the same file.
601   // One space would be enough to achieve that, but more makes it even easier
602   // to see.
603   LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
604 
605   // Print annotated input lines.
606   unsigned PrevLineInFilter = 0; // 0 means none so far
607   unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
608   std::string ElidedLines;
609   raw_string_ostream ElidedLinesOS(ElidedLines);
610   ColorMode TheColorMode =
611       WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
612   if (TheColorMode == ColorMode::Enable)
613     ElidedLinesOS.enable_colors(true);
614   auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
615   for (unsigned Line = 1;
616        InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
617        ++Line) {
618     const unsigned char *InputFileLine = InputFilePtr;
619 
620     // Compute the previous and next line included by the filter.
621     if (NextLineInFilter < Line)
622       NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
623                                                AnnotationItr, AnnotationEnd);
624     assert(NextLineInFilter && "expected NextLineInFilter to be computed");
625     if (NextLineInFilter == Line)
626       PrevLineInFilter = Line;
627 
628     // Elide this input line and its annotations if it's not within the
629     // context specified by -dump-input-context of an input line included by
630     // -dump-input-filter.  However, in case the resulting ellipsis would occupy
631     // more lines than the input lines and annotations it elides, buffer the
632     // elided lines and annotations so we can print them instead.
633     raw_ostream *LineOS = &OS;
634     if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
635         (NextLineInFilter == UINT_MAX ||
636          Line + DumpInputContext < NextLineInFilter))
637       LineOS = &ElidedLinesOS;
638     else {
639       LineOS = &OS;
640       DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
641     }
642 
643     // Print right-aligned line number.
644     WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
645               TheColorMode)
646         << format_decimal(Line, LabelWidth) << ": ";
647 
648     // For the case where -v and colors are enabled, find the annotations for
649     // good matches for expected patterns in order to highlight everything
650     // else in the line.  There are no such annotations if -v is disabled.
651     std::vector<InputAnnotation> FoundAndExpectedMatches;
652     if (Req.Verbose && TheColorMode == ColorMode::Enable) {
653       for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
654            ++I) {
655         if (I->FoundAndExpectedMatch)
656           FoundAndExpectedMatches.push_back(*I);
657       }
658     }
659 
660     // Print numbered line with highlighting where there are no matches for
661     // expected patterns.
662     bool Newline = false;
663     {
664       WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
665                     /*BG=*/false, TheColorMode);
666       bool InMatch = false;
667       if (Req.Verbose)
668         COS.changeColor(raw_ostream::CYAN, true, true);
669       for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
670         bool WasInMatch = InMatch;
671         InMatch = false;
672         for (auto M : FoundAndExpectedMatches) {
673           if (M.InputStartCol <= Col && Col < M.InputEndCol) {
674             InMatch = true;
675             break;
676           }
677         }
678         if (!WasInMatch && InMatch)
679           COS.resetColor();
680         else if (WasInMatch && !InMatch)
681           COS.changeColor(raw_ostream::CYAN, true, true);
682         if (*InputFilePtr == '\n') {
683           Newline = true;
684           COS << ' ';
685         } else
686           COS << *InputFilePtr;
687         ++InputFilePtr;
688       }
689     }
690     *LineOS << '\n';
691     unsigned InputLineWidth = InputFilePtr - InputFileLine;
692 
693     // Print any annotations.
694     while (AnnotationItr != AnnotationEnd &&
695            AnnotationItr->InputLine == Line) {
696       WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
697                     /*BG=*/false, TheColorMode);
698       // The two spaces below are where the ": " appears on input lines.
699       COS << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
700       unsigned Col;
701       for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
702         COS << ' ';
703       COS << AnnotationItr->Marker.Lead;
704       // If InputEndCol=UINT_MAX, stop at InputLineWidth.
705       for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
706            ++Col)
707         COS << '~';
708       const std::string &Note = AnnotationItr->Marker.Note;
709       if (!Note.empty()) {
710         // Put the note at the end of the input line.  If we were to instead
711         // put the note right after the marker, subsequent annotations for the
712         // same input line might appear to mark this note instead of the input
713         // line.
714         for (; Col <= InputLineWidth; ++Col)
715           COS << ' ';
716         COS << ' ' << Note;
717       }
718       COS << '\n';
719       ++AnnotationItr;
720     }
721   }
722   DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
723 
724   OS << ">>>>>>\n";
725 }
726 
727 int main(int argc, char **argv) {
728   // Enable use of ANSI color codes because FileCheck is using them to
729   // highlight text.
730   llvm::sys::Process::UseANSIEscapeCodes(true);
731 
732   InitLLVM X(argc, argv);
733   cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
734                               "FILECHECK_OPTS");
735 
736   // Select -dump-input* values.  The -help documentation specifies the default
737   // value and which value to choose if an option is specified multiple times.
738   // In the latter case, the general rule of thumb is to choose the value that
739   // provides the most information.
740   DumpInputValue DumpInput =
741       DumpInputs.empty()
742           ? DumpInputFail
743           : *std::max_element(DumpInputs.begin(), DumpInputs.end());
744   DumpInputFilterValue DumpInputFilter;
745   if (DumpInputFilters.empty())
746     DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
747                                                    : DumpInputFilterError;
748   else
749     DumpInputFilter =
750         *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end());
751   unsigned DumpInputContext = DumpInputContexts.empty()
752                                   ? 5
753                                   : *std::max_element(DumpInputContexts.begin(),
754                                                       DumpInputContexts.end());
755 
756   if (DumpInput == DumpInputHelp) {
757     DumpInputAnnotationHelp(outs());
758     return 0;
759   }
760   if (CheckFilename.empty()) {
761     errs() << "<check-file> not specified\n";
762     return 2;
763   }
764 
765   FileCheckRequest Req;
766   append_range(Req.CheckPrefixes, CheckPrefixes);
767 
768   append_range(Req.CommentPrefixes, CommentPrefixes);
769 
770   append_range(Req.ImplicitCheckNot, ImplicitCheckNot);
771 
772   bool GlobalDefineError = false;
773   for (StringRef G : GlobalDefines) {
774     size_t EqIdx = G.find('=');
775     if (EqIdx == std::string::npos) {
776       errs() << "Missing equal sign in command-line definition '-D" << G
777              << "'\n";
778       GlobalDefineError = true;
779       continue;
780     }
781     if (EqIdx == 0) {
782       errs() << "Missing variable name in command-line definition '-D" << G
783              << "'\n";
784       GlobalDefineError = true;
785       continue;
786     }
787     Req.GlobalDefines.push_back(G);
788   }
789   if (GlobalDefineError)
790     return 2;
791 
792   Req.AllowEmptyInput = AllowEmptyInput;
793   Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
794   Req.EnableVarScope = EnableVarScope;
795   Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
796   Req.Verbose = Verbose;
797   Req.VerboseVerbose = VerboseVerbose;
798   Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
799   Req.MatchFullLines = MatchFullLines;
800   Req.IgnoreCase = IgnoreCase;
801 
802   if (VerboseVerbose)
803     Req.Verbose = true;
804 
805   FileCheck FC(Req);
806   if (!FC.ValidateCheckPrefixes())
807     return 2;
808 
809   Regex PrefixRE = FC.buildCheckPrefixRegex();
810   std::string REError;
811   if (!PrefixRE.isValid(REError)) {
812     errs() << "Unable to combine check-prefix strings into a prefix regular "
813               "expression! This is likely a bug in FileCheck's verification of "
814               "the check-prefix strings. Regular expression parsing failed "
815               "with the following error: "
816            << REError << "\n";
817     return 2;
818   }
819 
820   SourceMgr SM;
821 
822   // Read the expected strings from the check file.
823   ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
824       MemoryBuffer::getFileOrSTDIN(CheckFilename, /*FileSize*/ -1,
825                                    /*RequiresNullTerminator*/ true,
826                                    /*IsText*/ true);
827   if (std::error_code EC = CheckFileOrErr.getError()) {
828     errs() << "Could not open check file '" << CheckFilename
829            << "': " << EC.message() << '\n';
830     return 2;
831   }
832   MemoryBuffer &CheckFile = *CheckFileOrErr.get();
833 
834   SmallString<4096> CheckFileBuffer;
835   StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
836 
837   unsigned CheckFileBufferID =
838       SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
839                                 CheckFileText, CheckFile.getBufferIdentifier()),
840                             SMLoc());
841 
842   std::pair<unsigned, unsigned> ImpPatBufferIDRange;
843   if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange))
844     return 2;
845 
846   // Open the file to check and add it to SourceMgr.
847   ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
848       MemoryBuffer::getFileOrSTDIN(InputFilename, /*FileSize*/ -1,
849                                    /*RequiresNullTerminator*/ true,
850                                    /*IsText*/ true);
851   if (InputFilename == "-")
852     InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
853   if (std::error_code EC = InputFileOrErr.getError()) {
854     errs() << "Could not open input file '" << InputFilename
855            << "': " << EC.message() << '\n';
856     return 2;
857   }
858   MemoryBuffer &InputFile = *InputFileOrErr.get();
859 
860   if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
861     errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
862     DumpCommandLine(argc, argv);
863     return 2;
864   }
865 
866   SmallString<4096> InputFileBuffer;
867   StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
868 
869   SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
870                             InputFileText, InputFile.getBufferIdentifier()),
871                         SMLoc());
872 
873   std::vector<FileCheckDiag> Diags;
874   int ExitCode = FC.checkInput(SM, InputFileText,
875                                DumpInput == DumpInputNever ? nullptr : &Diags)
876                      ? EXIT_SUCCESS
877                      : 1;
878   if (DumpInput == DumpInputAlways ||
879       (ExitCode == 1 && DumpInput == DumpInputFail)) {
880     errs() << "\n"
881            << "Input file: " << InputFilename << "\n"
882            << "Check file: " << CheckFilename << "\n"
883            << "\n"
884            << "-dump-input=help explains the following input dump.\n"
885            << "\n";
886     std::vector<InputAnnotation> Annotations;
887     unsigned LabelWidth;
888     BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
889                           Annotations, LabelWidth);
890     DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext,
891                        InputFileText, Annotations, LabelWidth);
892   }
893 
894   return ExitCode;
895 }
896