xref: /openbsd-src/gnu/llvm/llvm/utils/FileCheck/FileCheck.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===- FileCheck.cpp - Check that File's Contents match what is expected --===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // FileCheck does a line-by line check of a file that validates whether it
1009467b48Spatrick // contains the expected content.  This is useful for regression tests etc.
1109467b48Spatrick //
1209467b48Spatrick // This program exits with an exit status of 2 on error, exit status of 0 if
1309467b48Spatrick // the file matched the expected contents, and exit status of 1 if it did not
1409467b48Spatrick // contain the expected contents.
1509467b48Spatrick //
1609467b48Spatrick //===----------------------------------------------------------------------===//
1709467b48Spatrick 
1873471bf0Spatrick #include "llvm/FileCheck/FileCheck.h"
1909467b48Spatrick #include "llvm/Support/CommandLine.h"
2009467b48Spatrick #include "llvm/Support/InitLLVM.h"
21*d415bd75Srobert #include "llvm/Support/MemoryBuffer.h"
2209467b48Spatrick #include "llvm/Support/Process.h"
23*d415bd75Srobert #include "llvm/Support/SourceMgr.h"
2409467b48Spatrick #include "llvm/Support/WithColor.h"
2509467b48Spatrick #include "llvm/Support/raw_ostream.h"
2609467b48Spatrick #include <cmath>
2773471bf0Spatrick #include <map>
2809467b48Spatrick using namespace llvm;
2909467b48Spatrick 
3009467b48Spatrick static cl::extrahelp FileCheckOptsEnv(
3109467b48Spatrick     "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
3209467b48Spatrick     "from the command line.\n");
3309467b48Spatrick 
3409467b48Spatrick static cl::opt<std::string>
3509467b48Spatrick     CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
3609467b48Spatrick 
3709467b48Spatrick static cl::opt<std::string>
3809467b48Spatrick     InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
3909467b48Spatrick                   cl::init("-"), cl::value_desc("filename"));
4009467b48Spatrick 
4109467b48Spatrick static cl::list<std::string> CheckPrefixes(
4209467b48Spatrick     "check-prefix",
4309467b48Spatrick     cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
4409467b48Spatrick static cl::alias CheckPrefixesAlias(
4509467b48Spatrick     "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
4609467b48Spatrick     cl::NotHidden,
4709467b48Spatrick     cl::desc(
4809467b48Spatrick         "Alias for -check-prefix permitting multiple comma separated values"));
4909467b48Spatrick 
50097a140dSpatrick static cl::list<std::string> CommentPrefixes(
51097a140dSpatrick     "comment-prefixes", cl::CommaSeparated, cl::Hidden,
52097a140dSpatrick     cl::desc("Comma-separated list of comment prefixes to use from check file\n"
53097a140dSpatrick              "(defaults to 'COM,RUN'). Please avoid using this feature in\n"
54097a140dSpatrick              "LLVM's LIT-based test suites, which should be easier to\n"
55097a140dSpatrick              "maintain if they all follow a consistent comment style. This\n"
56097a140dSpatrick              "feature is meant for non-LIT test suites using FileCheck."));
57097a140dSpatrick 
5809467b48Spatrick static cl::opt<bool> NoCanonicalizeWhiteSpace(
5909467b48Spatrick     "strict-whitespace",
6009467b48Spatrick     cl::desc("Do not treat all horizontal whitespace as equivalent"));
6109467b48Spatrick 
6209467b48Spatrick static cl::opt<bool> IgnoreCase(
6309467b48Spatrick     "ignore-case",
6409467b48Spatrick     cl::desc("Use case-insensitive matching"));
6509467b48Spatrick 
6609467b48Spatrick static cl::list<std::string> ImplicitCheckNot(
6709467b48Spatrick     "implicit-check-not",
6809467b48Spatrick     cl::desc("Add an implicit negative check with this pattern to every\n"
6909467b48Spatrick              "positive check. This can be used to ensure that no instances of\n"
7009467b48Spatrick              "this pattern occur which are not matched by a positive pattern"),
7109467b48Spatrick     cl::value_desc("pattern"));
7209467b48Spatrick 
7309467b48Spatrick static cl::list<std::string>
7409467b48Spatrick     GlobalDefines("D", cl::AlwaysPrefix,
7509467b48Spatrick                   cl::desc("Define a variable to be used in capture patterns."),
7609467b48Spatrick                   cl::value_desc("VAR=VALUE"));
7709467b48Spatrick 
7809467b48Spatrick static cl::opt<bool> AllowEmptyInput(
7909467b48Spatrick     "allow-empty", cl::init(false),
8009467b48Spatrick     cl::desc("Allow the input file to be empty. This is useful when making\n"
8109467b48Spatrick              "checks that some error message does not occur, for example."));
8209467b48Spatrick 
8373471bf0Spatrick static cl::opt<bool> AllowUnusedPrefixes(
84*d415bd75Srobert     "allow-unused-prefixes",
8573471bf0Spatrick     cl::desc("Allow prefixes to be specified but not appear in the test."));
8673471bf0Spatrick 
8709467b48Spatrick static cl::opt<bool> MatchFullLines(
8809467b48Spatrick     "match-full-lines", cl::init(false),
8909467b48Spatrick     cl::desc("Require all positive matches to cover an entire input line.\n"
9009467b48Spatrick              "Allows leading and trailing whitespace if --strict-whitespace\n"
9109467b48Spatrick              "is not also passed."));
9209467b48Spatrick 
9309467b48Spatrick static cl::opt<bool> EnableVarScope(
9409467b48Spatrick     "enable-var-scope", cl::init(false),
9509467b48Spatrick     cl::desc("Enables scope for regex variables. Variables with names that\n"
9609467b48Spatrick              "do not start with '$' will be reset at the beginning of\n"
9709467b48Spatrick              "each CHECK-LABEL block."));
9809467b48Spatrick 
9909467b48Spatrick static cl::opt<bool> AllowDeprecatedDagOverlap(
10009467b48Spatrick     "allow-deprecated-dag-overlap", cl::init(false),
10109467b48Spatrick     cl::desc("Enable overlapping among matches in a group of consecutive\n"
10209467b48Spatrick              "CHECK-DAG directives.  This option is deprecated and is only\n"
10309467b48Spatrick              "provided for convenience as old tests are migrated to the new\n"
10409467b48Spatrick              "non-overlapping CHECK-DAG implementation.\n"));
10509467b48Spatrick 
10609467b48Spatrick static cl::opt<bool> Verbose(
107*d415bd75Srobert     "v",
10809467b48Spatrick     cl::desc("Print directive pattern matches, or add them to the input dump\n"
10909467b48Spatrick              "if enabled.\n"));
11009467b48Spatrick 
11109467b48Spatrick static cl::opt<bool> VerboseVerbose(
112*d415bd75Srobert     "vv",
11309467b48Spatrick     cl::desc("Print information helpful in diagnosing internal FileCheck\n"
11409467b48Spatrick              "issues, or add it to the input dump if enabled.  Implies\n"
11509467b48Spatrick              "-v.\n"));
11609467b48Spatrick 
11709467b48Spatrick // The order of DumpInputValue members affects their precedence, as documented
11809467b48Spatrick // for -dump-input below.
11909467b48Spatrick enum DumpInputValue {
12009467b48Spatrick   DumpInputNever,
12109467b48Spatrick   DumpInputFail,
12209467b48Spatrick   DumpInputAlways,
12309467b48Spatrick   DumpInputHelp
12409467b48Spatrick };
12509467b48Spatrick 
12609467b48Spatrick static cl::list<DumpInputValue> DumpInputs(
12709467b48Spatrick     "dump-input",
12809467b48Spatrick     cl::desc("Dump input to stderr, adding annotations representing\n"
12909467b48Spatrick              "currently enabled diagnostics.  When there are multiple\n"
13009467b48Spatrick              "occurrences of this option, the <value> that appears earliest\n"
131097a140dSpatrick              "in the list below has precedence.  The default is 'fail'.\n"),
13209467b48Spatrick     cl::value_desc("mode"),
133097a140dSpatrick     cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"),
13409467b48Spatrick                clEnumValN(DumpInputAlways, "always", "Always dump input"),
13509467b48Spatrick                clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
13609467b48Spatrick                clEnumValN(DumpInputNever, "never", "Never dump input")));
13709467b48Spatrick 
138097a140dSpatrick // The order of DumpInputFilterValue members affects their precedence, as
139097a140dSpatrick // documented for -dump-input-filter below.
140097a140dSpatrick enum DumpInputFilterValue {
141097a140dSpatrick   DumpInputFilterError,
142097a140dSpatrick   DumpInputFilterAnnotation,
143097a140dSpatrick   DumpInputFilterAnnotationFull,
144097a140dSpatrick   DumpInputFilterAll
145097a140dSpatrick };
146097a140dSpatrick 
147097a140dSpatrick static cl::list<DumpInputFilterValue> DumpInputFilters(
148097a140dSpatrick     "dump-input-filter",
149097a140dSpatrick     cl::desc("In the dump requested by -dump-input, print only input lines of\n"
150097a140dSpatrick              "kind <value> plus any context specified by -dump-input-context.\n"
151097a140dSpatrick              "When there are multiple occurrences of this option, the <value>\n"
152097a140dSpatrick              "that appears earliest in the list below has precedence.  The\n"
153097a140dSpatrick              "default is 'error' when -dump-input=fail, and it's 'all' when\n"
154097a140dSpatrick              "-dump-input=always.\n"),
155097a140dSpatrick     cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"),
156097a140dSpatrick                clEnumValN(DumpInputFilterAnnotationFull, "annotation-full",
157097a140dSpatrick                           "Input lines with annotations"),
158097a140dSpatrick                clEnumValN(DumpInputFilterAnnotation, "annotation",
159097a140dSpatrick                           "Input lines with starting points of annotations"),
160097a140dSpatrick                clEnumValN(DumpInputFilterError, "error",
161097a140dSpatrick                           "Input lines with starting points of error "
162097a140dSpatrick                           "annotations")));
163097a140dSpatrick 
164097a140dSpatrick static cl::list<unsigned> DumpInputContexts(
165097a140dSpatrick     "dump-input-context", cl::value_desc("N"),
166097a140dSpatrick     cl::desc("In the dump requested by -dump-input, print <N> input lines\n"
167097a140dSpatrick              "before and <N> input lines after any lines specified by\n"
168097a140dSpatrick              "-dump-input-filter.  When there are multiple occurrences of\n"
169097a140dSpatrick              "this option, the largest specified <N> has precedence.  The\n"
170097a140dSpatrick              "default is 5.\n"));
171097a140dSpatrick 
17209467b48Spatrick typedef cl::list<std::string>::const_iterator prefix_iterator;
17309467b48Spatrick 
17409467b48Spatrick 
17509467b48Spatrick 
17609467b48Spatrick 
17709467b48Spatrick 
17809467b48Spatrick 
17909467b48Spatrick 
DumpCommandLine(int argc,char ** argv)18009467b48Spatrick static void DumpCommandLine(int argc, char **argv) {
18109467b48Spatrick   errs() << "FileCheck command line: ";
18209467b48Spatrick   for (int I = 0; I < argc; I++)
18309467b48Spatrick     errs() << " " << argv[I];
18409467b48Spatrick   errs() << "\n";
18509467b48Spatrick }
18609467b48Spatrick 
18709467b48Spatrick struct MarkerStyle {
18809467b48Spatrick   /// The starting char (before tildes) for marking the line.
18909467b48Spatrick   char Lead;
19009467b48Spatrick   /// What color to use for this annotation.
19109467b48Spatrick   raw_ostream::Colors Color;
19209467b48Spatrick   /// A note to follow the marker, or empty string if none.
19309467b48Spatrick   std::string Note;
194097a140dSpatrick   /// Does this marker indicate inclusion by -dump-input-filter=error?
195097a140dSpatrick   bool FiltersAsError;
MarkerStyleMarkerStyle19609467b48Spatrick   MarkerStyle() {}
MarkerStyleMarkerStyle19709467b48Spatrick   MarkerStyle(char Lead, raw_ostream::Colors Color,
198097a140dSpatrick               const std::string &Note = "", bool FiltersAsError = false)
199097a140dSpatrick       : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) {
200097a140dSpatrick     assert((!FiltersAsError || !Note.empty()) &&
201097a140dSpatrick            "expected error diagnostic to have note");
202097a140dSpatrick   }
20309467b48Spatrick };
20409467b48Spatrick 
GetMarker(FileCheckDiag::MatchType MatchTy)20509467b48Spatrick static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
20609467b48Spatrick   switch (MatchTy) {
20709467b48Spatrick   case FileCheckDiag::MatchFoundAndExpected:
20809467b48Spatrick     return MarkerStyle('^', raw_ostream::GREEN);
20909467b48Spatrick   case FileCheckDiag::MatchFoundButExcluded:
210097a140dSpatrick     return MarkerStyle('!', raw_ostream::RED, "error: no match expected",
211097a140dSpatrick                        /*FiltersAsError=*/true);
21209467b48Spatrick   case FileCheckDiag::MatchFoundButWrongLine:
213097a140dSpatrick     return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line",
214097a140dSpatrick                        /*FiltersAsError=*/true);
21509467b48Spatrick   case FileCheckDiag::MatchFoundButDiscarded:
21609467b48Spatrick     return MarkerStyle('!', raw_ostream::CYAN,
21709467b48Spatrick                        "discard: overlaps earlier match");
21873471bf0Spatrick   case FileCheckDiag::MatchFoundErrorNote:
21973471bf0Spatrick     // Note should always be overridden within the FileCheckDiag.
22073471bf0Spatrick     return MarkerStyle('!', raw_ostream::RED,
22173471bf0Spatrick                        "error: unknown error after match",
22273471bf0Spatrick                        /*FiltersAsError=*/true);
22309467b48Spatrick   case FileCheckDiag::MatchNoneAndExcluded:
22409467b48Spatrick     return MarkerStyle('X', raw_ostream::GREEN);
22509467b48Spatrick   case FileCheckDiag::MatchNoneButExpected:
226097a140dSpatrick     return MarkerStyle('X', raw_ostream::RED, "error: no match found",
227097a140dSpatrick                        /*FiltersAsError=*/true);
22873471bf0Spatrick   case FileCheckDiag::MatchNoneForInvalidPattern:
22973471bf0Spatrick     return MarkerStyle('X', raw_ostream::RED,
23073471bf0Spatrick                        "error: match failed for invalid pattern",
23173471bf0Spatrick                        /*FiltersAsError=*/true);
23209467b48Spatrick   case FileCheckDiag::MatchFuzzy:
233097a140dSpatrick     return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
234097a140dSpatrick                        /*FiltersAsError=*/true);
23509467b48Spatrick   }
23609467b48Spatrick   llvm_unreachable_internal("unexpected match type");
23709467b48Spatrick }
23809467b48Spatrick 
DumpInputAnnotationHelp(raw_ostream & OS)23909467b48Spatrick static void DumpInputAnnotationHelp(raw_ostream &OS) {
24009467b48Spatrick   OS << "The following description was requested by -dump-input=help to\n"
241097a140dSpatrick      << "explain the input dump printed by FileCheck.\n"
242097a140dSpatrick      << "\n"
243097a140dSpatrick      << "Related command-line options:\n"
244097a140dSpatrick      << "\n"
245097a140dSpatrick      << "  - -dump-input=<value> enables or disables the input dump\n"
246097a140dSpatrick      << "  - -dump-input-filter=<value> filters the input lines\n"
247097a140dSpatrick      << "  - -dump-input-context=<N> adjusts the context of filtered lines\n"
248097a140dSpatrick      << "  - -v and -vv add more annotations\n"
249097a140dSpatrick      << "  - -color forces colors to be enabled both in the dump and below\n"
250097a140dSpatrick      << "  - -help documents the above options in more detail\n"
251097a140dSpatrick      << "\n"
252097a140dSpatrick      << "These options can also be set via FILECHECK_OPTS.  For example, for\n"
253097a140dSpatrick      << "maximum debugging output on failures:\n"
254097a140dSpatrick      << "\n"
255097a140dSpatrick      << "  $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
256097a140dSpatrick      << "\n"
257097a140dSpatrick      << "Input dump annotation format:\n"
258097a140dSpatrick      << "\n";
25909467b48Spatrick 
26009467b48Spatrick   // Labels for input lines.
26109467b48Spatrick   OS << "  - ";
26209467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
26373471bf0Spatrick   OS << "     labels line number L of the input file\n"
26473471bf0Spatrick      << "           An extra space is added after each input line to represent"
26573471bf0Spatrick      << " the\n"
26673471bf0Spatrick      << "           newline character\n";
26709467b48Spatrick 
26809467b48Spatrick   // Labels for annotation lines.
26909467b48Spatrick   OS << "  - ";
27009467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
271097a140dSpatrick   OS << "    labels the only match result for either (1) a pattern of type T"
272097a140dSpatrick      << " from\n"
273097a140dSpatrick      << "           line L of the check file if L is an integer or (2) the"
274097a140dSpatrick      << " I-th implicit\n"
275097a140dSpatrick      << "           pattern if L is \"imp\" followed by an integer "
276097a140dSpatrick      << "I (index origin one)\n";
27709467b48Spatrick   OS << "  - ";
27809467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
279097a140dSpatrick   OS << "  labels the Nth match result for such a pattern\n";
28009467b48Spatrick 
28109467b48Spatrick   // Markers on annotation lines.
28209467b48Spatrick   OS << "  - ";
28309467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
28409467b48Spatrick   OS << "    marks good match (reported if -v)\n"
28509467b48Spatrick      << "  - ";
28609467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
28709467b48Spatrick   OS << "    marks bad match, such as:\n"
28809467b48Spatrick      << "           - CHECK-NEXT on same line as previous match (error)\n"
28909467b48Spatrick      << "           - CHECK-NOT found (error)\n"
29009467b48Spatrick      << "           - CHECK-DAG overlapping match (discarded, reported if "
29109467b48Spatrick      << "-vv)\n"
29209467b48Spatrick      << "  - ";
29309467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
29409467b48Spatrick   OS << "    marks search range when no match is found, such as:\n"
29509467b48Spatrick      << "           - CHECK-NEXT not found (error)\n"
29609467b48Spatrick      << "           - CHECK-NOT not found (success, reported if -vv)\n"
29709467b48Spatrick      << "           - CHECK-DAG not found after discarded matches (error)\n"
29809467b48Spatrick      << "  - ";
29909467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
30009467b48Spatrick   OS << "      marks fuzzy match when no match is found\n";
30109467b48Spatrick 
302097a140dSpatrick   // Elided lines.
303097a140dSpatrick   OS << "  - ";
304097a140dSpatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
305097a140dSpatrick   OS << "    indicates elided input lines and annotations, as specified by\n"
306097a140dSpatrick      << "           -dump-input-filter and -dump-input-context\n";
307097a140dSpatrick 
30809467b48Spatrick   // Colors.
30909467b48Spatrick   OS << "  - colors ";
31009467b48Spatrick   WithColor(OS, raw_ostream::GREEN, true) << "success";
31109467b48Spatrick   OS << ", ";
31209467b48Spatrick   WithColor(OS, raw_ostream::RED, true) << "error";
31309467b48Spatrick   OS << ", ";
31409467b48Spatrick   WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
31509467b48Spatrick   OS << ", ";
31609467b48Spatrick   WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
31709467b48Spatrick   OS << ", ";
31809467b48Spatrick   WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
319097a140dSpatrick   OS << "\n";
32009467b48Spatrick }
32109467b48Spatrick 
32209467b48Spatrick /// An annotation for a single input line.
32309467b48Spatrick struct InputAnnotation {
324097a140dSpatrick   /// The index of the match result across all checks
325097a140dSpatrick   unsigned DiagIndex;
32609467b48Spatrick   /// The label for this annotation.
32709467b48Spatrick   std::string Label;
328097a140dSpatrick   /// Is this the initial fragment of a diagnostic that has been broken across
329097a140dSpatrick   /// multiple lines?
330097a140dSpatrick   bool IsFirstLine;
33109467b48Spatrick   /// What input line (one-origin indexing) this annotation marks.  This might
332097a140dSpatrick   /// be different from the starting line of the original diagnostic if
333097a140dSpatrick   /// !IsFirstLine.
33409467b48Spatrick   unsigned InputLine;
335097a140dSpatrick   /// The column range (one-origin indexing, open end) in which to mark the
33609467b48Spatrick   /// input line.  If InputEndCol is UINT_MAX, treat it as the last column
33709467b48Spatrick   /// before the newline.
33809467b48Spatrick   unsigned InputStartCol, InputEndCol;
33909467b48Spatrick   /// The marker to use.
34009467b48Spatrick   MarkerStyle Marker;
34109467b48Spatrick   /// Whether this annotation represents a good match for an expected pattern.
34209467b48Spatrick   bool FoundAndExpectedMatch;
34309467b48Spatrick };
34409467b48Spatrick 
34509467b48Spatrick /// Get an abbreviation for the check type.
GetCheckTypeAbbreviation(Check::FileCheckType Ty)34673471bf0Spatrick static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
34709467b48Spatrick   switch (Ty) {
34809467b48Spatrick   case Check::CheckPlain:
34909467b48Spatrick     if (Ty.getCount() > 1)
35009467b48Spatrick       return "count";
35109467b48Spatrick     return "check";
35209467b48Spatrick   case Check::CheckNext:
35309467b48Spatrick     return "next";
35409467b48Spatrick   case Check::CheckSame:
35509467b48Spatrick     return "same";
35609467b48Spatrick   case Check::CheckNot:
35709467b48Spatrick     return "not";
35809467b48Spatrick   case Check::CheckDAG:
35909467b48Spatrick     return "dag";
36009467b48Spatrick   case Check::CheckLabel:
36109467b48Spatrick     return "label";
36209467b48Spatrick   case Check::CheckEmpty:
36309467b48Spatrick     return "empty";
364097a140dSpatrick   case Check::CheckComment:
365097a140dSpatrick     return "com";
36609467b48Spatrick   case Check::CheckEOF:
36709467b48Spatrick     return "eof";
36809467b48Spatrick   case Check::CheckBadNot:
36909467b48Spatrick     return "bad-not";
37009467b48Spatrick   case Check::CheckBadCount:
37109467b48Spatrick     return "bad-count";
372*d415bd75Srobert   case Check::CheckMisspelled:
373*d415bd75Srobert     return "misspelled";
37409467b48Spatrick   case Check::CheckNone:
37509467b48Spatrick     llvm_unreachable("invalid FileCheckType");
37609467b48Spatrick   }
37709467b48Spatrick   llvm_unreachable("unknown FileCheckType");
37809467b48Spatrick }
37909467b48Spatrick 
380097a140dSpatrick static void
BuildInputAnnotations(const SourceMgr & SM,unsigned CheckFileBufferID,const std::pair<unsigned,unsigned> & ImpPatBufferIDRange,const std::vector<FileCheckDiag> & Diags,std::vector<InputAnnotation> & Annotations,unsigned & LabelWidth)381097a140dSpatrick BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
382097a140dSpatrick                       const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
383097a140dSpatrick                       const std::vector<FileCheckDiag> &Diags,
38409467b48Spatrick                       std::vector<InputAnnotation> &Annotations,
38509467b48Spatrick                       unsigned &LabelWidth) {
38673471bf0Spatrick   struct CompareSMLoc {
38773471bf0Spatrick     bool operator()(const SMLoc &LHS, const SMLoc &RHS) const {
38873471bf0Spatrick       return LHS.getPointer() < RHS.getPointer();
38973471bf0Spatrick     }
39073471bf0Spatrick   };
39173471bf0Spatrick   // How many diagnostics does each pattern have?
39273471bf0Spatrick   std::map<SMLoc, unsigned, CompareSMLoc> DiagCountPerPattern;
39373471bf0Spatrick   for (auto Diag : Diags)
39473471bf0Spatrick     ++DiagCountPerPattern[Diag.CheckLoc];
39573471bf0Spatrick   // How many diagnostics have we seen so far per pattern?
39673471bf0Spatrick   std::map<SMLoc, unsigned, CompareSMLoc> DiagIndexPerPattern;
39773471bf0Spatrick   // How many total diagnostics have we seen so far?
39873471bf0Spatrick   unsigned DiagIndex = 0;
39909467b48Spatrick   // What's the widest label?
40009467b48Spatrick   LabelWidth = 0;
40109467b48Spatrick   for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
40209467b48Spatrick        ++DiagItr) {
40309467b48Spatrick     InputAnnotation A;
40473471bf0Spatrick     A.DiagIndex = DiagIndex++;
40509467b48Spatrick 
40609467b48Spatrick     // Build label, which uniquely identifies this check result.
407097a140dSpatrick     unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
408097a140dSpatrick     auto CheckLineAndCol =
409097a140dSpatrick         SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
41009467b48Spatrick     llvm::raw_string_ostream Label(A.Label);
411097a140dSpatrick     Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
412097a140dSpatrick     if (CheckBufferID == CheckFileBufferID)
413097a140dSpatrick       Label << CheckLineAndCol.first;
414097a140dSpatrick     else if (ImpPatBufferIDRange.first <= CheckBufferID &&
415097a140dSpatrick              CheckBufferID < ImpPatBufferIDRange.second)
416097a140dSpatrick       Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
417097a140dSpatrick     else
418097a140dSpatrick       llvm_unreachable("expected diagnostic's check location to be either in "
419097a140dSpatrick                        "the check file or for an implicit pattern");
42073471bf0Spatrick     if (DiagCountPerPattern[DiagItr->CheckLoc] > 1)
42173471bf0Spatrick       Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++;
42209467b48Spatrick     LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
42309467b48Spatrick 
42409467b48Spatrick     A.Marker = GetMarker(DiagItr->MatchTy);
42573471bf0Spatrick     if (!DiagItr->Note.empty()) {
42673471bf0Spatrick       A.Marker.Note = DiagItr->Note;
42773471bf0Spatrick       // It's less confusing if notes that don't actually have ranges don't have
42873471bf0Spatrick       // markers.  For example, a marker for 'with "VAR" equal to "5"' would
42973471bf0Spatrick       // seem to indicate where "VAR" matches, but the location we actually have
43073471bf0Spatrick       // for the marker simply points to the start of the match/search range for
43173471bf0Spatrick       // the full pattern of which the substitution is potentially just one
43273471bf0Spatrick       // component.
43373471bf0Spatrick       if (DiagItr->InputStartLine == DiagItr->InputEndLine &&
43473471bf0Spatrick           DiagItr->InputStartCol == DiagItr->InputEndCol)
43573471bf0Spatrick         A.Marker.Lead = ' ';
43673471bf0Spatrick     }
43773471bf0Spatrick     if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
43873471bf0Spatrick       assert(!DiagItr->Note.empty() &&
43973471bf0Spatrick              "expected custom note for MatchFoundErrorNote");
44073471bf0Spatrick       A.Marker.Note = "error: " + A.Marker.Note;
44173471bf0Spatrick     }
44209467b48Spatrick     A.FoundAndExpectedMatch =
44309467b48Spatrick         DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
44409467b48Spatrick 
44509467b48Spatrick     // Compute the mark location, and break annotation into multiple
44609467b48Spatrick     // annotations if it spans multiple lines.
447097a140dSpatrick     A.IsFirstLine = true;
44809467b48Spatrick     A.InputLine = DiagItr->InputStartLine;
44909467b48Spatrick     A.InputStartCol = DiagItr->InputStartCol;
45009467b48Spatrick     if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
45109467b48Spatrick       // Sometimes ranges are empty in order to indicate a specific point, but
45209467b48Spatrick       // that would mean nothing would be marked, so adjust the range to
45309467b48Spatrick       // include the following character.
45409467b48Spatrick       A.InputEndCol =
45509467b48Spatrick           std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
45609467b48Spatrick       Annotations.push_back(A);
45709467b48Spatrick     } else {
45809467b48Spatrick       assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
45909467b48Spatrick              "expected input range not to be inverted");
46009467b48Spatrick       A.InputEndCol = UINT_MAX;
46109467b48Spatrick       Annotations.push_back(A);
46209467b48Spatrick       for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
46309467b48Spatrick            L <= E; ++L) {
46409467b48Spatrick         // If a range ends before the first column on a line, then it has no
46509467b48Spatrick         // characters on that line, so there's nothing to render.
46609467b48Spatrick         if (DiagItr->InputEndCol == 1 && L == E)
46709467b48Spatrick           break;
46809467b48Spatrick         InputAnnotation B;
469097a140dSpatrick         B.DiagIndex = A.DiagIndex;
47009467b48Spatrick         B.Label = A.Label;
471097a140dSpatrick         B.IsFirstLine = false;
47209467b48Spatrick         B.InputLine = L;
47309467b48Spatrick         B.Marker = A.Marker;
47409467b48Spatrick         B.Marker.Lead = '~';
47509467b48Spatrick         B.Marker.Note = "";
47609467b48Spatrick         B.InputStartCol = 1;
47709467b48Spatrick         if (L != E)
47809467b48Spatrick           B.InputEndCol = UINT_MAX;
47909467b48Spatrick         else
48009467b48Spatrick           B.InputEndCol = DiagItr->InputEndCol;
48109467b48Spatrick         B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
48209467b48Spatrick         Annotations.push_back(B);
48309467b48Spatrick       }
48409467b48Spatrick     }
48509467b48Spatrick   }
48609467b48Spatrick }
48709467b48Spatrick 
FindInputLineInFilter(DumpInputFilterValue DumpInputFilter,unsigned CurInputLine,const std::vector<InputAnnotation>::iterator & AnnotationBeg,const std::vector<InputAnnotation>::iterator & AnnotationEnd)488097a140dSpatrick static unsigned FindInputLineInFilter(
489097a140dSpatrick     DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
490097a140dSpatrick     const std::vector<InputAnnotation>::iterator &AnnotationBeg,
491097a140dSpatrick     const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
492097a140dSpatrick   if (DumpInputFilter == DumpInputFilterAll)
493097a140dSpatrick     return CurInputLine;
494097a140dSpatrick   for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
495097a140dSpatrick        ++AnnotationItr) {
496097a140dSpatrick     switch (DumpInputFilter) {
497097a140dSpatrick     case DumpInputFilterAll:
498097a140dSpatrick       llvm_unreachable("unexpected DumpInputFilterAll");
499097a140dSpatrick       break;
500097a140dSpatrick     case DumpInputFilterAnnotationFull:
501097a140dSpatrick       return AnnotationItr->InputLine;
502097a140dSpatrick     case DumpInputFilterAnnotation:
503097a140dSpatrick       if (AnnotationItr->IsFirstLine)
504097a140dSpatrick         return AnnotationItr->InputLine;
505097a140dSpatrick       break;
506097a140dSpatrick     case DumpInputFilterError:
507097a140dSpatrick       if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
508097a140dSpatrick         return AnnotationItr->InputLine;
509097a140dSpatrick       break;
510097a140dSpatrick     }
511097a140dSpatrick   }
512097a140dSpatrick   return UINT_MAX;
513097a140dSpatrick }
514097a140dSpatrick 
515097a140dSpatrick /// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would
516097a140dSpatrick /// occupy less lines than ElidedLines, but print ElidedLines otherwise.  Either
517097a140dSpatrick /// way, clear ElidedLines.  Thus, if ElidedLines is empty, do nothing.
DumpEllipsisOrElidedLines(raw_ostream & OS,std::string & ElidedLines,unsigned LabelWidth)518097a140dSpatrick static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
519097a140dSpatrick                                       unsigned LabelWidth) {
520097a140dSpatrick   if (ElidedLines.empty())
521097a140dSpatrick     return;
522097a140dSpatrick   unsigned EllipsisLines = 3;
523097a140dSpatrick   if (EllipsisLines < StringRef(ElidedLines).count('\n')) {
524097a140dSpatrick     for (unsigned i = 0; i < EllipsisLines; ++i) {
525097a140dSpatrick       WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
526097a140dSpatrick           << right_justify(".", LabelWidth);
527097a140dSpatrick       OS << '\n';
528097a140dSpatrick     }
529097a140dSpatrick   } else
530097a140dSpatrick     OS << ElidedLines;
531097a140dSpatrick   ElidedLines.clear();
532097a140dSpatrick }
533097a140dSpatrick 
DumpAnnotatedInput(raw_ostream & OS,const FileCheckRequest & Req,DumpInputFilterValue DumpInputFilter,unsigned DumpInputContext,StringRef InputFileText,std::vector<InputAnnotation> & Annotations,unsigned LabelWidth)53409467b48Spatrick static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
535097a140dSpatrick                                DumpInputFilterValue DumpInputFilter,
536097a140dSpatrick                                unsigned DumpInputContext,
53709467b48Spatrick                                StringRef InputFileText,
53809467b48Spatrick                                std::vector<InputAnnotation> &Annotations,
53909467b48Spatrick                                unsigned LabelWidth) {
540097a140dSpatrick   OS << "Input was:\n<<<<<<\n";
54109467b48Spatrick 
54209467b48Spatrick   // Sort annotations.
54373471bf0Spatrick   llvm::sort(Annotations,
54409467b48Spatrick              [](const InputAnnotation &A, const InputAnnotation &B) {
545097a140dSpatrick                // 1. Sort annotations in the order of the input lines.
546097a140dSpatrick                //
547097a140dSpatrick                // This makes it easier to find relevant annotations while
548097a140dSpatrick                // iterating input lines in the implementation below.  FileCheck
549097a140dSpatrick                // does not always produce diagnostics in the order of input
550097a140dSpatrick                // lines due to, for example, CHECK-DAG and CHECK-NOT.
55109467b48Spatrick                if (A.InputLine != B.InputLine)
55209467b48Spatrick                  return A.InputLine < B.InputLine;
553097a140dSpatrick                // 2. Sort annotations in the temporal order FileCheck produced
554097a140dSpatrick                // their associated diagnostics.
555097a140dSpatrick                //
556097a140dSpatrick                // This sort offers several benefits:
557097a140dSpatrick                //
558097a140dSpatrick                // A. On a single input line, the order of annotations reflects
559097a140dSpatrick                //    the FileCheck logic for processing directives/patterns.
560097a140dSpatrick                //    This can be helpful in understanding cases in which the
561097a140dSpatrick                //    order of the associated directives/patterns in the check
562097a140dSpatrick                //    file or on the command line either (i) does not match the
563097a140dSpatrick                //    temporal order in which FileCheck looks for matches for the
564097a140dSpatrick                //    directives/patterns (due to, for example, CHECK-LABEL,
565097a140dSpatrick                //    CHECK-NOT, or `--implicit-check-not`) or (ii) does match
566097a140dSpatrick                //    that order but does not match the order of those
567097a140dSpatrick                //    diagnostics along an input line (due to, for example,
568097a140dSpatrick                //    CHECK-DAG).
569097a140dSpatrick                //
570097a140dSpatrick                //    On the other hand, because our presentation format presents
571097a140dSpatrick                //    input lines in order, there's no clear way to offer the
572097a140dSpatrick                //    same benefit across input lines.  For consistency, it might
573097a140dSpatrick                //    then seem worthwhile to have annotations on a single line
574097a140dSpatrick                //    also sorted in input order (that is, by input column).
575097a140dSpatrick                //    However, in practice, this appears to be more confusing
576097a140dSpatrick                //    than helpful.  Perhaps it's intuitive to expect annotations
577097a140dSpatrick                //    to be listed in the temporal order in which they were
578097a140dSpatrick                //    produced except in cases the presentation format obviously
579097a140dSpatrick                //    and inherently cannot support it (that is, across input
580097a140dSpatrick                //    lines).
581097a140dSpatrick                //
582097a140dSpatrick                // B. When diagnostics' annotations are split among multiple
583097a140dSpatrick                //    input lines, the user must track them from one input line
584097a140dSpatrick                //    to the next.  One property of the sort chosen here is that
585097a140dSpatrick                //    it facilitates the user in this regard by ensuring the
586097a140dSpatrick                //    following: when comparing any two input lines, a
587097a140dSpatrick                //    diagnostic's annotations are sorted in the same position
588097a140dSpatrick                //    relative to all other diagnostics' annotations.
589097a140dSpatrick                return A.DiagIndex < B.DiagIndex;
59009467b48Spatrick              });
59109467b48Spatrick 
59209467b48Spatrick   // Compute the width of the label column.
59309467b48Spatrick   const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
59409467b48Spatrick                       *InputFileEnd = InputFileText.bytes_end();
59509467b48Spatrick   unsigned LineCount = InputFileText.count('\n');
59609467b48Spatrick   if (InputFileEnd[-1] != '\n')
59709467b48Spatrick     ++LineCount;
59809467b48Spatrick   unsigned LineNoWidth = std::log10(LineCount) + 1;
59909467b48Spatrick   // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
60009467b48Spatrick   // on input lines and (2) to the right of the (left-aligned) labels on
60109467b48Spatrick   // annotation lines so that input lines and annotation lines are more
60209467b48Spatrick   // visually distinct.  For example, the spaces on the annotation lines ensure
60309467b48Spatrick   // that input line numbers and check directive line numbers never align
60409467b48Spatrick   // horizontally.  Those line numbers might not even be for the same file.
60509467b48Spatrick   // One space would be enough to achieve that, but more makes it even easier
60609467b48Spatrick   // to see.
60709467b48Spatrick   LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
60809467b48Spatrick 
60909467b48Spatrick   // Print annotated input lines.
610097a140dSpatrick   unsigned PrevLineInFilter = 0; // 0 means none so far
611097a140dSpatrick   unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
612097a140dSpatrick   std::string ElidedLines;
613097a140dSpatrick   raw_string_ostream ElidedLinesOS(ElidedLines);
614097a140dSpatrick   ColorMode TheColorMode =
615097a140dSpatrick       WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
616097a140dSpatrick   if (TheColorMode == ColorMode::Enable)
617097a140dSpatrick     ElidedLinesOS.enable_colors(true);
61809467b48Spatrick   auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
61909467b48Spatrick   for (unsigned Line = 1;
62009467b48Spatrick        InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
62109467b48Spatrick        ++Line) {
62209467b48Spatrick     const unsigned char *InputFileLine = InputFilePtr;
62309467b48Spatrick 
624097a140dSpatrick     // Compute the previous and next line included by the filter.
625097a140dSpatrick     if (NextLineInFilter < Line)
626097a140dSpatrick       NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
627097a140dSpatrick                                                AnnotationItr, AnnotationEnd);
628097a140dSpatrick     assert(NextLineInFilter && "expected NextLineInFilter to be computed");
629097a140dSpatrick     if (NextLineInFilter == Line)
630097a140dSpatrick       PrevLineInFilter = Line;
631097a140dSpatrick 
632097a140dSpatrick     // Elide this input line and its annotations if it's not within the
633097a140dSpatrick     // context specified by -dump-input-context of an input line included by
634097a140dSpatrick     // -dump-input-filter.  However, in case the resulting ellipsis would occupy
635097a140dSpatrick     // more lines than the input lines and annotations it elides, buffer the
636097a140dSpatrick     // elided lines and annotations so we can print them instead.
637*d415bd75Srobert     raw_ostream *LineOS;
638097a140dSpatrick     if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
639097a140dSpatrick         (NextLineInFilter == UINT_MAX ||
640097a140dSpatrick          Line + DumpInputContext < NextLineInFilter))
641097a140dSpatrick       LineOS = &ElidedLinesOS;
642097a140dSpatrick     else {
643097a140dSpatrick       LineOS = &OS;
644097a140dSpatrick       DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
645097a140dSpatrick     }
646097a140dSpatrick 
64709467b48Spatrick     // Print right-aligned line number.
648097a140dSpatrick     WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
649097a140dSpatrick               TheColorMode)
65009467b48Spatrick         << format_decimal(Line, LabelWidth) << ": ";
65109467b48Spatrick 
65209467b48Spatrick     // For the case where -v and colors are enabled, find the annotations for
65309467b48Spatrick     // good matches for expected patterns in order to highlight everything
65409467b48Spatrick     // else in the line.  There are no such annotations if -v is disabled.
65509467b48Spatrick     std::vector<InputAnnotation> FoundAndExpectedMatches;
656097a140dSpatrick     if (Req.Verbose && TheColorMode == ColorMode::Enable) {
65709467b48Spatrick       for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
65809467b48Spatrick            ++I) {
65909467b48Spatrick         if (I->FoundAndExpectedMatch)
66009467b48Spatrick           FoundAndExpectedMatches.push_back(*I);
66109467b48Spatrick       }
66209467b48Spatrick     }
66309467b48Spatrick 
66409467b48Spatrick     // Print numbered line with highlighting where there are no matches for
66509467b48Spatrick     // expected patterns.
66609467b48Spatrick     bool Newline = false;
66709467b48Spatrick     {
668097a140dSpatrick       WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
669097a140dSpatrick                     /*BG=*/false, TheColorMode);
67009467b48Spatrick       bool InMatch = false;
67109467b48Spatrick       if (Req.Verbose)
67209467b48Spatrick         COS.changeColor(raw_ostream::CYAN, true, true);
67309467b48Spatrick       for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
67409467b48Spatrick         bool WasInMatch = InMatch;
67509467b48Spatrick         InMatch = false;
67609467b48Spatrick         for (auto M : FoundAndExpectedMatches) {
67709467b48Spatrick           if (M.InputStartCol <= Col && Col < M.InputEndCol) {
67809467b48Spatrick             InMatch = true;
67909467b48Spatrick             break;
68009467b48Spatrick           }
68109467b48Spatrick         }
68209467b48Spatrick         if (!WasInMatch && InMatch)
68309467b48Spatrick           COS.resetColor();
68409467b48Spatrick         else if (WasInMatch && !InMatch)
68509467b48Spatrick           COS.changeColor(raw_ostream::CYAN, true, true);
68673471bf0Spatrick         if (*InputFilePtr == '\n') {
68709467b48Spatrick           Newline = true;
68873471bf0Spatrick           COS << ' ';
68973471bf0Spatrick         } else
69009467b48Spatrick           COS << *InputFilePtr;
69109467b48Spatrick         ++InputFilePtr;
69209467b48Spatrick       }
69309467b48Spatrick     }
694097a140dSpatrick     *LineOS << '\n';
69573471bf0Spatrick     unsigned InputLineWidth = InputFilePtr - InputFileLine;
69609467b48Spatrick 
69709467b48Spatrick     // Print any annotations.
69809467b48Spatrick     while (AnnotationItr != AnnotationEnd &&
69909467b48Spatrick            AnnotationItr->InputLine == Line) {
700097a140dSpatrick       WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
701097a140dSpatrick                     /*BG=*/false, TheColorMode);
70209467b48Spatrick       // The two spaces below are where the ": " appears on input lines.
70309467b48Spatrick       COS << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
70409467b48Spatrick       unsigned Col;
70509467b48Spatrick       for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
70609467b48Spatrick         COS << ' ';
70709467b48Spatrick       COS << AnnotationItr->Marker.Lead;
70809467b48Spatrick       // If InputEndCol=UINT_MAX, stop at InputLineWidth.
70909467b48Spatrick       for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
71009467b48Spatrick            ++Col)
71109467b48Spatrick         COS << '~';
71209467b48Spatrick       const std::string &Note = AnnotationItr->Marker.Note;
71309467b48Spatrick       if (!Note.empty()) {
71409467b48Spatrick         // Put the note at the end of the input line.  If we were to instead
71509467b48Spatrick         // put the note right after the marker, subsequent annotations for the
71609467b48Spatrick         // same input line might appear to mark this note instead of the input
71709467b48Spatrick         // line.
71809467b48Spatrick         for (; Col <= InputLineWidth; ++Col)
71909467b48Spatrick           COS << ' ';
72009467b48Spatrick         COS << ' ' << Note;
72109467b48Spatrick       }
72209467b48Spatrick       COS << '\n';
72309467b48Spatrick       ++AnnotationItr;
72409467b48Spatrick     }
72509467b48Spatrick   }
726097a140dSpatrick   DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
72709467b48Spatrick 
72809467b48Spatrick   OS << ">>>>>>\n";
72909467b48Spatrick }
73009467b48Spatrick 
main(int argc,char ** argv)73109467b48Spatrick int main(int argc, char **argv) {
73209467b48Spatrick   // Enable use of ANSI color codes because FileCheck is using them to
73309467b48Spatrick   // highlight text.
73409467b48Spatrick   llvm::sys::Process::UseANSIEscapeCodes(true);
73509467b48Spatrick 
73609467b48Spatrick   InitLLVM X(argc, argv);
73709467b48Spatrick   cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
73809467b48Spatrick                               "FILECHECK_OPTS");
739097a140dSpatrick 
740097a140dSpatrick   // Select -dump-input* values.  The -help documentation specifies the default
741097a140dSpatrick   // value and which value to choose if an option is specified multiple times.
742097a140dSpatrick   // In the latter case, the general rule of thumb is to choose the value that
743097a140dSpatrick   // provides the most information.
74409467b48Spatrick   DumpInputValue DumpInput =
74509467b48Spatrick       DumpInputs.empty()
746097a140dSpatrick           ? DumpInputFail
74709467b48Spatrick           : *std::max_element(DumpInputs.begin(), DumpInputs.end());
748097a140dSpatrick   DumpInputFilterValue DumpInputFilter;
749097a140dSpatrick   if (DumpInputFilters.empty())
750097a140dSpatrick     DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
751097a140dSpatrick                                                    : DumpInputFilterError;
752097a140dSpatrick   else
753097a140dSpatrick     DumpInputFilter =
754097a140dSpatrick         *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end());
755097a140dSpatrick   unsigned DumpInputContext = DumpInputContexts.empty()
756097a140dSpatrick                                   ? 5
757097a140dSpatrick                                   : *std::max_element(DumpInputContexts.begin(),
758097a140dSpatrick                                                       DumpInputContexts.end());
759097a140dSpatrick 
76009467b48Spatrick   if (DumpInput == DumpInputHelp) {
76109467b48Spatrick     DumpInputAnnotationHelp(outs());
76209467b48Spatrick     return 0;
76309467b48Spatrick   }
76409467b48Spatrick   if (CheckFilename.empty()) {
76509467b48Spatrick     errs() << "<check-file> not specified\n";
76609467b48Spatrick     return 2;
76709467b48Spatrick   }
76809467b48Spatrick 
76909467b48Spatrick   FileCheckRequest Req;
77073471bf0Spatrick   append_range(Req.CheckPrefixes, CheckPrefixes);
77109467b48Spatrick 
77273471bf0Spatrick   append_range(Req.CommentPrefixes, CommentPrefixes);
773097a140dSpatrick 
77473471bf0Spatrick   append_range(Req.ImplicitCheckNot, ImplicitCheckNot);
77509467b48Spatrick 
77609467b48Spatrick   bool GlobalDefineError = false;
777097a140dSpatrick   for (StringRef G : GlobalDefines) {
77809467b48Spatrick     size_t EqIdx = G.find('=');
77909467b48Spatrick     if (EqIdx == std::string::npos) {
78009467b48Spatrick       errs() << "Missing equal sign in command-line definition '-D" << G
78109467b48Spatrick              << "'\n";
78209467b48Spatrick       GlobalDefineError = true;
78309467b48Spatrick       continue;
78409467b48Spatrick     }
78509467b48Spatrick     if (EqIdx == 0) {
78609467b48Spatrick       errs() << "Missing variable name in command-line definition '-D" << G
78709467b48Spatrick              << "'\n";
78809467b48Spatrick       GlobalDefineError = true;
78909467b48Spatrick       continue;
79009467b48Spatrick     }
79109467b48Spatrick     Req.GlobalDefines.push_back(G);
79209467b48Spatrick   }
79309467b48Spatrick   if (GlobalDefineError)
79409467b48Spatrick     return 2;
79509467b48Spatrick 
79609467b48Spatrick   Req.AllowEmptyInput = AllowEmptyInput;
79773471bf0Spatrick   Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
79809467b48Spatrick   Req.EnableVarScope = EnableVarScope;
79909467b48Spatrick   Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
80009467b48Spatrick   Req.Verbose = Verbose;
80109467b48Spatrick   Req.VerboseVerbose = VerboseVerbose;
80209467b48Spatrick   Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
80309467b48Spatrick   Req.MatchFullLines = MatchFullLines;
80409467b48Spatrick   Req.IgnoreCase = IgnoreCase;
80509467b48Spatrick 
80609467b48Spatrick   if (VerboseVerbose)
80709467b48Spatrick     Req.Verbose = true;
80809467b48Spatrick 
80909467b48Spatrick   FileCheck FC(Req);
810097a140dSpatrick   if (!FC.ValidateCheckPrefixes())
81109467b48Spatrick     return 2;
81209467b48Spatrick 
81309467b48Spatrick   Regex PrefixRE = FC.buildCheckPrefixRegex();
81409467b48Spatrick   std::string REError;
81509467b48Spatrick   if (!PrefixRE.isValid(REError)) {
81609467b48Spatrick     errs() << "Unable to combine check-prefix strings into a prefix regular "
81709467b48Spatrick               "expression! This is likely a bug in FileCheck's verification of "
81809467b48Spatrick               "the check-prefix strings. Regular expression parsing failed "
81909467b48Spatrick               "with the following error: "
82009467b48Spatrick            << REError << "\n";
82109467b48Spatrick     return 2;
82209467b48Spatrick   }
82309467b48Spatrick 
82409467b48Spatrick   SourceMgr SM;
82509467b48Spatrick 
82609467b48Spatrick   // Read the expected strings from the check file.
82709467b48Spatrick   ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
82873471bf0Spatrick       MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true);
82909467b48Spatrick   if (std::error_code EC = CheckFileOrErr.getError()) {
83009467b48Spatrick     errs() << "Could not open check file '" << CheckFilename
83109467b48Spatrick            << "': " << EC.message() << '\n';
83209467b48Spatrick     return 2;
83309467b48Spatrick   }
83409467b48Spatrick   MemoryBuffer &CheckFile = *CheckFileOrErr.get();
83509467b48Spatrick 
83609467b48Spatrick   SmallString<4096> CheckFileBuffer;
83709467b48Spatrick   StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
83809467b48Spatrick 
839097a140dSpatrick   unsigned CheckFileBufferID =
84009467b48Spatrick       SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
84109467b48Spatrick                                 CheckFileText, CheckFile.getBufferIdentifier()),
84209467b48Spatrick                             SMLoc());
84309467b48Spatrick 
844097a140dSpatrick   std::pair<unsigned, unsigned> ImpPatBufferIDRange;
845097a140dSpatrick   if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange))
84609467b48Spatrick     return 2;
84709467b48Spatrick 
84809467b48Spatrick   // Open the file to check and add it to SourceMgr.
84909467b48Spatrick   ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
85073471bf0Spatrick       MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
851097a140dSpatrick   if (InputFilename == "-")
852097a140dSpatrick     InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
85309467b48Spatrick   if (std::error_code EC = InputFileOrErr.getError()) {
85409467b48Spatrick     errs() << "Could not open input file '" << InputFilename
85509467b48Spatrick            << "': " << EC.message() << '\n';
85609467b48Spatrick     return 2;
85709467b48Spatrick   }
85809467b48Spatrick   MemoryBuffer &InputFile = *InputFileOrErr.get();
85909467b48Spatrick 
86009467b48Spatrick   if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
86109467b48Spatrick     errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
86209467b48Spatrick     DumpCommandLine(argc, argv);
86309467b48Spatrick     return 2;
86409467b48Spatrick   }
86509467b48Spatrick 
86609467b48Spatrick   SmallString<4096> InputFileBuffer;
86709467b48Spatrick   StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
86809467b48Spatrick 
86909467b48Spatrick   SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
87009467b48Spatrick                             InputFileText, InputFile.getBufferIdentifier()),
87109467b48Spatrick                         SMLoc());
87209467b48Spatrick 
87309467b48Spatrick   std::vector<FileCheckDiag> Diags;
87409467b48Spatrick   int ExitCode = FC.checkInput(SM, InputFileText,
87509467b48Spatrick                                DumpInput == DumpInputNever ? nullptr : &Diags)
87609467b48Spatrick                      ? EXIT_SUCCESS
87709467b48Spatrick                      : 1;
87809467b48Spatrick   if (DumpInput == DumpInputAlways ||
87909467b48Spatrick       (ExitCode == 1 && DumpInput == DumpInputFail)) {
88009467b48Spatrick     errs() << "\n"
881097a140dSpatrick            << "Input file: " << InputFilename << "\n"
88209467b48Spatrick            << "Check file: " << CheckFilename << "\n"
88309467b48Spatrick            << "\n"
884097a140dSpatrick            << "-dump-input=help explains the following input dump.\n"
88509467b48Spatrick            << "\n";
88609467b48Spatrick     std::vector<InputAnnotation> Annotations;
88709467b48Spatrick     unsigned LabelWidth;
888097a140dSpatrick     BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
889097a140dSpatrick                           Annotations, LabelWidth);
890097a140dSpatrick     DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext,
891097a140dSpatrick                        InputFileText, Annotations, LabelWidth);
89209467b48Spatrick   }
89309467b48Spatrick 
89409467b48Spatrick   return ExitCode;
89509467b48Spatrick }
896