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