xref: /openbsd-src/gnu/llvm/llvm/utils/FileCheck/FileCheck.cpp (revision 09467b48e8bc8b4905716062da846024139afbf2)
1*09467b48Spatrick //===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2*09467b48Spatrick //
3*09467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*09467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*09467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*09467b48Spatrick //
7*09467b48Spatrick //===----------------------------------------------------------------------===//
8*09467b48Spatrick //
9*09467b48Spatrick // FileCheck does a line-by line check of a file that validates whether it
10*09467b48Spatrick // contains the expected content.  This is useful for regression tests etc.
11*09467b48Spatrick //
12*09467b48Spatrick // This program exits with an exit status of 2 on error, exit status of 0 if
13*09467b48Spatrick // the file matched the expected contents, and exit status of 1 if it did not
14*09467b48Spatrick // contain the expected contents.
15*09467b48Spatrick //
16*09467b48Spatrick //===----------------------------------------------------------------------===//
17*09467b48Spatrick 
18*09467b48Spatrick #include "llvm/Support/CommandLine.h"
19*09467b48Spatrick #include "llvm/Support/InitLLVM.h"
20*09467b48Spatrick #include "llvm/Support/Process.h"
21*09467b48Spatrick #include "llvm/Support/WithColor.h"
22*09467b48Spatrick #include "llvm/Support/raw_ostream.h"
23*09467b48Spatrick #include "llvm/Support/FileCheck.h"
24*09467b48Spatrick #include <cmath>
25*09467b48Spatrick using namespace llvm;
26*09467b48Spatrick 
27*09467b48Spatrick static cl::extrahelp FileCheckOptsEnv(
28*09467b48Spatrick     "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
29*09467b48Spatrick     "from the command line.\n");
30*09467b48Spatrick 
31*09467b48Spatrick static cl::opt<std::string>
32*09467b48Spatrick     CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
33*09467b48Spatrick 
34*09467b48Spatrick static cl::opt<std::string>
35*09467b48Spatrick     InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
36*09467b48Spatrick                   cl::init("-"), cl::value_desc("filename"));
37*09467b48Spatrick 
38*09467b48Spatrick static cl::list<std::string> CheckPrefixes(
39*09467b48Spatrick     "check-prefix",
40*09467b48Spatrick     cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
41*09467b48Spatrick static cl::alias CheckPrefixesAlias(
42*09467b48Spatrick     "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
43*09467b48Spatrick     cl::NotHidden,
44*09467b48Spatrick     cl::desc(
45*09467b48Spatrick         "Alias for -check-prefix permitting multiple comma separated values"));
46*09467b48Spatrick 
47*09467b48Spatrick static cl::opt<bool> NoCanonicalizeWhiteSpace(
48*09467b48Spatrick     "strict-whitespace",
49*09467b48Spatrick     cl::desc("Do not treat all horizontal whitespace as equivalent"));
50*09467b48Spatrick 
51*09467b48Spatrick static cl::opt<bool> IgnoreCase(
52*09467b48Spatrick     "ignore-case",
53*09467b48Spatrick     cl::desc("Use case-insensitive matching"));
54*09467b48Spatrick 
55*09467b48Spatrick static cl::list<std::string> ImplicitCheckNot(
56*09467b48Spatrick     "implicit-check-not",
57*09467b48Spatrick     cl::desc("Add an implicit negative check with this pattern to every\n"
58*09467b48Spatrick              "positive check. This can be used to ensure that no instances of\n"
59*09467b48Spatrick              "this pattern occur which are not matched by a positive pattern"),
60*09467b48Spatrick     cl::value_desc("pattern"));
61*09467b48Spatrick 
62*09467b48Spatrick static cl::list<std::string>
63*09467b48Spatrick     GlobalDefines("D", cl::AlwaysPrefix,
64*09467b48Spatrick                   cl::desc("Define a variable to be used in capture patterns."),
65*09467b48Spatrick                   cl::value_desc("VAR=VALUE"));
66*09467b48Spatrick 
67*09467b48Spatrick static cl::opt<bool> AllowEmptyInput(
68*09467b48Spatrick     "allow-empty", cl::init(false),
69*09467b48Spatrick     cl::desc("Allow the input file to be empty. This is useful when making\n"
70*09467b48Spatrick              "checks that some error message does not occur, for example."));
71*09467b48Spatrick 
72*09467b48Spatrick static cl::opt<bool> MatchFullLines(
73*09467b48Spatrick     "match-full-lines", cl::init(false),
74*09467b48Spatrick     cl::desc("Require all positive matches to cover an entire input line.\n"
75*09467b48Spatrick              "Allows leading and trailing whitespace if --strict-whitespace\n"
76*09467b48Spatrick              "is not also passed."));
77*09467b48Spatrick 
78*09467b48Spatrick static cl::opt<bool> EnableVarScope(
79*09467b48Spatrick     "enable-var-scope", cl::init(false),
80*09467b48Spatrick     cl::desc("Enables scope for regex variables. Variables with names that\n"
81*09467b48Spatrick              "do not start with '$' will be reset at the beginning of\n"
82*09467b48Spatrick              "each CHECK-LABEL block."));
83*09467b48Spatrick 
84*09467b48Spatrick static cl::opt<bool> AllowDeprecatedDagOverlap(
85*09467b48Spatrick     "allow-deprecated-dag-overlap", cl::init(false),
86*09467b48Spatrick     cl::desc("Enable overlapping among matches in a group of consecutive\n"
87*09467b48Spatrick              "CHECK-DAG directives.  This option is deprecated and is only\n"
88*09467b48Spatrick              "provided for convenience as old tests are migrated to the new\n"
89*09467b48Spatrick              "non-overlapping CHECK-DAG implementation.\n"));
90*09467b48Spatrick 
91*09467b48Spatrick static cl::opt<bool> Verbose(
92*09467b48Spatrick     "v", cl::init(false),
93*09467b48Spatrick     cl::desc("Print directive pattern matches, or add them to the input dump\n"
94*09467b48Spatrick              "if enabled.\n"));
95*09467b48Spatrick 
96*09467b48Spatrick static cl::opt<bool> VerboseVerbose(
97*09467b48Spatrick     "vv", cl::init(false),
98*09467b48Spatrick     cl::desc("Print information helpful in diagnosing internal FileCheck\n"
99*09467b48Spatrick              "issues, or add it to the input dump if enabled.  Implies\n"
100*09467b48Spatrick              "-v.\n"));
101*09467b48Spatrick static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE";
102*09467b48Spatrick 
103*09467b48Spatrick static cl::opt<bool> DumpInputOnFailure(
104*09467b48Spatrick     "dump-input-on-failure",
105*09467b48Spatrick     cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)),
106*09467b48Spatrick     cl::desc("Dump original input to stderr before failing.\n"
107*09467b48Spatrick              "The value can be also controlled using\n"
108*09467b48Spatrick              "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n"
109*09467b48Spatrick              "This option is deprecated in favor of -dump-input=fail.\n"));
110*09467b48Spatrick 
111*09467b48Spatrick // The order of DumpInputValue members affects their precedence, as documented
112*09467b48Spatrick // for -dump-input below.
113*09467b48Spatrick enum DumpInputValue {
114*09467b48Spatrick   DumpInputDefault,
115*09467b48Spatrick   DumpInputNever,
116*09467b48Spatrick   DumpInputFail,
117*09467b48Spatrick   DumpInputAlways,
118*09467b48Spatrick   DumpInputHelp
119*09467b48Spatrick };
120*09467b48Spatrick 
121*09467b48Spatrick static cl::list<DumpInputValue> DumpInputs(
122*09467b48Spatrick     "dump-input",
123*09467b48Spatrick     cl::desc("Dump input to stderr, adding annotations representing\n"
124*09467b48Spatrick              "currently enabled diagnostics.  When there are multiple\n"
125*09467b48Spatrick              "occurrences of this option, the <value> that appears earliest\n"
126*09467b48Spatrick              "in the list below has precedence.\n"),
127*09467b48Spatrick     cl::value_desc("mode"),
128*09467b48Spatrick     cl::values(clEnumValN(DumpInputHelp, "help",
129*09467b48Spatrick                           "Explain dump format and quit"),
130*09467b48Spatrick                clEnumValN(DumpInputAlways, "always", "Always dump input"),
131*09467b48Spatrick                clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
132*09467b48Spatrick                clEnumValN(DumpInputNever, "never", "Never dump input")));
133*09467b48Spatrick 
134*09467b48Spatrick typedef cl::list<std::string>::const_iterator prefix_iterator;
135*09467b48Spatrick 
136*09467b48Spatrick 
137*09467b48Spatrick 
138*09467b48Spatrick 
139*09467b48Spatrick 
140*09467b48Spatrick 
141*09467b48Spatrick 
142*09467b48Spatrick static void DumpCommandLine(int argc, char **argv) {
143*09467b48Spatrick   errs() << "FileCheck command line: ";
144*09467b48Spatrick   for (int I = 0; I < argc; I++)
145*09467b48Spatrick     errs() << " " << argv[I];
146*09467b48Spatrick   errs() << "\n";
147*09467b48Spatrick }
148*09467b48Spatrick 
149*09467b48Spatrick struct MarkerStyle {
150*09467b48Spatrick   /// The starting char (before tildes) for marking the line.
151*09467b48Spatrick   char Lead;
152*09467b48Spatrick   /// What color to use for this annotation.
153*09467b48Spatrick   raw_ostream::Colors Color;
154*09467b48Spatrick   /// A note to follow the marker, or empty string if none.
155*09467b48Spatrick   std::string Note;
156*09467b48Spatrick   MarkerStyle() {}
157*09467b48Spatrick   MarkerStyle(char Lead, raw_ostream::Colors Color,
158*09467b48Spatrick               const std::string &Note = "")
159*09467b48Spatrick       : Lead(Lead), Color(Color), Note(Note) {}
160*09467b48Spatrick };
161*09467b48Spatrick 
162*09467b48Spatrick static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
163*09467b48Spatrick   switch (MatchTy) {
164*09467b48Spatrick   case FileCheckDiag::MatchFoundAndExpected:
165*09467b48Spatrick     return MarkerStyle('^', raw_ostream::GREEN);
166*09467b48Spatrick   case FileCheckDiag::MatchFoundButExcluded:
167*09467b48Spatrick     return MarkerStyle('!', raw_ostream::RED, "error: no match expected");
168*09467b48Spatrick   case FileCheckDiag::MatchFoundButWrongLine:
169*09467b48Spatrick     return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line");
170*09467b48Spatrick   case FileCheckDiag::MatchFoundButDiscarded:
171*09467b48Spatrick     return MarkerStyle('!', raw_ostream::CYAN,
172*09467b48Spatrick                        "discard: overlaps earlier match");
173*09467b48Spatrick   case FileCheckDiag::MatchNoneAndExcluded:
174*09467b48Spatrick     return MarkerStyle('X', raw_ostream::GREEN);
175*09467b48Spatrick   case FileCheckDiag::MatchNoneButExpected:
176*09467b48Spatrick     return MarkerStyle('X', raw_ostream::RED, "error: no match found");
177*09467b48Spatrick   case FileCheckDiag::MatchFuzzy:
178*09467b48Spatrick     return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match");
179*09467b48Spatrick   }
180*09467b48Spatrick   llvm_unreachable_internal("unexpected match type");
181*09467b48Spatrick }
182*09467b48Spatrick 
183*09467b48Spatrick static void DumpInputAnnotationHelp(raw_ostream &OS) {
184*09467b48Spatrick   OS << "The following description was requested by -dump-input=help to\n"
185*09467b48Spatrick      << "explain the input annotations printed by -dump-input=always and\n"
186*09467b48Spatrick      << "-dump-input=fail:\n\n";
187*09467b48Spatrick 
188*09467b48Spatrick   // Labels for input lines.
189*09467b48Spatrick   OS << "  - ";
190*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
191*09467b48Spatrick   OS << "     labels line number L of the input file\n";
192*09467b48Spatrick 
193*09467b48Spatrick   // Labels for annotation lines.
194*09467b48Spatrick   OS << "  - ";
195*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
196*09467b48Spatrick   OS << "    labels the only match result for a pattern of type T from "
197*09467b48Spatrick      << "line L of\n"
198*09467b48Spatrick      << "           the check file\n";
199*09467b48Spatrick   OS << "  - ";
200*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
201*09467b48Spatrick   OS << "  labels the Nth match result for a pattern of type T from line "
202*09467b48Spatrick      << "L of\n"
203*09467b48Spatrick      << "           the check file\n";
204*09467b48Spatrick 
205*09467b48Spatrick   // Markers on annotation lines.
206*09467b48Spatrick   OS << "  - ";
207*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
208*09467b48Spatrick   OS << "    marks good match (reported if -v)\n"
209*09467b48Spatrick      << "  - ";
210*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
211*09467b48Spatrick   OS << "    marks bad match, such as:\n"
212*09467b48Spatrick      << "           - CHECK-NEXT on same line as previous match (error)\n"
213*09467b48Spatrick      << "           - CHECK-NOT found (error)\n"
214*09467b48Spatrick      << "           - CHECK-DAG overlapping match (discarded, reported if "
215*09467b48Spatrick      << "-vv)\n"
216*09467b48Spatrick      << "  - ";
217*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
218*09467b48Spatrick   OS << "    marks search range when no match is found, such as:\n"
219*09467b48Spatrick      << "           - CHECK-NEXT not found (error)\n"
220*09467b48Spatrick      << "           - CHECK-NOT not found (success, reported if -vv)\n"
221*09467b48Spatrick      << "           - CHECK-DAG not found after discarded matches (error)\n"
222*09467b48Spatrick      << "  - ";
223*09467b48Spatrick   WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
224*09467b48Spatrick   OS << "      marks fuzzy match when no match is found\n";
225*09467b48Spatrick 
226*09467b48Spatrick   // Colors.
227*09467b48Spatrick   OS << "  - colors ";
228*09467b48Spatrick   WithColor(OS, raw_ostream::GREEN, true) << "success";
229*09467b48Spatrick   OS << ", ";
230*09467b48Spatrick   WithColor(OS, raw_ostream::RED, true) << "error";
231*09467b48Spatrick   OS << ", ";
232*09467b48Spatrick   WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
233*09467b48Spatrick   OS << ", ";
234*09467b48Spatrick   WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
235*09467b48Spatrick   OS << ", ";
236*09467b48Spatrick   WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
237*09467b48Spatrick   OS << "\n\n"
238*09467b48Spatrick      << "If you are not seeing color above or in input dumps, try: -color\n";
239*09467b48Spatrick }
240*09467b48Spatrick 
241*09467b48Spatrick /// An annotation for a single input line.
242*09467b48Spatrick struct InputAnnotation {
243*09467b48Spatrick   /// The check file line (one-origin indexing) where the directive that
244*09467b48Spatrick   /// produced this annotation is located.
245*09467b48Spatrick   unsigned CheckLine;
246*09467b48Spatrick   /// The index of the match result for this check.
247*09467b48Spatrick   unsigned CheckDiagIndex;
248*09467b48Spatrick   /// The label for this annotation.
249*09467b48Spatrick   std::string Label;
250*09467b48Spatrick   /// What input line (one-origin indexing) this annotation marks.  This might
251*09467b48Spatrick   /// be different from the starting line of the original diagnostic if this is
252*09467b48Spatrick   /// a non-initial fragment of a diagnostic that has been broken across
253*09467b48Spatrick   /// multiple lines.
254*09467b48Spatrick   unsigned InputLine;
255*09467b48Spatrick   /// The column range (one-origin indexing, open end) in which to to mark the
256*09467b48Spatrick   /// input line.  If InputEndCol is UINT_MAX, treat it as the last column
257*09467b48Spatrick   /// before the newline.
258*09467b48Spatrick   unsigned InputStartCol, InputEndCol;
259*09467b48Spatrick   /// The marker to use.
260*09467b48Spatrick   MarkerStyle Marker;
261*09467b48Spatrick   /// Whether this annotation represents a good match for an expected pattern.
262*09467b48Spatrick   bool FoundAndExpectedMatch;
263*09467b48Spatrick };
264*09467b48Spatrick 
265*09467b48Spatrick /// Get an abbreviation for the check type.
266*09467b48Spatrick std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
267*09467b48Spatrick   switch (Ty) {
268*09467b48Spatrick   case Check::CheckPlain:
269*09467b48Spatrick     if (Ty.getCount() > 1)
270*09467b48Spatrick       return "count";
271*09467b48Spatrick     return "check";
272*09467b48Spatrick   case Check::CheckNext:
273*09467b48Spatrick     return "next";
274*09467b48Spatrick   case Check::CheckSame:
275*09467b48Spatrick     return "same";
276*09467b48Spatrick   case Check::CheckNot:
277*09467b48Spatrick     return "not";
278*09467b48Spatrick   case Check::CheckDAG:
279*09467b48Spatrick     return "dag";
280*09467b48Spatrick   case Check::CheckLabel:
281*09467b48Spatrick     return "label";
282*09467b48Spatrick   case Check::CheckEmpty:
283*09467b48Spatrick     return "empty";
284*09467b48Spatrick   case Check::CheckEOF:
285*09467b48Spatrick     return "eof";
286*09467b48Spatrick   case Check::CheckBadNot:
287*09467b48Spatrick     return "bad-not";
288*09467b48Spatrick   case Check::CheckBadCount:
289*09467b48Spatrick     return "bad-count";
290*09467b48Spatrick   case Check::CheckNone:
291*09467b48Spatrick     llvm_unreachable("invalid FileCheckType");
292*09467b48Spatrick   }
293*09467b48Spatrick   llvm_unreachable("unknown FileCheckType");
294*09467b48Spatrick }
295*09467b48Spatrick 
296*09467b48Spatrick static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,
297*09467b48Spatrick                                   std::vector<InputAnnotation> &Annotations,
298*09467b48Spatrick                                   unsigned &LabelWidth) {
299*09467b48Spatrick   // How many diagnostics has the current check seen so far?
300*09467b48Spatrick   unsigned CheckDiagCount = 0;
301*09467b48Spatrick   // What's the widest label?
302*09467b48Spatrick   LabelWidth = 0;
303*09467b48Spatrick   for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
304*09467b48Spatrick        ++DiagItr) {
305*09467b48Spatrick     InputAnnotation A;
306*09467b48Spatrick 
307*09467b48Spatrick     // Build label, which uniquely identifies this check result.
308*09467b48Spatrick     A.CheckLine = DiagItr->CheckLine;
309*09467b48Spatrick     llvm::raw_string_ostream Label(A.Label);
310*09467b48Spatrick     Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"
311*09467b48Spatrick           << DiagItr->CheckLine;
312*09467b48Spatrick     A.CheckDiagIndex = UINT_MAX;
313*09467b48Spatrick     auto DiagNext = std::next(DiagItr);
314*09467b48Spatrick     if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
315*09467b48Spatrick         DiagItr->CheckLine == DiagNext->CheckLine)
316*09467b48Spatrick       A.CheckDiagIndex = CheckDiagCount++;
317*09467b48Spatrick     else if (CheckDiagCount) {
318*09467b48Spatrick       A.CheckDiagIndex = CheckDiagCount;
319*09467b48Spatrick       CheckDiagCount = 0;
320*09467b48Spatrick     }
321*09467b48Spatrick     if (A.CheckDiagIndex != UINT_MAX)
322*09467b48Spatrick       Label << "'" << A.CheckDiagIndex;
323*09467b48Spatrick     else
324*09467b48Spatrick       A.CheckDiagIndex = 0;
325*09467b48Spatrick     Label.flush();
326*09467b48Spatrick     LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
327*09467b48Spatrick 
328*09467b48Spatrick     A.Marker = GetMarker(DiagItr->MatchTy);
329*09467b48Spatrick     A.FoundAndExpectedMatch =
330*09467b48Spatrick         DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
331*09467b48Spatrick 
332*09467b48Spatrick     // Compute the mark location, and break annotation into multiple
333*09467b48Spatrick     // annotations if it spans multiple lines.
334*09467b48Spatrick     A.InputLine = DiagItr->InputStartLine;
335*09467b48Spatrick     A.InputStartCol = DiagItr->InputStartCol;
336*09467b48Spatrick     if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
337*09467b48Spatrick       // Sometimes ranges are empty in order to indicate a specific point, but
338*09467b48Spatrick       // that would mean nothing would be marked, so adjust the range to
339*09467b48Spatrick       // include the following character.
340*09467b48Spatrick       A.InputEndCol =
341*09467b48Spatrick           std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
342*09467b48Spatrick       Annotations.push_back(A);
343*09467b48Spatrick     } else {
344*09467b48Spatrick       assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
345*09467b48Spatrick              "expected input range not to be inverted");
346*09467b48Spatrick       A.InputEndCol = UINT_MAX;
347*09467b48Spatrick       Annotations.push_back(A);
348*09467b48Spatrick       for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
349*09467b48Spatrick            L <= E; ++L) {
350*09467b48Spatrick         // If a range ends before the first column on a line, then it has no
351*09467b48Spatrick         // characters on that line, so there's nothing to render.
352*09467b48Spatrick         if (DiagItr->InputEndCol == 1 && L == E)
353*09467b48Spatrick           break;
354*09467b48Spatrick         InputAnnotation B;
355*09467b48Spatrick         B.CheckLine = A.CheckLine;
356*09467b48Spatrick         B.CheckDiagIndex = A.CheckDiagIndex;
357*09467b48Spatrick         B.Label = A.Label;
358*09467b48Spatrick         B.InputLine = L;
359*09467b48Spatrick         B.Marker = A.Marker;
360*09467b48Spatrick         B.Marker.Lead = '~';
361*09467b48Spatrick         B.Marker.Note = "";
362*09467b48Spatrick         B.InputStartCol = 1;
363*09467b48Spatrick         if (L != E)
364*09467b48Spatrick           B.InputEndCol = UINT_MAX;
365*09467b48Spatrick         else
366*09467b48Spatrick           B.InputEndCol = DiagItr->InputEndCol;
367*09467b48Spatrick         B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
368*09467b48Spatrick         Annotations.push_back(B);
369*09467b48Spatrick       }
370*09467b48Spatrick     }
371*09467b48Spatrick   }
372*09467b48Spatrick }
373*09467b48Spatrick 
374*09467b48Spatrick static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
375*09467b48Spatrick                                StringRef InputFileText,
376*09467b48Spatrick                                std::vector<InputAnnotation> &Annotations,
377*09467b48Spatrick                                unsigned LabelWidth) {
378*09467b48Spatrick   OS << "Full input was:\n<<<<<<\n";
379*09467b48Spatrick 
380*09467b48Spatrick   // Sort annotations.
381*09467b48Spatrick   //
382*09467b48Spatrick   // First, sort in the order of input lines to make it easier to find relevant
383*09467b48Spatrick   // annotations while iterating input lines in the implementation below.
384*09467b48Spatrick   // FileCheck diagnostics are not always reported and recorded in the order of
385*09467b48Spatrick   // input lines due to, for example, CHECK-DAG and CHECK-NOT.
386*09467b48Spatrick   //
387*09467b48Spatrick   // Second, for annotations for the same input line, sort in the order of the
388*09467b48Spatrick   // FileCheck directive's line in the check file (where there's at most one
389*09467b48Spatrick   // directive per line) and then by the index of the match result for that
390*09467b48Spatrick   // directive.  The rationale of this choice is that, for any input line, this
391*09467b48Spatrick   // sort establishes a total order of annotations that, with respect to match
392*09467b48Spatrick   // results, is consistent across multiple lines, thus making match results
393*09467b48Spatrick   // easier to track from one line to the next when they span multiple lines.
394*09467b48Spatrick   std::sort(Annotations.begin(), Annotations.end(),
395*09467b48Spatrick             [](const InputAnnotation &A, const InputAnnotation &B) {
396*09467b48Spatrick               if (A.InputLine != B.InputLine)
397*09467b48Spatrick                 return A.InputLine < B.InputLine;
398*09467b48Spatrick               if (A.CheckLine != B.CheckLine)
399*09467b48Spatrick                 return A.CheckLine < B.CheckLine;
400*09467b48Spatrick               // FIXME: Sometimes CHECK-LABEL reports its match twice with
401*09467b48Spatrick               // other diagnostics in between, and then diag index incrementing
402*09467b48Spatrick               // fails to work properly, and then this assert fails.  We should
403*09467b48Spatrick               // suppress one of those diagnostics or do a better job of
404*09467b48Spatrick               // computing this index.  For now, we just produce a redundant
405*09467b48Spatrick               // CHECK-LABEL annotation.
406*09467b48Spatrick               // assert(A.CheckDiagIndex != B.CheckDiagIndex &&
407*09467b48Spatrick               //        "expected diagnostic indices to be unique within a "
408*09467b48Spatrick               //        " check line");
409*09467b48Spatrick               return A.CheckDiagIndex < B.CheckDiagIndex;
410*09467b48Spatrick             });
411*09467b48Spatrick 
412*09467b48Spatrick   // Compute the width of the label column.
413*09467b48Spatrick   const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
414*09467b48Spatrick                       *InputFileEnd = InputFileText.bytes_end();
415*09467b48Spatrick   unsigned LineCount = InputFileText.count('\n');
416*09467b48Spatrick   if (InputFileEnd[-1] != '\n')
417*09467b48Spatrick     ++LineCount;
418*09467b48Spatrick   unsigned LineNoWidth = std::log10(LineCount) + 1;
419*09467b48Spatrick   // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
420*09467b48Spatrick   // on input lines and (2) to the right of the (left-aligned) labels on
421*09467b48Spatrick   // annotation lines so that input lines and annotation lines are more
422*09467b48Spatrick   // visually distinct.  For example, the spaces on the annotation lines ensure
423*09467b48Spatrick   // that input line numbers and check directive line numbers never align
424*09467b48Spatrick   // horizontally.  Those line numbers might not even be for the same file.
425*09467b48Spatrick   // One space would be enough to achieve that, but more makes it even easier
426*09467b48Spatrick   // to see.
427*09467b48Spatrick   LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
428*09467b48Spatrick 
429*09467b48Spatrick   // Print annotated input lines.
430*09467b48Spatrick   auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
431*09467b48Spatrick   for (unsigned Line = 1;
432*09467b48Spatrick        InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
433*09467b48Spatrick        ++Line) {
434*09467b48Spatrick     const unsigned char *InputFileLine = InputFilePtr;
435*09467b48Spatrick 
436*09467b48Spatrick     // Print right-aligned line number.
437*09467b48Spatrick     WithColor(OS, raw_ostream::BLACK, true)
438*09467b48Spatrick         << format_decimal(Line, LabelWidth) << ": ";
439*09467b48Spatrick 
440*09467b48Spatrick     // For the case where -v and colors are enabled, find the annotations for
441*09467b48Spatrick     // good matches for expected patterns in order to highlight everything
442*09467b48Spatrick     // else in the line.  There are no such annotations if -v is disabled.
443*09467b48Spatrick     std::vector<InputAnnotation> FoundAndExpectedMatches;
444*09467b48Spatrick     if (Req.Verbose && WithColor(OS).colorsEnabled()) {
445*09467b48Spatrick       for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
446*09467b48Spatrick            ++I) {
447*09467b48Spatrick         if (I->FoundAndExpectedMatch)
448*09467b48Spatrick           FoundAndExpectedMatches.push_back(*I);
449*09467b48Spatrick       }
450*09467b48Spatrick     }
451*09467b48Spatrick 
452*09467b48Spatrick     // Print numbered line with highlighting where there are no matches for
453*09467b48Spatrick     // expected patterns.
454*09467b48Spatrick     bool Newline = false;
455*09467b48Spatrick     {
456*09467b48Spatrick       WithColor COS(OS);
457*09467b48Spatrick       bool InMatch = false;
458*09467b48Spatrick       if (Req.Verbose)
459*09467b48Spatrick         COS.changeColor(raw_ostream::CYAN, true, true);
460*09467b48Spatrick       for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
461*09467b48Spatrick         bool WasInMatch = InMatch;
462*09467b48Spatrick         InMatch = false;
463*09467b48Spatrick         for (auto M : FoundAndExpectedMatches) {
464*09467b48Spatrick           if (M.InputStartCol <= Col && Col < M.InputEndCol) {
465*09467b48Spatrick             InMatch = true;
466*09467b48Spatrick             break;
467*09467b48Spatrick           }
468*09467b48Spatrick         }
469*09467b48Spatrick         if (!WasInMatch && InMatch)
470*09467b48Spatrick           COS.resetColor();
471*09467b48Spatrick         else if (WasInMatch && !InMatch)
472*09467b48Spatrick           COS.changeColor(raw_ostream::CYAN, true, true);
473*09467b48Spatrick         if (*InputFilePtr == '\n')
474*09467b48Spatrick           Newline = true;
475*09467b48Spatrick         else
476*09467b48Spatrick           COS << *InputFilePtr;
477*09467b48Spatrick         ++InputFilePtr;
478*09467b48Spatrick       }
479*09467b48Spatrick     }
480*09467b48Spatrick     OS << '\n';
481*09467b48Spatrick     unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline;
482*09467b48Spatrick 
483*09467b48Spatrick     // Print any annotations.
484*09467b48Spatrick     while (AnnotationItr != AnnotationEnd &&
485*09467b48Spatrick            AnnotationItr->InputLine == Line) {
486*09467b48Spatrick       WithColor COS(OS, AnnotationItr->Marker.Color, true);
487*09467b48Spatrick       // The two spaces below are where the ": " appears on input lines.
488*09467b48Spatrick       COS << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
489*09467b48Spatrick       unsigned Col;
490*09467b48Spatrick       for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
491*09467b48Spatrick         COS << ' ';
492*09467b48Spatrick       COS << AnnotationItr->Marker.Lead;
493*09467b48Spatrick       // If InputEndCol=UINT_MAX, stop at InputLineWidth.
494*09467b48Spatrick       for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
495*09467b48Spatrick            ++Col)
496*09467b48Spatrick         COS << '~';
497*09467b48Spatrick       const std::string &Note = AnnotationItr->Marker.Note;
498*09467b48Spatrick       if (!Note.empty()) {
499*09467b48Spatrick         // Put the note at the end of the input line.  If we were to instead
500*09467b48Spatrick         // put the note right after the marker, subsequent annotations for the
501*09467b48Spatrick         // same input line might appear to mark this note instead of the input
502*09467b48Spatrick         // line.
503*09467b48Spatrick         for (; Col <= InputLineWidth; ++Col)
504*09467b48Spatrick           COS << ' ';
505*09467b48Spatrick         COS << ' ' << Note;
506*09467b48Spatrick       }
507*09467b48Spatrick       COS << '\n';
508*09467b48Spatrick       ++AnnotationItr;
509*09467b48Spatrick     }
510*09467b48Spatrick   }
511*09467b48Spatrick 
512*09467b48Spatrick   OS << ">>>>>>\n";
513*09467b48Spatrick }
514*09467b48Spatrick 
515*09467b48Spatrick int main(int argc, char **argv) {
516*09467b48Spatrick   // Enable use of ANSI color codes because FileCheck is using them to
517*09467b48Spatrick   // highlight text.
518*09467b48Spatrick   llvm::sys::Process::UseANSIEscapeCodes(true);
519*09467b48Spatrick 
520*09467b48Spatrick   InitLLVM X(argc, argv);
521*09467b48Spatrick   cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
522*09467b48Spatrick                               "FILECHECK_OPTS");
523*09467b48Spatrick   DumpInputValue DumpInput =
524*09467b48Spatrick       DumpInputs.empty()
525*09467b48Spatrick           ? DumpInputDefault
526*09467b48Spatrick           : *std::max_element(DumpInputs.begin(), DumpInputs.end());
527*09467b48Spatrick   if (DumpInput == DumpInputHelp) {
528*09467b48Spatrick     DumpInputAnnotationHelp(outs());
529*09467b48Spatrick     return 0;
530*09467b48Spatrick   }
531*09467b48Spatrick   if (CheckFilename.empty()) {
532*09467b48Spatrick     errs() << "<check-file> not specified\n";
533*09467b48Spatrick     return 2;
534*09467b48Spatrick   }
535*09467b48Spatrick 
536*09467b48Spatrick   FileCheckRequest Req;
537*09467b48Spatrick   for (auto Prefix : CheckPrefixes)
538*09467b48Spatrick     Req.CheckPrefixes.push_back(Prefix);
539*09467b48Spatrick 
540*09467b48Spatrick   for (auto CheckNot : ImplicitCheckNot)
541*09467b48Spatrick     Req.ImplicitCheckNot.push_back(CheckNot);
542*09467b48Spatrick 
543*09467b48Spatrick   bool GlobalDefineError = false;
544*09467b48Spatrick   for (auto G : GlobalDefines) {
545*09467b48Spatrick     size_t EqIdx = G.find('=');
546*09467b48Spatrick     if (EqIdx == std::string::npos) {
547*09467b48Spatrick       errs() << "Missing equal sign in command-line definition '-D" << G
548*09467b48Spatrick              << "'\n";
549*09467b48Spatrick       GlobalDefineError = true;
550*09467b48Spatrick       continue;
551*09467b48Spatrick     }
552*09467b48Spatrick     if (EqIdx == 0) {
553*09467b48Spatrick       errs() << "Missing variable name in command-line definition '-D" << G
554*09467b48Spatrick              << "'\n";
555*09467b48Spatrick       GlobalDefineError = true;
556*09467b48Spatrick       continue;
557*09467b48Spatrick     }
558*09467b48Spatrick     Req.GlobalDefines.push_back(G);
559*09467b48Spatrick   }
560*09467b48Spatrick   if (GlobalDefineError)
561*09467b48Spatrick     return 2;
562*09467b48Spatrick 
563*09467b48Spatrick   Req.AllowEmptyInput = AllowEmptyInput;
564*09467b48Spatrick   Req.EnableVarScope = EnableVarScope;
565*09467b48Spatrick   Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
566*09467b48Spatrick   Req.Verbose = Verbose;
567*09467b48Spatrick   Req.VerboseVerbose = VerboseVerbose;
568*09467b48Spatrick   Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
569*09467b48Spatrick   Req.MatchFullLines = MatchFullLines;
570*09467b48Spatrick   Req.IgnoreCase = IgnoreCase;
571*09467b48Spatrick 
572*09467b48Spatrick   if (VerboseVerbose)
573*09467b48Spatrick     Req.Verbose = true;
574*09467b48Spatrick 
575*09467b48Spatrick   FileCheck FC(Req);
576*09467b48Spatrick   if (!FC.ValidateCheckPrefixes()) {
577*09467b48Spatrick     errs() << "Supplied check-prefix is invalid! Prefixes must be unique and "
578*09467b48Spatrick               "start with a letter and contain only alphanumeric characters, "
579*09467b48Spatrick               "hyphens and underscores\n";
580*09467b48Spatrick     return 2;
581*09467b48Spatrick   }
582*09467b48Spatrick 
583*09467b48Spatrick   Regex PrefixRE = FC.buildCheckPrefixRegex();
584*09467b48Spatrick   std::string REError;
585*09467b48Spatrick   if (!PrefixRE.isValid(REError)) {
586*09467b48Spatrick     errs() << "Unable to combine check-prefix strings into a prefix regular "
587*09467b48Spatrick               "expression! This is likely a bug in FileCheck's verification of "
588*09467b48Spatrick               "the check-prefix strings. Regular expression parsing failed "
589*09467b48Spatrick               "with the following error: "
590*09467b48Spatrick            << REError << "\n";
591*09467b48Spatrick     return 2;
592*09467b48Spatrick   }
593*09467b48Spatrick 
594*09467b48Spatrick   SourceMgr SM;
595*09467b48Spatrick 
596*09467b48Spatrick   // Read the expected strings from the check file.
597*09467b48Spatrick   ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
598*09467b48Spatrick       MemoryBuffer::getFileOrSTDIN(CheckFilename);
599*09467b48Spatrick   if (std::error_code EC = CheckFileOrErr.getError()) {
600*09467b48Spatrick     errs() << "Could not open check file '" << CheckFilename
601*09467b48Spatrick            << "': " << EC.message() << '\n';
602*09467b48Spatrick     return 2;
603*09467b48Spatrick   }
604*09467b48Spatrick   MemoryBuffer &CheckFile = *CheckFileOrErr.get();
605*09467b48Spatrick 
606*09467b48Spatrick   SmallString<4096> CheckFileBuffer;
607*09467b48Spatrick   StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
608*09467b48Spatrick 
609*09467b48Spatrick   SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
610*09467b48Spatrick                             CheckFileText, CheckFile.getBufferIdentifier()),
611*09467b48Spatrick                         SMLoc());
612*09467b48Spatrick 
613*09467b48Spatrick   if (FC.readCheckFile(SM, CheckFileText, PrefixRE))
614*09467b48Spatrick     return 2;
615*09467b48Spatrick 
616*09467b48Spatrick   // Open the file to check and add it to SourceMgr.
617*09467b48Spatrick   ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
618*09467b48Spatrick       MemoryBuffer::getFileOrSTDIN(InputFilename);
619*09467b48Spatrick   if (std::error_code EC = InputFileOrErr.getError()) {
620*09467b48Spatrick     errs() << "Could not open input file '" << InputFilename
621*09467b48Spatrick            << "': " << EC.message() << '\n';
622*09467b48Spatrick     return 2;
623*09467b48Spatrick   }
624*09467b48Spatrick   MemoryBuffer &InputFile = *InputFileOrErr.get();
625*09467b48Spatrick 
626*09467b48Spatrick   if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
627*09467b48Spatrick     errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
628*09467b48Spatrick     DumpCommandLine(argc, argv);
629*09467b48Spatrick     return 2;
630*09467b48Spatrick   }
631*09467b48Spatrick 
632*09467b48Spatrick   SmallString<4096> InputFileBuffer;
633*09467b48Spatrick   StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
634*09467b48Spatrick 
635*09467b48Spatrick   SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
636*09467b48Spatrick                             InputFileText, InputFile.getBufferIdentifier()),
637*09467b48Spatrick                         SMLoc());
638*09467b48Spatrick 
639*09467b48Spatrick   if (DumpInput == DumpInputDefault)
640*09467b48Spatrick     DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever;
641*09467b48Spatrick 
642*09467b48Spatrick   std::vector<FileCheckDiag> Diags;
643*09467b48Spatrick   int ExitCode = FC.checkInput(SM, InputFileText,
644*09467b48Spatrick                                DumpInput == DumpInputNever ? nullptr : &Diags)
645*09467b48Spatrick                      ? EXIT_SUCCESS
646*09467b48Spatrick                      : 1;
647*09467b48Spatrick   if (DumpInput == DumpInputAlways ||
648*09467b48Spatrick       (ExitCode == 1 && DumpInput == DumpInputFail)) {
649*09467b48Spatrick     errs() << "\n"
650*09467b48Spatrick            << "Input file: "
651*09467b48Spatrick            << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue())
652*09467b48Spatrick            << "\n"
653*09467b48Spatrick            << "Check file: " << CheckFilename << "\n"
654*09467b48Spatrick            << "\n"
655*09467b48Spatrick            << "-dump-input=help describes the format of the following dump.\n"
656*09467b48Spatrick            << "\n";
657*09467b48Spatrick     std::vector<InputAnnotation> Annotations;
658*09467b48Spatrick     unsigned LabelWidth;
659*09467b48Spatrick     BuildInputAnnotations(Diags, Annotations, LabelWidth);
660*09467b48Spatrick     DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth);
661*09467b48Spatrick   }
662*09467b48Spatrick 
663*09467b48Spatrick   return ExitCode;
664*09467b48Spatrick }
665