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