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