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