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