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>
27using namespace llvm;
28
29static cl::extrahelp FileCheckOptsEnv(
30 "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
31 "from the command line.\n");
32
33static cl::opt<std::string>
34 CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
35
36static cl::opt<std::string>
37 InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
38 cl::init(Val: "-"), cl::value_desc("filename"));
39
40static cl::list<std::string>
41 CheckPrefixes("check-prefixes", cl::CommaSeparated,
42 cl::desc("Comma separated list of prefixes to use from check "
43 "file\n(defaults to 'CHECK')"));
44static cl::alias CheckPrefixesAlias("check-prefix", cl::aliasopt(CheckPrefixes),
45 cl::CommaSeparated, cl::NotHidden,
46 cl::desc("Alias for -check-prefixes"));
47
48static cl::list<std::string> CommentPrefixes(
49 "comment-prefixes", cl::CommaSeparated, cl::Hidden,
50 cl::desc("Comma-separated list of comment prefixes to use from check file\n"
51 "(defaults to 'COM,RUN'). Please avoid using this feature in\n"
52 "LLVM's LIT-based test suites, which should be easier to\n"
53 "maintain if they all follow a consistent comment style. This\n"
54 "feature is meant for non-LIT test suites using FileCheck."));
55
56static cl::opt<bool> NoCanonicalizeWhiteSpace(
57 "strict-whitespace",
58 cl::desc("Do not treat all horizontal whitespace as equivalent"));
59
60static cl::opt<bool> IgnoreCase(
61 "ignore-case",
62 cl::desc("Use case-insensitive matching"));
63
64static cl::list<std::string> ImplicitCheckNot(
65 "implicit-check-not",
66 cl::desc("Add an implicit negative check with this pattern to every\n"
67 "positive check. This can be used to ensure that no instances of\n"
68 "this pattern occur which are not matched by a positive pattern"),
69 cl::value_desc("pattern"));
70
71static cl::list<std::string>
72 GlobalDefines("D", cl::AlwaysPrefix,
73 cl::desc("Define a variable to be used in capture patterns."),
74 cl::value_desc("VAR=VALUE"));
75
76static cl::opt<bool> AllowEmptyInput(
77 "allow-empty", cl::init(Val: false),
78 cl::desc("Allow the input file to be empty. This is useful when making\n"
79 "checks that some error message does not occur, for example."));
80
81static cl::opt<bool> AllowUnusedPrefixes(
82 "allow-unused-prefixes",
83 cl::desc("Allow prefixes to be specified but not appear in the test."));
84
85static cl::opt<bool> MatchFullLines(
86 "match-full-lines", cl::init(Val: false),
87 cl::desc("Require all positive matches to cover an entire input line.\n"
88 "Allows leading and trailing whitespace if --strict-whitespace\n"
89 "is not also passed."));
90
91static cl::opt<bool> EnableVarScope(
92 "enable-var-scope", cl::init(Val: false),
93 cl::desc("Enables scope for regex variables. Variables with names that\n"
94 "do not start with '$' will be reset at the beginning of\n"
95 "each CHECK-LABEL block."));
96
97static cl::opt<bool> AllowDeprecatedDagOverlap(
98 "allow-deprecated-dag-overlap", cl::init(Val: false),
99 cl::desc("Enable overlapping among matches in a group of consecutive\n"
100 "CHECK-DAG directives. This option is deprecated and is only\n"
101 "provided for convenience as old tests are migrated to the new\n"
102 "non-overlapping CHECK-DAG implementation.\n"));
103
104static cl::opt<bool> Verbose(
105 "v",
106 cl::desc("Print directive pattern matches, or add them to the input dump\n"
107 "if enabled.\n"));
108
109static cl::opt<bool> VerboseVerbose(
110 "vv",
111 cl::desc("Print information helpful in diagnosing internal FileCheck\n"
112 "issues, or add it to the input dump if enabled. Implies\n"
113 "-v.\n"));
114
115// The order of DumpInputValue members affects their precedence, as documented
116// for -dump-input below.
117enum DumpInputValue {
118 DumpInputNever,
119 DumpInputFail,
120 DumpInputAlways,
121 DumpInputHelp
122};
123
124static cl::list<DumpInputValue> DumpInputs(
125 "dump-input",
126 cl::desc("Dump input to stderr, adding annotations representing\n"
127 "currently enabled diagnostics. When there are multiple\n"
128 "occurrences of this option, the <value> that appears earliest\n"
129 "in the list below has precedence. The default is 'fail'.\n"),
130 cl::value_desc("mode"),
131 cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"),
132 clEnumValN(DumpInputAlways, "always", "Always dump input"),
133 clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
134 clEnumValN(DumpInputNever, "never", "Never dump input")));
135
136// The order of DumpInputFilterValue members affects their precedence, as
137// documented for -dump-input-filter below.
138enum DumpInputFilterValue {
139 DumpInputFilterError,
140 DumpInputFilterAnnotation,
141 DumpInputFilterAnnotationFull,
142 DumpInputFilterAll
143};
144
145static cl::list<DumpInputFilterValue> DumpInputFilters(
146 "dump-input-filter",
147 cl::desc("In the dump requested by -dump-input, print only input lines of\n"
148 "kind <value> plus any context specified by -dump-input-context.\n"
149 "When there are multiple occurrences of this option, the <value>\n"
150 "that appears earliest in the list below has precedence. The\n"
151 "default is 'error' when -dump-input=fail, and it's 'all' when\n"
152 "-dump-input=always.\n"),
153 cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"),
154 clEnumValN(DumpInputFilterAnnotationFull, "annotation-full",
155 "Input lines with annotations"),
156 clEnumValN(DumpInputFilterAnnotation, "annotation",
157 "Input lines with starting points of annotations"),
158 clEnumValN(DumpInputFilterError, "error",
159 "Input lines with starting points of error "
160 "annotations")));
161
162static cl::list<unsigned> DumpInputContexts(
163 "dump-input-context", cl::value_desc("N"),
164 cl::desc("In the dump requested by -dump-input, print <N> input lines\n"
165 "before and <N> input lines after any lines specified by\n"
166 "-dump-input-filter. When there are multiple occurrences of\n"
167 "this option, the largest specified <N> has precedence. The\n"
168 "default is 5.\n"));
169
170static cl::opt<unsigned> DumpInputLabelWidth(
171 "dump-input-label-width", cl::value_desc("N"), cl::init(Val: 0), cl::Hidden,
172 cl::desc("In the dump requested by -dump-input, set <N> as the minimum\n"
173 "width for the initial label column. When there are multiple\n"
174 "occurrences of this option, the last specified has precedence.\n"
175 "The default is 0, meaning that the actual labels fully\n"
176 "determine the width. FileCheck's own test suite uses this\n"
177 "option to avoid a fluctuating column width when checking input\n"
178 "dumps. This option is not expected to be useful elsewhere.\n"));
179
180typedef cl::list<std::string>::const_iterator prefix_iterator;
181
182
183
184
185
186
187
188static void DumpCommandLine(int argc, char **argv) {
189 errs() << "FileCheck command line: ";
190 for (int I = 0; I < argc; I++)
191 errs() << " " << argv[I];
192 errs() << "\n";
193}
194
195struct MarkerStyle {
196 /// The first char for marking the input line.
197 char Head;
198 /// Every character for marking the input line between \c Head and \c Tail.
199 /// Normally it is a tilde.
200 char Mid;
201 /// The final char for marking the input line. Normally it is a tilde.
202 char Tail;
203 /// What color to use for this annotation.
204 raw_ostream::Colors Color;
205 /// A note to follow the marker, or empty string if none.
206 std::string Note;
207 /// Does this marker indicate inclusion by -dump-input-filter=error?
208 bool FiltersAsError;
209};
210
211static MarkerStyle getMarker(const FileCheckDiag &Diag) {
212 // By default, the marker is based on whether the diagnostic is an error or is
213 // a MatchNoteDiag on a MatchResultDiag that is an error.
214 //
215 // It's less confusing if diagnostics that don't actually have match ranges
216 // don't have markers. For example, a marker for the MatchNoteDiag
217 // 'with "VAR" equal to "5"' would seem to indicate where "VAR" matches, but
218 // we don't actually have that location. Instead, we just place the note
219 // after the start of the associated MatchResultDiag. Search ranges are
220 // indicated separately.
221 MarkerStyle Res;
222 bool IsError = Diag.isError() || Diag.getMatchResultDiag().isError();
223 if (Diag.getMatchRange()) {
224 Res.Head = IsError ? '!' : '^';
225 Res.Mid = Res.Tail = '~';
226 } else {
227 Res.Head = Res.Mid = Res.Tail = ' ';
228 }
229 Res.Color = IsError ? raw_ostream::RED : raw_ostream::GREEN;
230 Res.FiltersAsError = IsError;
231
232 // Add Note. Override the default Head and Color for some diagnostic kinds.
233 switch (Diag.getKind()) {
234 case FileCheckDiag::MatchFoundDiag:
235 switch (cast<MatchFoundDiag>(Val: Diag).getStatus()) {
236 case MatchFoundDiag::Success:
237 break;
238 case MatchFoundDiag::Excluded:
239 Res.Note = "no match expected";
240 break;
241 case MatchFoundDiag::WrongLine:
242 Res.Note = "match on wrong line";
243 break;
244 case MatchFoundDiag::Discarded:
245 Res.Head = '!'; // Not an error, but not a successful match either.
246 Res.Color = raw_ostream::CYAN;
247 Res.Note = "discard: overlaps earlier match";
248 break;
249 }
250 break;
251 case FileCheckDiag::MatchNoneDiag:
252 switch (cast<MatchNoneDiag>(Val: Diag).getStatus()) {
253 case MatchNoneDiag::Success:
254 break;
255 case MatchNoneDiag::InvalidPattern:
256 Res.Note = "match failed for invalid pattern";
257 break;
258 case MatchNoneDiag::Expected:
259 Res.Note = "no match found in search range";
260 break;
261 }
262 break;
263 case FileCheckDiag::MatchFuzzyDiag:
264 Res.Head = '?';
265 Res.Color = raw_ostream::MAGENTA;
266 Res.Note = "possible intended match";
267 break;
268 case FileCheckDiag::MatchCustomNoteDiag:
269 Res.Note = cast<MatchCustomNoteDiag>(Val: Diag).getNote();
270 break;
271 }
272 if (Diag.isError()) {
273 assert(!Res.Note.empty() && "expected error diagnostic to have note");
274 Res.Note = "error: " + Res.Note;
275 }
276 return Res;
277}
278
279static void DumpInputAnnotationHelp(raw_ostream &OS) {
280 OS << "The following description was requested by -dump-input=help to\n"
281 << "explain the input dump printed by FileCheck.\n"
282 << "\n"
283 << "Related command-line options:\n"
284 << "\n"
285 << " - -dump-input=<value> enables or disables the input dump\n"
286 << " - -dump-input-filter=<value> filters the input lines\n"
287 << " - -dump-input-context=<N> adjusts the context of filtered lines\n"
288 << " - -v and -vv add more annotations\n"
289 << " - -color forces colors to be enabled both in the dump and below\n"
290 << " - -help documents the above options in more detail\n"
291 << "\n"
292 << "These options can also be set via FILECHECK_OPTS. For example, for\n"
293 << "maximum debugging output on failures:\n"
294 << "\n"
295 << " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
296 << "\n"
297 << "Input dump annotation format:\n"
298 << "\n";
299
300 // Labels for input lines.
301 OS << " - ";
302 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
303 OS << " labels line number L of the input file\n"
304 << " An extra space is added after each input line to represent"
305 << " the\n"
306 << " newline character\n";
307
308 // Labels for annotation lines.
309 OS << " - ";
310 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
311 OS << " labels the only match result for either (1) a pattern of type T"
312 << " from\n"
313 << " line L of the check file if L is an integer or (2) the"
314 << " I-th implicit\n"
315 << " pattern if L is \"imp\" followed by an integer "
316 << "I (index origin one)\n";
317 OS << " - ";
318 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
319 OS << " labels the Nth match result for such a pattern\n";
320
321 // Markers on annotation lines.
322 OS << " - ";
323 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
324 OS << " marks good match (reported if -v)\n"
325 << " - ";
326 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
327 OS << " marks bad match, such as:\n"
328 << " - CHECK-NEXT on same line as previous match (error)\n"
329 << " - CHECK-NOT found (error)\n"
330 << " - CHECK-DAG overlapping match (discarded, reported if "
331 << "-vv)\n"
332 << " - ";
333 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "{ }";
334 OS << " encloses search range (exclusive bounds) when no match is found "
335 << "or\n"
336 << " there is an error, such as:\n"
337 << " - the errors mentioned above\n"
338 << " - CHECK-NEXT not found (error)\n"
339 << " - CHECK-NOT not found (success, reported if -vv)\n"
340 << " - CHECK-DAG not found after discarded matches (error)\n"
341 << " - ";
342 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
343 OS << " marks fuzzy match when no match is found\n";
344
345 // Elided lines.
346 OS << " - ";
347 WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
348 OS << " indicates elided input lines and annotations, as specified by\n"
349 << " -dump-input-filter and -dump-input-context\n";
350
351 // Colors.
352 OS << " - colors ";
353 WithColor(OS, raw_ostream::GREEN, true) << "success";
354 OS << ", ";
355 WithColor(OS, raw_ostream::RED, true) << "error";
356 OS << ", ";
357 WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
358 OS << ", ";
359 WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
360 OS << ", ";
361 WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
362 OS << "\n";
363}
364
365/// An annotation for a single input line.
366struct InputAnnotation {
367 /// A globally unique index for this annotation before it was broken into
368 /// multiple lines.
369 unsigned LabelIndexGlobal;
370 /// The globally unique label for this annotation before it was broken into
371 /// multiple lines. There is one \c Label per \c LabelIndexGlobal and
372 /// vice-versa.
373 std::string Label;
374 /// Is this the initial (possibly only) fragment of an annotation, which has
375 /// been broken across multiple lines if necessary?
376 bool IsFirstLine;
377 /// What input line (one-origin indexing) this annotation marks. This might
378 /// be different from the starting line of the original diagnostic if
379 /// !IsFirstLine.
380 unsigned InputLine;
381 /// The column range (inclusive boundaries) in which to mark the input line.
382 /// A value of one indicates the first column of the actual input, and a
383 /// value of zero indicates the left margin. If \c InputLastCol is
384 /// \c UINT_MAX, the rest of the input line should be marked, and another
385 /// \c InputAnnotation will continue it on the next line.
386 unsigned InputFirstCol, InputLastCol;
387 /// The marker to use.
388 MarkerStyle Marker;
389 /// Whether this annotation represents a good match for an expected pattern.
390 bool FoundAndExpectedMatch;
391};
392
393/// Get an abbreviation for the check type.
394static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
395 switch (Ty) {
396 case Check::CheckPlain:
397 if (Ty.getCount() > 1)
398 return "count";
399 return "check";
400 case Check::CheckNext:
401 return "next";
402 case Check::CheckSame:
403 return "same";
404 case Check::CheckNot:
405 return "not";
406 case Check::CheckDAG:
407 return "dag";
408 case Check::CheckLabel:
409 return "label";
410 case Check::CheckEmpty:
411 return "empty";
412 case Check::CheckComment:
413 return "com";
414 case Check::CheckEOF:
415 return "eof";
416 case Check::CheckBadNot:
417 return "bad-not";
418 case Check::CheckBadCount:
419 return "bad-count";
420 case Check::CheckMisspelled:
421 return "misspelled";
422 case Check::CheckNone:
423 llvm_unreachable("invalid FileCheckType");
424 }
425 llvm_unreachable("unknown FileCheckType");
426}
427
428template <> struct llvm::DenseMapInfo<SMLoc> {
429 static unsigned getHashValue(const SMLoc &Loc) {
430 return DenseMapInfo<const char *>::getHashValue(PtrVal: Loc.getPointer());
431 }
432 static bool isEqual(const SMLoc &LHS, SMLoc &RHS) { return LHS == RHS; }
433};
434
435namespace {
436/// Stores all information needed to generate \c InputAnnotation labels.
437class InputAnnotationLabeler {
438private:
439 const SourceMgr &SM;
440 const unsigned CheckFileBufferID;
441 const std::pair<unsigned, unsigned> ImpPatBufferIDRange;
442
443 /// How many unique input annotation labels does each check pattern need?
444 /// Each check pattern can have multiple \c MatchResultDiag's, each followed
445 /// by a series of zero or more \c MatchNoteDiag's. Each such
446 /// \c MatchResultDiag and its \c MatchNoteDiag series can require multiple
447 /// labels.
448 DenseMap<SMLoc, unsigned> LabelCountPerPattern;
449 /// For each check pattern, how many labels have we generated so far?
450 DenseMap<SMLoc, unsigned> LabelIndexPerPattern;
451 /// For each check pattern, what is the common prefix for all its labels?
452 DenseMap<SMLoc, std::string> LabelPrefixPerPattern;
453 /// How many total labels have we generated so far over all check patterns?
454 unsigned LabelIndexGlobal;
455 /// The widest label generated so far over all check patterns.
456 unsigned LabelWidthGlobal;
457
458public:
459 /// - \p CheckFileBufferID is the buffer ID for the check file.
460 /// - \p ImpPatBufferIDRange is the buffer ID range for all implicit patterns.
461 InputAnnotationLabeler(const SourceMgr &SM, unsigned CheckFileBufferID,
462 std::pair<unsigned, unsigned> ImpPatBufferIDRange)
463 : SM(SM), CheckFileBufferID(CheckFileBufferID),
464 ImpPatBufferIDRange(ImpPatBufferIDRange), LabelIndexGlobal(0),
465 LabelWidthGlobal(0) {}
466 /// Add \c C to the number of expected \c makeLabel calls for \c Diag.
467 /// \c expect must not be called after the first \c makeLabel call.
468 void expect(const FileCheckDiag &Diag, unsigned C) {
469 LabelCountPerPattern[Diag.getMatchResultDiag().getCheckLoc()] += C;
470 }
471 /// Write a new globally unique label for \c Diag into \p Label, and write its
472 /// globally unique index into LabelIndexGlobal. All \c makeLabel calls must
473 /// have already been predicted by \c expect calls.
474 void makeLabel(const FileCheckDiag &Diag, std::string &Label,
475 unsigned &LabelIndexGlobal) {
476 const MatchResultDiag &MRD = Diag.getMatchResultDiag();
477 SMLoc CheckLoc = MRD.getCheckLoc();
478 std::string &LabelPrefix = LabelPrefixPerPattern[CheckLoc];
479 if (LabelPrefix.empty()) {
480 llvm::raw_string_ostream LabelStrm(LabelPrefix);
481 LabelStrm << GetCheckTypeAbbreviation(Ty: MRD.getCheckTy()) << ":";
482 unsigned CheckBufferID = SM.FindBufferContainingLoc(Loc: CheckLoc);
483 if (CheckBufferID == CheckFileBufferID)
484 LabelStrm << SM.getLineAndColumn(Loc: CheckLoc, BufferID: CheckBufferID).first;
485 else if (ImpPatBufferIDRange.first <= CheckBufferID &&
486 CheckBufferID < ImpPatBufferIDRange.second)
487 LabelStrm << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
488 else
489 llvm_unreachable(
490 "expected check location to be either in the check file or for an "
491 "implicit pattern");
492 }
493 assert(Label.empty() && "expected empty string for writing label");
494 llvm::raw_string_ostream LabelStrm(Label);
495 LabelStrm << LabelPrefix;
496 unsigned LabelCount = LabelCountPerPattern[CheckLoc];
497 unsigned LabelIndex = LabelIndexPerPattern[CheckLoc]++;
498 assert(LabelIndex < LabelCount &&
499 "expected all makeLabel calls to be predicted by expect calls");
500 if (LabelCount > 1)
501 LabelStrm << "'" << LabelIndex;
502 LabelWidthGlobal =
503 std::max(a: (std::string::size_type)LabelWidthGlobal, b: Label.size());
504 LabelIndexGlobal = this->LabelIndexGlobal++;
505 }
506 /// Get the widest label generated.
507 unsigned getLabelWidthGlobal() const { return LabelWidthGlobal; }
508};
509
510/// A range specifying where annotation markers are physically \a drawn in the
511/// input dump.
512struct MarkerRange {
513public:
514 /// An inclusive \c MarkerRange boundary. Both line and column use a 1-based
515 /// index origin.
516 struct Loc {
517 unsigned Line;
518 unsigned Col;
519 /// Make an invalid location to be overwritten before being used.
520 Loc() : Line(0), Col(0) {}
521 /// Make a valid location.
522 Loc(const std::pair<unsigned, unsigned> &LineAndCol)
523 : Line(LineAndCol.first), Col(LineAndCol.second) {}
524 };
525
526private:
527 /// Location of the first marked character.
528 Loc First;
529 /// Location of the last marked character.
530 Loc Last;
531
532public:
533 /// Make an invalid range to be overwritten before being used.
534 MarkerRange() = default;
535 /// \p Range specifies the \a logical input range to be depicted by annotation
536 /// markers \a drawn at the resulting \c MarkerRange.
537 ///
538 /// \a how that drawing depicts that logical input range is determined by
539 /// \p ShowExclusive. The drawing specifies either:
540 /// - The \a inclusive start and end bounds of the logical input range if
541 /// \p !ShowExclusive. In this case:
542 /// - If the logical input range is empty, then the resulting \c MarkerRange
543 /// is expanded to a single character. This avoids a missing marker, but
544 /// it means the markers for a single-character range are
545 /// indistinguishable from markers for an empty range.
546 /// - The first and last location of the \c MarkerRange are always real
547 /// locations in the input (never, for example, column 0).
548 /// - The \a exclusive start and end bounds of the logical input range if
549 /// \p ShowExclusive. In this case:
550 /// - The \c MarkerRange length is then always at least two because
551 /// exclusive boundaries never occupy the same location.
552 /// - If a \p Range boundary is an input line boundary, the corresponding
553 /// \c MarkerRange column might be in the line's margin (e.g., column 0)
554 /// to avoid placing a marker on an adjacent line. That decision can make
555 /// input annotations more concise (more line-liners) and easier to read.
556 /// It also avoids non-existent adjacent lines (e.g., line 0) that are not
557 /// depicted in the input dump.
558 MarkerRange(const SourceMgr &SM, SMRange Range, bool ShowExclusive = false) {
559 // Given an SMRange representing the range of text "range of text", the
560 // following example compares how it and the resulting MarkerRange encode
561 // the same start (s) and end (e) bounds:
562 //
563 // ....range of text....
564 // s e SMRange
565 // s e MarkerRange with ShowExclusive=false
566 // s e MarkerRange with ShowExclusive=true
567 if (ShowExclusive) {
568 // Range has inclusive start, but ShowExclusive requires exclusive start.
569 First = SM.getLineAndColumn(Loc: Range.Start);
570 --First.Col;
571 // Range has an exclusive end as ShowExclusive requires. If it is at a
572 // line boundary, it is at the start of the next line, so normally move it
573 // to the end of the previous line. For an empty range, do not do that as
574 // we do not want an end marker on the line before the start marker.
575 if (Range.Start == Range.End) {
576 Last = SM.getLineAndColumn(Loc: Range.End);
577 } else {
578 SMLoc EndLoc = SMLoc::getFromPointer(Ptr: Range.End.getPointer() - 1);
579 Last = SM.getLineAndColumn(Loc: EndLoc);
580 ++Last.Col;
581 }
582 return;
583 }
584 // Range has an inclusive start as !ShowExclusive requires.
585 First = SM.getLineAndColumn(Loc: Range.Start);
586 // Range has an exclusive end, but !ShowExclusive requires an inclusive end.
587 if (Range.Start == Range.End) {
588 // Convert the empty range to a one-character range.
589 Last = First;
590 } else {
591 // We cannot simply subtract one from the end column number because that
592 // might result in column 0, which does not exist and is thus incorrect
593 // for an inclusive boundary.
594 SMLoc EndLoc = SMLoc::getFromPointer(Ptr: Range.End.getPointer() - 1);
595 Last = SM.getLineAndColumn(Loc: EndLoc);
596 }
597 }
598 /// \p Loc specifies a single input character to be marked by a single
599 /// annotation marker character.
600 MarkerRange(Loc OneChar) : First(OneChar), Last(OneChar) {}
601 /// Is the marker range contained on a single line?
602 bool isSingleLine() const { return First.Line == Last.Line; }
603 /// Get the location of the first marked character.
604 Loc getFirstLoc() const { return First; }
605 /// Get the location of the last marked character.
606 Loc getLastLoc() const { return Last; }
607};
608
609/// Emits search range annotations for each \c MatchResultDiag as it is
610/// encountered.
611///
612/// In some cases, it emits a single, one-line annotation. Otherwise, it emits
613/// separate annotations for the start and end of the search range. The logic
614/// for making this determination is encapsulated in static member functions.
615class SearchRangeAnnotator {
616private:
617 const SourceMgr &SM;
618 InputAnnotationLabeler &Labeler;
619 std::vector<InputAnnotation> &Annotations;
620
621 /// The most recent \c MatchResultDiag, or \c nullptr if all search range
622 /// annotations have been added already for the most recent
623 /// \c MatchResultDiag.
624 const MatchResultDiag *MRD;
625 /// Assuming \c makesAnnotationsFor(MRD), would a \c SearchRangeAnnotator make
626 /// a one-line search range annotation for \p MRD? Either way, the search
627 /// range computed for \p MRD is stored in \p SearchRange.
628 static bool makesOneLinerFor(const SourceMgr &SM, const MatchResultDiag &MRD,
629 MarkerRange &SearchRange) {
630 assert(makesAnnotationsFor(MRD) &&
631 "expected makesAnnotationsFor to be checked first");
632 SearchRange = {SM, MRD.getSearchRange(), /*ShowExclusive=*/true};
633 return SearchRange.isSingleLine();
634 }
635 /// Make the next annotation for the current \c MatchResultDiag.
636 void makeAnnotation(bool Start) {
637 InputAnnotation &A = Annotations.emplace_back();
638 Labeler.makeLabel(Diag: *MRD, Label&: A.Label, LabelIndexGlobal&: A.LabelIndexGlobal);
639 A.IsFirstLine = true;
640 A.FoundAndExpectedMatch = false;
641 MarkerRange SearchRange;
642 if (makesOneLinerFor(SM, MRD: *MRD, SearchRange)) {
643 assert(Start && "expected no search range end annotation for one-liner");
644 A.InputLine = SearchRange.getFirstLoc().Line;
645 A.InputFirstCol = SearchRange.getFirstLoc().Col;
646 A.InputLastCol = SearchRange.getLastLoc().Col;
647 MatchCustomNoteDiag NoteDiag("search range (exclusive bounds)");
648 NoteDiag.setMatchResultDiag(MRD);
649 A.Marker = getMarker(Diag: NoteDiag);
650 A.Marker.Head = '{';
651 A.Marker.Mid = ' ';
652 A.Marker.Tail = '}';
653 MRD = nullptr;
654 return;
655 }
656 // We have separate annotations for start and end.
657 MarkerRange::Loc Loc =
658 Start ? SearchRange.getFirstLoc() : SearchRange.getLastLoc();
659 A.InputLine = Loc.Line;
660 A.InputFirstCol = A.InputLastCol = Loc.Col;
661 MatchCustomNoteDiag NoteDiag(std::string("search range ") +
662 (Start ? "start" : "end") + " (exclusive)");
663 NoteDiag.setMatchResultDiag(MRD);
664 A.Marker = getMarker(Diag: NoteDiag);
665 A.Marker.Head = Start ? '{' : '}';
666 }
667
668public:
669 /// Would a \c SearchRangeAnnotator make any search range annotations for
670 /// \p MRD?
671 static bool makesAnnotationsFor(const MatchResultDiag &MRD) {
672 return !MRD.getMatchRange() || MRD.isError();
673 }
674 /// Tell the labeler how many times this will call
675 /// \c InputAnnotationLabeler::makeLabel for \c MRD.
676 void predictLabelsFor(const MatchResultDiag &MRD) {
677 if (!makesAnnotationsFor(MRD))
678 return;
679 MarkerRange SearchRange;
680 Labeler.expect(Diag: MRD, C: makesOneLinerFor(SM, MRD, SearchRange) ? 1 : 2);
681 }
682 /// \p Annotations is where this annotator should append search range
683 /// annotations.
684 SearchRangeAnnotator(const SourceMgr &SM, InputAnnotationLabeler &Labeler,
685 std::vector<InputAnnotation> &Annotations)
686 : SM(SM), Labeler(Labeler), Annotations(Annotations), MRD(nullptr) {}
687 /// Emit any search range start annotation or one-line search range annotation
688 /// for \p MRDNew. Emit any search range end annotation for \p MRDNew at the
689 /// next call to \c newMatchResultDiag or \c endDiags.
690 void newMatchResultDiag(const MatchResultDiag &MRDNew) {
691 if (MRD) {
692 makeAnnotation(/*Start=*/false);
693 MRD = nullptr;
694 }
695 if (makesAnnotationsFor(MRD: MRDNew)) {
696 MRD = &MRDNew;
697 makeAnnotation(/*Start=*/true);
698 }
699 }
700 /// Emit any search range end annotation for the final \c MatchResultDiag
701 /// passed to \c newMatchResultDiag.
702 void endDiags() {
703 if (MRD)
704 makeAnnotation(/*Start=*/false);
705 }
706};
707
708/// Emits the main annotations for each \c FileCheckDiag as it is encountered.
709class FileCheckDiagAnnotator {
710private:
711 const SourceMgr &SM;
712 InputAnnotationLabeler &Labeler;
713 std::vector<InputAnnotation> &Annotations;
714 /// Would a \c FileCheckDiagAnnotator make any annotations for \p Diag?
715 static bool makesAnnotationsFor(const FileCheckDiag &Diag) {
716 const MatchResultDiag *MRD = dyn_cast<MatchResultDiag>(Val: &Diag);
717 if (!MRD)
718 return true;
719 if (SearchRangeAnnotator::makesAnnotationsFor(MRD: *MRD) &&
720 getMarker(Diag: *MRD).Note.empty())
721 return false;
722 return true;
723 }
724
725public:
726 /// Tell the labeler how many times this will call
727 /// \c InputAnnotationLabeler::makeLabel for \c Diag.
728 void predictLabelsFor(const FileCheckDiag &Diag) {
729 Labeler.expect(Diag, C: makesAnnotationsFor(Diag) ? 1 : 0);
730 }
731 /// \p Annotations is where this annotator should append annotations.
732 FileCheckDiagAnnotator(const SourceMgr &SM, InputAnnotationLabeler &Labeler,
733 std::vector<InputAnnotation> &Annotations)
734 : SM(SM), Labeler(Labeler), Annotations(Annotations) {}
735 /// Emit any annotations for \c Diag.
736 void makeAnnotations(const FileCheckDiag &Diag) {
737 if (!makesAnnotationsFor(Diag))
738 return;
739
740 // Build label that is unique for this input annotation before it is
741 // potentially broken across multiple lines.
742 InputAnnotation A;
743 Labeler.makeLabel(Diag, Label&: A.Label, LabelIndexGlobal&: A.LabelIndexGlobal);
744
745 // Build the input marker.
746 A.Marker = getMarker(Diag);
747
748 // Does this diagnostic mark text that has been successfully matched?
749 A.FoundAndExpectedMatch = false;
750 if (const MatchFoundDiag *Found = dyn_cast<MatchFoundDiag>(Val: &Diag)) {
751 if (Found->getStatus() == MatchFoundDiag::Success)
752 A.FoundAndExpectedMatch = true;
753 }
754
755 // If Diag has a match range, position the marker there. Otherwise,
756 // position the marker at the start of the most recent MatchResultDiag, with
757 // which it is associated.
758 MarkerRange InputRange;
759 if (Diag.getMatchRange()) {
760 InputRange = MarkerRange(SM, *Diag.getMatchRange());
761 } else {
762 const MatchResultDiag &MRD = Diag.getMatchResultDiag();
763 InputRange = MRD.getMatchRange() ? MarkerRange(SM, *MRD.getMatchRange())
764 : MarkerRange(SM, MRD.getSearchRange());
765 InputRange = MarkerRange(InputRange.getFirstLoc());
766 assert(A.Marker.Head == ' ' && "expected no marker for no match range");
767 }
768
769 // Compute the marker location, and break annotation into multiple
770 // annotations if it spans multiple lines.
771 A.IsFirstLine = true;
772 A.InputLine = InputRange.getFirstLoc().Line;
773 A.InputFirstCol = InputRange.getFirstLoc().Col;
774 if (InputRange.isSingleLine()) {
775 A.InputLastCol = InputRange.getLastLoc().Col;
776 Annotations.push_back(x: A);
777 } else {
778 A.InputLastCol = UINT_MAX;
779 char MarkerTail = A.Marker.Tail;
780 A.Marker.Tail = A.Marker.Mid;
781 Annotations.push_back(x: A);
782 for (unsigned L = InputRange.getFirstLoc().Line + 1,
783 E = InputRange.getLastLoc().Line;
784 L <= E; ++L) {
785 InputAnnotation B;
786 B.LabelIndexGlobal = A.LabelIndexGlobal;
787 B.Label = A.Label;
788 B.IsFirstLine = false;
789 B.InputLine = L;
790 B.Marker = A.Marker;
791 B.Marker.Head = B.Marker.Mid = A.Marker.Mid;
792 B.Marker.Tail = L != E ? A.Marker.Mid : MarkerTail;
793 B.Marker.Note = "";
794 B.InputFirstCol = 1;
795 B.InputLastCol = L != E ? UINT_MAX : InputRange.getLastLoc().Col;
796 B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
797 Annotations.push_back(x: B);
798 }
799 }
800 }
801};
802} // namespace
803
804static void
805buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
806 const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
807 const FileCheckDiagList &Diags,
808 std::vector<InputAnnotation> &Annotations,
809 unsigned &LabelWidthGlobal) {
810 InputAnnotationLabeler Labeler(SM, CheckFileBufferID, ImpPatBufferIDRange);
811 SearchRangeAnnotator TheSearchRangeAnnotator(SM, Labeler, Annotations);
812 FileCheckDiagAnnotator TheFileCheckDiagAnnotator(SM, Labeler, Annotations);
813 for (const FileCheckDiag &Diag : Diags) {
814 if (const MatchResultDiag *MRD = dyn_cast<MatchResultDiag>(Val: &Diag))
815 TheSearchRangeAnnotator.predictLabelsFor(MRD: *MRD);
816 TheFileCheckDiagAnnotator.predictLabelsFor(Diag);
817 }
818 for (const FileCheckDiag &Diag : Diags) {
819 if (const MatchResultDiag *MRD = dyn_cast<MatchResultDiag>(Val: &Diag))
820 TheSearchRangeAnnotator.newMatchResultDiag(MRDNew: *MRD);
821 TheFileCheckDiagAnnotator.makeAnnotations(Diag);
822 }
823 TheSearchRangeAnnotator.endDiags();
824 LabelWidthGlobal = Labeler.getLabelWidthGlobal();
825}
826
827static unsigned FindInputLineInFilter(
828 DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
829 const std::vector<InputAnnotation>::iterator &AnnotationBeg,
830 const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
831 if (DumpInputFilter == DumpInputFilterAll)
832 return CurInputLine;
833 for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
834 ++AnnotationItr) {
835 switch (DumpInputFilter) {
836 case DumpInputFilterAll:
837 llvm_unreachable("unexpected DumpInputFilterAll");
838 break;
839 case DumpInputFilterAnnotationFull:
840 return AnnotationItr->InputLine;
841 case DumpInputFilterAnnotation:
842 if (AnnotationItr->IsFirstLine)
843 return AnnotationItr->InputLine;
844 break;
845 case DumpInputFilterError:
846 if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
847 return AnnotationItr->InputLine;
848 break;
849 }
850 }
851 return UINT_MAX;
852}
853
854/// To OS, print a vertical ellipsis (right-justified at LabelWidthGlobal) if it
855/// would occupy less lines than ElidedLines, but print ElidedLines otherwise.
856/// Either way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing.
857static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
858 unsigned LabelWidthGlobal) {
859 if (ElidedLines.empty())
860 return;
861 unsigned EllipsisLines = 3;
862 if (EllipsisLines < StringRef(ElidedLines).count(C: '\n')) {
863 for (unsigned i = 0; i < EllipsisLines; ++i) {
864 WithColor(OS, raw_ostream::BRIGHT_BLACK, /*Bold=*/true)
865 << right_justify(Str: ".", Width: LabelWidthGlobal);
866 OS << '\n';
867 }
868 } else
869 OS << ElidedLines;
870 ElidedLines.clear();
871}
872
873static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
874 DumpInputFilterValue DumpInputFilter,
875 unsigned DumpInputContext,
876 StringRef InputFileText,
877 std::vector<InputAnnotation> &Annotations,
878 unsigned LabelWidthGlobal) {
879 OS << "Input was:\n<<<<<<\n";
880
881 // Sort annotations.
882 llvm::sort(C&: Annotations,
883 Comp: [](const InputAnnotation &A, const InputAnnotation &B) {
884 // 1. Sort annotations in the order of the input lines.
885 //
886 // This makes it easier to find relevant annotations while
887 // iterating input lines in the implementation below. FileCheck
888 // does not always produce diagnostics in the order of input
889 // lines due to, for example, CHECK-DAG and CHECK-NOT.
890 if (A.InputLine != B.InputLine)
891 return A.InputLine < B.InputLine;
892 // 2. Sort annotations in the temporal order FileCheck produced
893 // their associated diagnostics.
894 //
895 // This sort offers several benefits:
896 //
897 // A. On a single input line, the order of annotations reflects
898 // the FileCheck logic for processing directives/patterns.
899 // This can be helpful in understanding cases in which the
900 // order of the associated directives/patterns in the check
901 // file or on the command line either (i) does not match the
902 // temporal order in which FileCheck looks for matches for the
903 // directives/patterns (due to, for example, CHECK-LABEL,
904 // CHECK-NOT, or `--implicit-check-not`) or (ii) does match
905 // that order but does not match the order of those
906 // diagnostics along an input line (due to, for example,
907 // CHECK-DAG).
908 //
909 // On the other hand, because our presentation format presents
910 // input lines in order, there's no clear way to offer the
911 // same benefit across input lines. For consistency, it might
912 // then seem worthwhile to have annotations on a single line
913 // also sorted in input order (that is, by input column).
914 // However, in practice, this appears to be more confusing
915 // than helpful. Perhaps it's intuitive to expect annotations
916 // to be listed in the temporal order in which they were
917 // produced except in cases the presentation format obviously
918 // and inherently cannot support it (that is, across input
919 // lines).
920 //
921 // B. When diagnostics' annotations are split among multiple
922 // input lines, the user must track them from one input line
923 // to the next. One property of the sort chosen here is that
924 // it facilitates the user in this regard by ensuring the
925 // following: when comparing any two input lines, a
926 // diagnostic's annotations are sorted in the same position
927 // relative to all other diagnostics' annotations.
928 return A.LabelIndexGlobal < B.LabelIndexGlobal;
929 });
930
931 // Compute the width of the label column.
932 const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
933 *InputFileEnd = InputFileText.bytes_end();
934 unsigned LineCount = InputFileText.count(C: '\n');
935 if (InputFileEnd[-1] != '\n')
936 ++LineCount;
937 unsigned LineNoWidth = NumDigitsBase10(X: LineCount);
938 // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
939 // on input lines and (2) to the right of the (left-aligned) labels on
940 // annotation lines so that input lines and annotation lines are more
941 // visually distinct. For example, the spaces on the annotation lines ensure
942 // that input line numbers and check directive line numbers never align
943 // horizontally. Those line numbers might not even be for the same file.
944 // One space would be enough to achieve that, but more makes it even easier
945 // to see.
946 LabelWidthGlobal = std::max(a: LabelWidthGlobal, b: LineNoWidth) + 3;
947 LabelWidthGlobal = std::max(a: LabelWidthGlobal, b: DumpInputLabelWidth.getValue());
948
949 // Print annotated input lines.
950 unsigned PrevLineInFilter = 0; // 0 means none so far
951 unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
952 std::string ElidedLines;
953 raw_string_ostream ElidedLinesOS(ElidedLines);
954 ColorMode TheColorMode =
955 WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
956 if (TheColorMode == ColorMode::Enable)
957 ElidedLinesOS.enable_colors(enable: true);
958 auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
959 for (unsigned Line = 1;
960 InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
961 ++Line) {
962 const unsigned char *InputFileLine = InputFilePtr;
963
964 // Compute the previous and next line included by the filter.
965 if (NextLineInFilter < Line)
966 NextLineInFilter = FindInputLineInFilter(DumpInputFilter, CurInputLine: Line,
967 AnnotationBeg: AnnotationItr, AnnotationEnd);
968 assert(NextLineInFilter && "expected NextLineInFilter to be computed");
969 if (NextLineInFilter == Line)
970 PrevLineInFilter = Line;
971
972 // Elide this input line and its annotations if it's not within the
973 // context specified by -dump-input-context of an input line included by
974 // -dump-input-filter. However, in case the resulting ellipsis would occupy
975 // more lines than the input lines and annotations it elides, buffer the
976 // elided lines and annotations so we can print them instead.
977 raw_ostream *LineOS;
978 if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
979 (NextLineInFilter == UINT_MAX ||
980 Line + DumpInputContext < NextLineInFilter))
981 LineOS = &ElidedLinesOS;
982 else {
983 LineOS = &OS;
984 DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidthGlobal);
985 }
986
987 // Print right-aligned line number.
988 WithColor(*LineOS, raw_ostream::BRIGHT_BLACK, /*Bold=*/true, /*BG=*/false,
989 TheColorMode)
990 << format_decimal(N: Line, Width: LabelWidthGlobal) << ": ";
991
992 // For the case where -v and colors are enabled, find the annotations for
993 // good matches for expected patterns in order to highlight everything
994 // else in the line. There are no such annotations if -v is disabled.
995 std::vector<InputAnnotation> FoundAndExpectedMatches;
996 if (Req.Verbose && TheColorMode == ColorMode::Enable) {
997 for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
998 ++I) {
999 if (I->FoundAndExpectedMatch)
1000 FoundAndExpectedMatches.push_back(x: *I);
1001 }
1002 }
1003
1004 // Print numbered line with highlighting where there are no matches for
1005 // expected patterns.
1006 bool Newline = false;
1007 {
1008 WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
1009 /*BG=*/false, TheColorMode);
1010 bool InMatch = false;
1011 if (Req.Verbose) {
1012 COS.changeColor(Color: raw_ostream::CYAN, /*Bold=*/true, /*BG=*/true);
1013 } else {
1014 // Our goal is to use the output streams's default color so that input
1015 // text is legibile in both light and dark themes. SAVEDCOLOR above
1016 // currently ignores the Bold=false there, so we override it with
1017 // resetColor here, which ensures consistent colors with the resetColor
1018 // below anyway.
1019 COS.resetColor();
1020 }
1021 for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
1022 bool WasInMatch = InMatch;
1023 InMatch = false;
1024 for (const InputAnnotation &M : FoundAndExpectedMatches) {
1025 if (M.InputFirstCol <= Col && Col <= M.InputLastCol) {
1026 InMatch = true;
1027 break;
1028 }
1029 }
1030 // If !Req.Verbose, FoundAndExpectedMatches is empty, so InMatch and
1031 // WasInMatch remain false, so these color transitions never happen.
1032 if (!WasInMatch && InMatch)
1033 COS.resetColor();
1034 else if (WasInMatch && !InMatch)
1035 COS.changeColor(Color: raw_ostream::CYAN, Bold: true, BG: true);
1036 if (*InputFilePtr == '\n') {
1037 Newline = true;
1038 COS << ' ';
1039 } else
1040 COS << *InputFilePtr;
1041 ++InputFilePtr;
1042 }
1043 }
1044 *LineOS << '\n';
1045 unsigned InputLineWidth = InputFilePtr - InputFileLine;
1046
1047 // Print any annotations.
1048 while (AnnotationItr != AnnotationEnd &&
1049 AnnotationItr->InputLine == Line) {
1050 WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
1051 /*BG=*/false, TheColorMode);
1052 // The space below aligns with the ":" on the input line.
1053 COS << left_justify(Str: AnnotationItr->Label, Width: LabelWidthGlobal) << " ";
1054 unsigned Col;
1055 // A search range annotation at the beginning of the line starts at column
1056 // 0 because it is an exclusive boundary.
1057 for (Col = 0; Col < AnnotationItr->InputFirstCol; ++Col)
1058 COS << ' ';
1059 COS << AnnotationItr->Marker.Head;
1060 // If InputLastCol==UINT_MAX, stop at InputLineWidth.
1061 for (++Col; Col < AnnotationItr->InputLastCol && Col <= InputLineWidth;
1062 ++Col)
1063 COS << AnnotationItr->Marker.Mid;
1064 if (Col <= AnnotationItr->InputLastCol &&
1065 AnnotationItr->InputLastCol != UINT_MAX) {
1066 COS << AnnotationItr->Marker.Tail;
1067 ++Col;
1068 }
1069 const std::string &Note = AnnotationItr->Marker.Note;
1070 if (!Note.empty()) {
1071 // Put the note at the end of the input line. If we were to instead
1072 // put the note right after the marker, subsequent annotations for the
1073 // same input line might appear to mark this note instead of the input
1074 // line.
1075 for (; Col <= InputLineWidth + 1; ++Col)
1076 COS << ' ';
1077 COS << ' ' << Note;
1078 }
1079 COS << '\n';
1080 ++AnnotationItr;
1081 }
1082 }
1083 DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidthGlobal);
1084
1085 OS << ">>>>>>\n";
1086}
1087
1088int main(int argc, char **argv) {
1089 // Enable use of ANSI color codes because FileCheck is using them to
1090 // highlight text.
1091 llvm::sys::Process::UseANSIEscapeCodes(enable: true);
1092
1093 InitLLVM X(argc, argv);
1094 cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
1095 /*VFS*/ nullptr, EnvVar: "FILECHECK_OPTS");
1096
1097 // Select -dump-input* values. The -help documentation specifies the default
1098 // value and which value to choose if an option is specified multiple times.
1099 // In the latter case, the general rule of thumb is to choose the value that
1100 // provides the most information.
1101 DumpInputValue DumpInput =
1102 DumpInputs.empty() ? DumpInputFail : *llvm::max_element(Range&: DumpInputs);
1103 DumpInputFilterValue DumpInputFilter;
1104 if (DumpInputFilters.empty())
1105 DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
1106 : DumpInputFilterError;
1107 else
1108 DumpInputFilter = *llvm::max_element(Range&: DumpInputFilters);
1109 unsigned DumpInputContext =
1110 DumpInputContexts.empty() ? 5 : *llvm::max_element(Range&: DumpInputContexts);
1111
1112 if (DumpInput == DumpInputHelp) {
1113 DumpInputAnnotationHelp(OS&: outs());
1114 return 0;
1115 }
1116 if (CheckFilename.empty()) {
1117 errs() << "<check-file> not specified\n";
1118 return 2;
1119 }
1120
1121 FileCheckRequest Req;
1122 append_range(C&: Req.CheckPrefixes, R&: CheckPrefixes);
1123
1124 append_range(C&: Req.CommentPrefixes, R&: CommentPrefixes);
1125
1126 append_range(C&: Req.ImplicitCheckNot, R&: ImplicitCheckNot);
1127
1128 bool GlobalDefineError = false;
1129 for (StringRef G : GlobalDefines) {
1130 size_t EqIdx = G.find(C: '=');
1131 if (EqIdx == std::string::npos) {
1132 errs() << "Missing equal sign in command-line definition '-D" << G
1133 << "'\n";
1134 GlobalDefineError = true;
1135 continue;
1136 }
1137 if (EqIdx == 0) {
1138 errs() << "Missing variable name in command-line definition '-D" << G
1139 << "'\n";
1140 GlobalDefineError = true;
1141 continue;
1142 }
1143 Req.GlobalDefines.push_back(x: G);
1144 }
1145 if (GlobalDefineError)
1146 return 2;
1147
1148 Req.AllowEmptyInput = AllowEmptyInput;
1149 Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
1150 Req.EnableVarScope = EnableVarScope;
1151 Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
1152 Req.Verbose = Verbose;
1153 Req.VerboseVerbose = VerboseVerbose;
1154 Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
1155 Req.MatchFullLines = MatchFullLines;
1156 Req.IgnoreCase = IgnoreCase;
1157
1158 if (VerboseVerbose)
1159 Req.Verbose = true;
1160
1161 FileCheck FC(Req);
1162 if (!FC.ValidateCheckPrefixes())
1163 return 2;
1164
1165 SourceMgr SM;
1166
1167 // Read the expected strings from the check file.
1168 ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
1169 MemoryBuffer::getFileOrSTDIN(Filename: CheckFilename, /*IsText=*/true);
1170 if (std::error_code EC = CheckFileOrErr.getError()) {
1171 errs() << "Could not open check file '" << CheckFilename
1172 << "': " << EC.message() << '\n';
1173 return 2;
1174 }
1175 MemoryBuffer &CheckFile = *CheckFileOrErr.get();
1176
1177 SmallString<4096> CheckFileBuffer;
1178 StringRef CheckFileText = FC.CanonicalizeFile(MB&: CheckFile, OutputBuffer&: CheckFileBuffer);
1179
1180 unsigned CheckFileBufferID =
1181 SM.AddNewSourceBuffer(F: MemoryBuffer::getMemBuffer(
1182 InputData: CheckFileText, BufferName: CheckFile.getBufferIdentifier()),
1183 IncludeLoc: SMLoc());
1184
1185 std::pair<unsigned, unsigned> ImpPatBufferIDRange;
1186 if (FC.readCheckFile(SM, Buffer: CheckFileText, ImpPatBufferIDRange: &ImpPatBufferIDRange))
1187 return 2;
1188
1189 // Open the file to check and add it to SourceMgr.
1190 ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
1191 MemoryBuffer::getFileOrSTDIN(Filename: InputFilename, /*IsText=*/true);
1192 if (InputFilename == "-")
1193 InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
1194 if (std::error_code EC = InputFileOrErr.getError()) {
1195 errs() << "Could not open input file '" << InputFilename
1196 << "': " << EC.message() << '\n';
1197 return 2;
1198 }
1199 MemoryBuffer &InputFile = *InputFileOrErr.get();
1200
1201 if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
1202 errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
1203 DumpCommandLine(argc, argv);
1204 return 2;
1205 }
1206
1207 SmallString<4096> InputFileBuffer;
1208 StringRef InputFileText = FC.CanonicalizeFile(MB&: InputFile, OutputBuffer&: InputFileBuffer);
1209
1210 SM.AddNewSourceBuffer(F: MemoryBuffer::getMemBuffer(
1211 InputData: InputFileText, BufferName: InputFile.getBufferIdentifier()),
1212 IncludeLoc: SMLoc());
1213
1214 FileCheckDiagList Diags;
1215 int ExitCode = FC.checkInput(SM, Buffer: InputFileText,
1216 Diags: DumpInput == DumpInputNever ? nullptr : &Diags)
1217 ? EXIT_SUCCESS
1218 : 1;
1219 if (DumpInput == DumpInputAlways ||
1220 (ExitCode == 1 && DumpInput == DumpInputFail)) {
1221 errs() << "\n"
1222 << "Input file: " << InputFilename << "\n"
1223 << "Check file: " << CheckFilename << "\n"
1224 << "\n"
1225 << "-dump-input=help explains the following input dump.\n"
1226 << "\n";
1227 std::vector<InputAnnotation> Annotations;
1228 unsigned LabelWidthGlobal;
1229 buildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
1230 Annotations, LabelWidthGlobal);
1231 DumpAnnotatedInput(OS&: errs(), Req, DumpInputFilter, DumpInputContext,
1232 InputFileText, Annotations, LabelWidthGlobal);
1233 }
1234
1235 return ExitCode;
1236}
1237