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