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