diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2019-10-10 14:27:14 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2019-10-10 14:27:14 +0000 |
commit | d3aed7fc7986971ea15313871e7ffe8b4a5efa18 (patch) | |
tree | 81b3db4ccb84dc019c77569aa8e1950f7cccc4d0 /llvm/utils/FileCheck/FileCheck.cpp | |
parent | a89e5a41ec7c2c7358fa1d85023cb1c4f3f5d136 (diff) | |
download | llvm-d3aed7fc7986971ea15313871e7ffe8b4a5efa18.zip llvm-d3aed7fc7986971ea15313871e7ffe8b4a5efa18.tar.gz llvm-d3aed7fc7986971ea15313871e7ffe8b4a5efa18.tar.bz2 |
Revert "[FileCheck] Implement --ignore-case option."
This reverts commit r374339. It broke tests:
http://lab.llvm.org:8011/builders/clang-x86_64-debian-fast/builds/19066
llvm-svn: 374359
Diffstat (limited to 'llvm/utils/FileCheck/FileCheck.cpp')
-rw-r--r-- | llvm/utils/FileCheck/FileCheck.cpp | 1307 |
1 files changed, 651 insertions, 656 deletions
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp index 4d2cd19..8718be2 100644 --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -1,656 +1,651 @@ -//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// FileCheck does a line-by line check of a file that validates whether it
-// contains the expected content. This is useful for regression tests etc.
-//
-// This program exits with an exit status of 2 on error, exit status of 0 if
-// the file matched the expected contents, and exit status of 1 if it did not
-// contain the expected contents.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/InitLLVM.h"
-#include "llvm/Support/Process.h"
-#include "llvm/Support/WithColor.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/FileCheck.h"
-#include <cmath>
-using namespace llvm;
-
-static cl::extrahelp FileCheckOptsEnv(
- "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
- "from the command line.\n");
-
-static cl::opt<std::string>
- CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
-
-static cl::opt<std::string>
- InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
- cl::init("-"), cl::value_desc("filename"));
-
-static cl::list<std::string> CheckPrefixes(
- "check-prefix",
- cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
-static cl::alias CheckPrefixesAlias(
- "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
- cl::NotHidden,
- cl::desc(
- "Alias for -check-prefix permitting multiple comma separated values"));
-
-static cl::opt<bool> NoCanonicalizeWhiteSpace(
- "strict-whitespace",
- cl::desc("Do not treat all horizontal whitespace as equivalent"));
-
-static cl::opt<bool> IgnoreCase(
- "ignore-case",
- cl::desc("Use case-insensitive matching"));
-
-static cl::list<std::string> ImplicitCheckNot(
- "implicit-check-not",
- cl::desc("Add an implicit negative check with this pattern to every\n"
- "positive check. This can be used to ensure that no instances of\n"
- "this pattern occur which are not matched by a positive pattern"),
- cl::value_desc("pattern"));
-
-static cl::list<std::string>
- GlobalDefines("D", cl::AlwaysPrefix,
- cl::desc("Define a variable to be used in capture patterns."),
- cl::value_desc("VAR=VALUE"));
-
-static cl::opt<bool> AllowEmptyInput(
- "allow-empty", cl::init(false),
- cl::desc("Allow the input file to be empty. This is useful when making\n"
- "checks that some error message does not occur, for example."));
-
-static cl::opt<bool> MatchFullLines(
- "match-full-lines", cl::init(false),
- cl::desc("Require all positive matches to cover an entire input line.\n"
- "Allows leading and trailing whitespace if --strict-whitespace\n"
- "is not also passed."));
-
-static cl::opt<bool> EnableVarScope(
- "enable-var-scope", cl::init(false),
- cl::desc("Enables scope for regex variables. Variables with names that\n"
- "do not start with '$' will be reset at the beginning of\n"
- "each CHECK-LABEL block."));
-
-static cl::opt<bool> AllowDeprecatedDagOverlap(
- "allow-deprecated-dag-overlap", cl::init(false),
- cl::desc("Enable overlapping among matches in a group of consecutive\n"
- "CHECK-DAG directives. This option is deprecated and is only\n"
- "provided for convenience as old tests are migrated to the new\n"
- "non-overlapping CHECK-DAG implementation.\n"));
-
-static cl::opt<bool> Verbose(
- "v", cl::init(false),
- cl::desc("Print directive pattern matches, or add them to the input dump\n"
- "if enabled.\n"));
-
-static cl::opt<bool> VerboseVerbose(
- "vv", cl::init(false),
- cl::desc("Print information helpful in diagnosing internal FileCheck\n"
- "issues, or add it to the input dump if enabled. Implies\n"
- "-v.\n"));
-static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE";
-
-static cl::opt<bool> DumpInputOnFailure(
- "dump-input-on-failure",
- cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)),
- cl::desc("Dump original input to stderr before failing.\n"
- "The value can be also controlled using\n"
- "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n"
- "This option is deprecated in favor of -dump-input=fail.\n"));
-
-enum DumpInputValue {
- DumpInputDefault,
- DumpInputHelp,
- DumpInputNever,
- DumpInputFail,
- DumpInputAlways
-};
-
-static cl::opt<DumpInputValue> DumpInput(
- "dump-input", cl::init(DumpInputDefault),
- cl::desc("Dump input to stderr, adding annotations representing\n"
- " currently enabled diagnostics\n"),
- cl::value_desc("mode"),
- cl::values(clEnumValN(DumpInputHelp, "help",
- "Explain dump format and quit"),
- clEnumValN(DumpInputNever, "never", "Never dump input"),
- clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
- clEnumValN(DumpInputAlways, "always", "Always dump input")));
-
-typedef cl::list<std::string>::const_iterator prefix_iterator;
-
-
-
-
-
-
-
-static void DumpCommandLine(int argc, char **argv) {
- errs() << "FileCheck command line: ";
- for (int I = 0; I < argc; I++)
- errs() << " " << argv[I];
- errs() << "\n";
-}
-
-struct MarkerStyle {
- /// The starting char (before tildes) for marking the line.
- char Lead;
- /// What color to use for this annotation.
- raw_ostream::Colors Color;
- /// A note to follow the marker, or empty string if none.
- std::string Note;
- MarkerStyle() {}
- MarkerStyle(char Lead, raw_ostream::Colors Color,
- const std::string &Note = "")
- : Lead(Lead), Color(Color), Note(Note) {}
-};
-
-static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
- switch (MatchTy) {
- case FileCheckDiag::MatchFoundAndExpected:
- return MarkerStyle('^', raw_ostream::GREEN);
- case FileCheckDiag::MatchFoundButExcluded:
- return MarkerStyle('!', raw_ostream::RED, "error: no match expected");
- case FileCheckDiag::MatchFoundButWrongLine:
- return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line");
- case FileCheckDiag::MatchFoundButDiscarded:
- return MarkerStyle('!', raw_ostream::CYAN,
- "discard: overlaps earlier match");
- case FileCheckDiag::MatchNoneAndExcluded:
- return MarkerStyle('X', raw_ostream::GREEN);
- case FileCheckDiag::MatchNoneButExpected:
- return MarkerStyle('X', raw_ostream::RED, "error: no match found");
- case FileCheckDiag::MatchFuzzy:
- return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match");
- }
- llvm_unreachable_internal("unexpected match type");
-}
-
-static void DumpInputAnnotationHelp(raw_ostream &OS) {
- OS << "The following description was requested by -dump-input=help to\n"
- << "explain the input annotations printed by -dump-input=always and\n"
- << "-dump-input=fail:\n\n";
-
- // Labels for input lines.
- OS << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
- OS << " labels line number L of the input file\n";
-
- // Labels for annotation lines.
- OS << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
- OS << " labels the only match result for a pattern of type T from "
- << "line L of\n"
- << " the check file\n";
- OS << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
- OS << " labels the Nth match result for a pattern of type T from line "
- << "L of\n"
- << " the check file\n";
-
- // Markers on annotation lines.
- OS << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
- OS << " marks good match (reported if -v)\n"
- << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
- OS << " marks bad match, such as:\n"
- << " - CHECK-NEXT on same line as previous match (error)\n"
- << " - CHECK-NOT found (error)\n"
- << " - CHECK-DAG overlapping match (discarded, reported if "
- << "-vv)\n"
- << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
- OS << " marks search range when no match is found, such as:\n"
- << " - CHECK-NEXT not found (error)\n"
- << " - CHECK-NOT not found (success, reported if -vv)\n"
- << " - CHECK-DAG not found after discarded matches (error)\n"
- << " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
- OS << " marks fuzzy match when no match is found\n";
-
- // Colors.
- OS << " - colors ";
- WithColor(OS, raw_ostream::GREEN, true) << "success";
- OS << ", ";
- WithColor(OS, raw_ostream::RED, true) << "error";
- OS << ", ";
- WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
- OS << ", ";
- WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
- OS << ", ";
- WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
- OS << "\n\n"
- << "If you are not seeing color above or in input dumps, try: -color\n";
-}
-
-/// An annotation for a single input line.
-struct InputAnnotation {
- /// The check file line (one-origin indexing) where the directive that
- /// produced this annotation is located.
- unsigned CheckLine;
- /// The index of the match result for this check.
- unsigned CheckDiagIndex;
- /// The label for this annotation.
- std::string Label;
- /// What input line (one-origin indexing) this annotation marks. This might
- /// be different from the starting line of the original diagnostic if this is
- /// a non-initial fragment of a diagnostic that has been broken across
- /// multiple lines.
- unsigned InputLine;
- /// The column range (one-origin indexing, open end) in which to to mark the
- /// input line. If InputEndCol is UINT_MAX, treat it as the last column
- /// before the newline.
- unsigned InputStartCol, InputEndCol;
- /// The marker to use.
- MarkerStyle Marker;
- /// Whether this annotation represents a good match for an expected pattern.
- bool FoundAndExpectedMatch;
-};
-
-/// Get an abbreviation for the check type.
-std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
- switch (Ty) {
- case Check::CheckPlain:
- if (Ty.getCount() > 1)
- return "count";
- return "check";
- case Check::CheckNext:
- return "next";
- case Check::CheckSame:
- return "same";
- case Check::CheckNot:
- return "not";
- case Check::CheckDAG:
- return "dag";
- case Check::CheckLabel:
- return "label";
- case Check::CheckEmpty:
- return "empty";
- case Check::CheckEOF:
- return "eof";
- case Check::CheckBadNot:
- return "bad-not";
- case Check::CheckBadCount:
- return "bad-count";
- case Check::CheckNone:
- llvm_unreachable("invalid FileCheckType");
- }
- llvm_unreachable("unknown FileCheckType");
-}
-
-static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,
- std::vector<InputAnnotation> &Annotations,
- unsigned &LabelWidth) {
- // How many diagnostics has the current check seen so far?
- unsigned CheckDiagCount = 0;
- // What's the widest label?
- LabelWidth = 0;
- for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
- ++DiagItr) {
- InputAnnotation A;
-
- // Build label, which uniquely identifies this check result.
- A.CheckLine = DiagItr->CheckLine;
- llvm::raw_string_ostream Label(A.Label);
- Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"
- << DiagItr->CheckLine;
- A.CheckDiagIndex = UINT_MAX;
- auto DiagNext = std::next(DiagItr);
- if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
- DiagItr->CheckLine == DiagNext->CheckLine)
- A.CheckDiagIndex = CheckDiagCount++;
- else if (CheckDiagCount) {
- A.CheckDiagIndex = CheckDiagCount;
- CheckDiagCount = 0;
- }
- if (A.CheckDiagIndex != UINT_MAX)
- Label << "'" << A.CheckDiagIndex;
- else
- A.CheckDiagIndex = 0;
- Label.flush();
- LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
-
- A.Marker = GetMarker(DiagItr->MatchTy);
- A.FoundAndExpectedMatch =
- DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
-
- // Compute the mark location, and break annotation into multiple
- // annotations if it spans multiple lines.
- A.InputLine = DiagItr->InputStartLine;
- A.InputStartCol = DiagItr->InputStartCol;
- if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
- // Sometimes ranges are empty in order to indicate a specific point, but
- // that would mean nothing would be marked, so adjust the range to
- // include the following character.
- A.InputEndCol =
- std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
- Annotations.push_back(A);
- } else {
- assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
- "expected input range not to be inverted");
- A.InputEndCol = UINT_MAX;
- Annotations.push_back(A);
- for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
- L <= E; ++L) {
- // If a range ends before the first column on a line, then it has no
- // characters on that line, so there's nothing to render.
- if (DiagItr->InputEndCol == 1 && L == E)
- break;
- InputAnnotation B;
- B.CheckLine = A.CheckLine;
- B.CheckDiagIndex = A.CheckDiagIndex;
- B.Label = A.Label;
- B.InputLine = L;
- B.Marker = A.Marker;
- B.Marker.Lead = '~';
- B.Marker.Note = "";
- B.InputStartCol = 1;
- if (L != E)
- B.InputEndCol = UINT_MAX;
- else
- B.InputEndCol = DiagItr->InputEndCol;
- B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
- Annotations.push_back(B);
- }
- }
- }
-}
-
-static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
- StringRef InputFileText,
- std::vector<InputAnnotation> &Annotations,
- unsigned LabelWidth) {
- OS << "Full input was:\n<<<<<<\n";
-
- // Sort annotations.
- //
- // First, sort in the order of input lines to make it easier to find relevant
- // annotations while iterating input lines in the implementation below.
- // FileCheck diagnostics are not always reported and recorded in the order of
- // input lines due to, for example, CHECK-DAG and CHECK-NOT.
- //
- // Second, for annotations for the same input line, sort in the order of the
- // FileCheck directive's line in the check file (where there's at most one
- // directive per line) and then by the index of the match result for that
- // directive. The rationale of this choice is that, for any input line, this
- // sort establishes a total order of annotations that, with respect to match
- // results, is consistent across multiple lines, thus making match results
- // easier to track from one line to the next when they span multiple lines.
- std::sort(Annotations.begin(), Annotations.end(),
- [](const InputAnnotation &A, const InputAnnotation &B) {
- if (A.InputLine != B.InputLine)
- return A.InputLine < B.InputLine;
- if (A.CheckLine != B.CheckLine)
- return A.CheckLine < B.CheckLine;
- // FIXME: Sometimes CHECK-LABEL reports its match twice with
- // other diagnostics in between, and then diag index incrementing
- // fails to work properly, and then this assert fails. We should
- // suppress one of those diagnostics or do a better job of
- // computing this index. For now, we just produce a redundant
- // CHECK-LABEL annotation.
- // assert(A.CheckDiagIndex != B.CheckDiagIndex &&
- // "expected diagnostic indices to be unique within a "
- // " check line");
- return A.CheckDiagIndex < B.CheckDiagIndex;
- });
-
- // Compute the width of the label column.
- const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
- *InputFileEnd = InputFileText.bytes_end();
- unsigned LineCount = InputFileText.count('\n');
- if (InputFileEnd[-1] != '\n')
- ++LineCount;
- unsigned LineNoWidth = std::log10(LineCount) + 1;
- // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
- // on input lines and (2) to the right of the (left-aligned) labels on
- // annotation lines so that input lines and annotation lines are more
- // visually distinct. For example, the spaces on the annotation lines ensure
- // that input line numbers and check directive line numbers never align
- // horizontally. Those line numbers might not even be for the same file.
- // One space would be enough to achieve that, but more makes it even easier
- // to see.
- LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
-
- // Print annotated input lines.
- auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
- for (unsigned Line = 1;
- InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
- ++Line) {
- const unsigned char *InputFileLine = InputFilePtr;
-
- // Print right-aligned line number.
- WithColor(OS, raw_ostream::BLACK, true)
- << format_decimal(Line, LabelWidth) << ": ";
-
- // For the case where -v and colors are enabled, find the annotations for
- // good matches for expected patterns in order to highlight everything
- // else in the line. There are no such annotations if -v is disabled.
- std::vector<InputAnnotation> FoundAndExpectedMatches;
- if (Req.Verbose && WithColor(OS).colorsEnabled()) {
- for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
- ++I) {
- if (I->FoundAndExpectedMatch)
- FoundAndExpectedMatches.push_back(*I);
- }
- }
-
- // Print numbered line with highlighting where there are no matches for
- // expected patterns.
- bool Newline = false;
- {
- WithColor COS(OS);
- bool InMatch = false;
- if (Req.Verbose)
- COS.changeColor(raw_ostream::CYAN, true, true);
- for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
- bool WasInMatch = InMatch;
- InMatch = false;
- for (auto M : FoundAndExpectedMatches) {
- if (M.InputStartCol <= Col && Col < M.InputEndCol) {
- InMatch = true;
- break;
- }
- }
- if (!WasInMatch && InMatch)
- COS.resetColor();
- else if (WasInMatch && !InMatch)
- COS.changeColor(raw_ostream::CYAN, true, true);
- if (*InputFilePtr == '\n')
- Newline = true;
- else
- COS << *InputFilePtr;
- ++InputFilePtr;
- }
- }
- OS << '\n';
- unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline;
-
- // Print any annotations.
- while (AnnotationItr != AnnotationEnd &&
- AnnotationItr->InputLine == Line) {
- WithColor COS(OS, AnnotationItr->Marker.Color, true);
- // The two spaces below are where the ": " appears on input lines.
- COS << left_justify(AnnotationItr->Label, LabelWidth) << " ";
- unsigned Col;
- for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
- COS << ' ';
- COS << AnnotationItr->Marker.Lead;
- // If InputEndCol=UINT_MAX, stop at InputLineWidth.
- for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
- ++Col)
- COS << '~';
- const std::string &Note = AnnotationItr->Marker.Note;
- if (!Note.empty()) {
- // Put the note at the end of the input line. If we were to instead
- // put the note right after the marker, subsequent annotations for the
- // same input line might appear to mark this note instead of the input
- // line.
- for (; Col <= InputLineWidth; ++Col)
- COS << ' ';
- COS << ' ' << Note;
- }
- COS << '\n';
- ++AnnotationItr;
- }
- }
-
- OS << ">>>>>>\n";
-}
-
-int main(int argc, char **argv) {
- // Enable use of ANSI color codes because FileCheck is using them to
- // highlight text.
- llvm::sys::Process::UseANSIEscapeCodes(true);
-
- InitLLVM X(argc, argv);
- cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
- "FILECHECK_OPTS");
- if (DumpInput == DumpInputHelp) {
- DumpInputAnnotationHelp(outs());
- return 0;
- }
- if (CheckFilename.empty()) {
- errs() << "<check-file> not specified\n";
- return 2;
- }
-
- FileCheckRequest Req;
- for (auto Prefix : CheckPrefixes)
- Req.CheckPrefixes.push_back(Prefix);
-
- for (auto CheckNot : ImplicitCheckNot)
- Req.ImplicitCheckNot.push_back(CheckNot);
-
- bool GlobalDefineError = false;
- for (auto G : GlobalDefines) {
- size_t EqIdx = G.find('=');
- if (EqIdx == std::string::npos) {
- errs() << "Missing equal sign in command-line definition '-D" << G
- << "'\n";
- GlobalDefineError = true;
- continue;
- }
- if (EqIdx == 0) {
- errs() << "Missing variable name in command-line definition '-D" << G
- << "'\n";
- GlobalDefineError = true;
- continue;
- }
- Req.GlobalDefines.push_back(G);
- }
- if (GlobalDefineError)
- return 2;
-
- Req.AllowEmptyInput = AllowEmptyInput;
- Req.EnableVarScope = EnableVarScope;
- Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
- Req.Verbose = Verbose;
- Req.VerboseVerbose = VerboseVerbose;
- Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
- Req.MatchFullLines = MatchFullLines;
- Req.IgnoreCase = IgnoreCase;
-
- if (VerboseVerbose)
- Req.Verbose = true;
-
- FileCheck FC(Req);
- if (!FC.ValidateCheckPrefixes()) {
- errs() << "Supplied check-prefix is invalid! Prefixes must be unique and "
- "start with a letter and contain only alphanumeric characters, "
- "hyphens and underscores\n";
- return 2;
- }
-
- Regex PrefixRE = FC.buildCheckPrefixRegex();
- std::string REError;
- if (!PrefixRE.isValid(REError)) {
- errs() << "Unable to combine check-prefix strings into a prefix regular "
- "expression! This is likely a bug in FileCheck's verification of "
- "the check-prefix strings. Regular expression parsing failed "
- "with the following error: "
- << REError << "\n";
- return 2;
- }
-
- SourceMgr SM;
-
- // Read the expected strings from the check file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
- MemoryBuffer::getFileOrSTDIN(CheckFilename);
- if (std::error_code EC = CheckFileOrErr.getError()) {
- errs() << "Could not open check file '" << CheckFilename
- << "': " << EC.message() << '\n';
- return 2;
- }
- MemoryBuffer &CheckFile = *CheckFileOrErr.get();
-
- SmallString<4096> CheckFileBuffer;
- StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
-
- SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
- CheckFileText, CheckFile.getBufferIdentifier()),
- SMLoc());
-
- if (FC.readCheckFile(SM, CheckFileText, PrefixRE))
- return 2;
-
- // Open the file to check and add it to SourceMgr.
- ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
- MemoryBuffer::getFileOrSTDIN(InputFilename);
- if (std::error_code EC = InputFileOrErr.getError()) {
- errs() << "Could not open input file '" << InputFilename
- << "': " << EC.message() << '\n';
- return 2;
- }
- MemoryBuffer &InputFile = *InputFileOrErr.get();
-
- if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
- errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
- DumpCommandLine(argc, argv);
- return 2;
- }
-
- SmallString<4096> InputFileBuffer;
- StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
-
- SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
- InputFileText, InputFile.getBufferIdentifier()),
- SMLoc());
-
- if (DumpInput == DumpInputDefault)
- DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever;
-
- std::vector<FileCheckDiag> Diags;
- int ExitCode = FC.checkInput(SM, InputFileText,
- DumpInput == DumpInputNever ? nullptr : &Diags)
- ? EXIT_SUCCESS
- : 1;
- if (DumpInput == DumpInputAlways ||
- (ExitCode == 1 && DumpInput == DumpInputFail)) {
- errs() << "\n"
- << "Input file: "
- << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue())
- << "\n"
- << "Check file: " << CheckFilename << "\n"
- << "\n"
- << "-dump-input=help describes the format of the following dump.\n"
- << "\n";
- std::vector<InputAnnotation> Annotations;
- unsigned LabelWidth;
- BuildInputAnnotations(Diags, Annotations, LabelWidth);
- DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth);
- }
-
- return ExitCode;
-}
+//===- FileCheck.cpp - Check that File's Contents match what is expected --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// FileCheck does a line-by line check of a file that validates whether it +// contains the expected content. This is useful for regression tests etc. +// +// This program exits with an exit status of 2 on error, exit status of 0 if +// the file matched the expected contents, and exit status of 1 if it did not +// contain the expected contents. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FileCheck.h" +#include <cmath> +using namespace llvm; + +static cl::extrahelp FileCheckOptsEnv( + "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n" + "from the command line.\n"); + +static cl::opt<std::string> + CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional); + +static cl::opt<std::string> + InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), + cl::init("-"), cl::value_desc("filename")); + +static cl::list<std::string> CheckPrefixes( + "check-prefix", + cl::desc("Prefix to use from check file (defaults to 'CHECK')")); +static cl::alias CheckPrefixesAlias( + "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, + cl::NotHidden, + cl::desc( + "Alias for -check-prefix permitting multiple comma separated values")); + +static cl::opt<bool> NoCanonicalizeWhiteSpace( + "strict-whitespace", + cl::desc("Do not treat all horizontal whitespace as equivalent")); + +static cl::list<std::string> ImplicitCheckNot( + "implicit-check-not", + cl::desc("Add an implicit negative check with this pattern to every\n" + "positive check. This can be used to ensure that no instances of\n" + "this pattern occur which are not matched by a positive pattern"), + cl::value_desc("pattern")); + +static cl::list<std::string> + GlobalDefines("D", cl::AlwaysPrefix, + cl::desc("Define a variable to be used in capture patterns."), + cl::value_desc("VAR=VALUE")); + +static cl::opt<bool> AllowEmptyInput( + "allow-empty", cl::init(false), + cl::desc("Allow the input file to be empty. This is useful when making\n" + "checks that some error message does not occur, for example.")); + +static cl::opt<bool> MatchFullLines( + "match-full-lines", cl::init(false), + cl::desc("Require all positive matches to cover an entire input line.\n" + "Allows leading and trailing whitespace if --strict-whitespace\n" + "is not also passed.")); + +static cl::opt<bool> EnableVarScope( + "enable-var-scope", cl::init(false), + cl::desc("Enables scope for regex variables. Variables with names that\n" + "do not start with '$' will be reset at the beginning of\n" + "each CHECK-LABEL block.")); + +static cl::opt<bool> AllowDeprecatedDagOverlap( + "allow-deprecated-dag-overlap", cl::init(false), + cl::desc("Enable overlapping among matches in a group of consecutive\n" + "CHECK-DAG directives. This option is deprecated and is only\n" + "provided for convenience as old tests are migrated to the new\n" + "non-overlapping CHECK-DAG implementation.\n")); + +static cl::opt<bool> Verbose( + "v", cl::init(false), + cl::desc("Print directive pattern matches, or add them to the input dump\n" + "if enabled.\n")); + +static cl::opt<bool> VerboseVerbose( + "vv", cl::init(false), + cl::desc("Print information helpful in diagnosing internal FileCheck\n" + "issues, or add it to the input dump if enabled. Implies\n" + "-v.\n")); +static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; + +static cl::opt<bool> DumpInputOnFailure( + "dump-input-on-failure", + cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)), + cl::desc("Dump original input to stderr before failing.\n" + "The value can be also controlled using\n" + "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" + "This option is deprecated in favor of -dump-input=fail.\n")); + +enum DumpInputValue { + DumpInputDefault, + DumpInputHelp, + DumpInputNever, + DumpInputFail, + DumpInputAlways +}; + +static cl::opt<DumpInputValue> DumpInput( + "dump-input", cl::init(DumpInputDefault), + cl::desc("Dump input to stderr, adding annotations representing\n" + " currently enabled diagnostics\n"), + cl::value_desc("mode"), + cl::values(clEnumValN(DumpInputHelp, "help", + "Explain dump format and quit"), + clEnumValN(DumpInputNever, "never", "Never dump input"), + clEnumValN(DumpInputFail, "fail", "Dump input on failure"), + clEnumValN(DumpInputAlways, "always", "Always dump input"))); + +typedef cl::list<std::string>::const_iterator prefix_iterator; + + + + + + + +static void DumpCommandLine(int argc, char **argv) { + errs() << "FileCheck command line: "; + for (int I = 0; I < argc; I++) + errs() << " " << argv[I]; + errs() << "\n"; +} + +struct MarkerStyle { + /// The starting char (before tildes) for marking the line. + char Lead; + /// What color to use for this annotation. + raw_ostream::Colors Color; + /// A note to follow the marker, or empty string if none. + std::string Note; + MarkerStyle() {} + MarkerStyle(char Lead, raw_ostream::Colors Color, + const std::string &Note = "") + : Lead(Lead), Color(Color), Note(Note) {} +}; + +static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { + switch (MatchTy) { + case FileCheckDiag::MatchFoundAndExpected: + return MarkerStyle('^', raw_ostream::GREEN); + case FileCheckDiag::MatchFoundButExcluded: + return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); + case FileCheckDiag::MatchFoundButWrongLine: + return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); + case FileCheckDiag::MatchFoundButDiscarded: + return MarkerStyle('!', raw_ostream::CYAN, + "discard: overlaps earlier match"); + case FileCheckDiag::MatchNoneAndExcluded: + return MarkerStyle('X', raw_ostream::GREEN); + case FileCheckDiag::MatchNoneButExpected: + return MarkerStyle('X', raw_ostream::RED, "error: no match found"); + case FileCheckDiag::MatchFuzzy: + return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); + } + llvm_unreachable_internal("unexpected match type"); +} + +static void DumpInputAnnotationHelp(raw_ostream &OS) { + OS << "The following description was requested by -dump-input=help to\n" + << "explain the input annotations printed by -dump-input=always and\n" + << "-dump-input=fail:\n\n"; + + // Labels for input lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; + OS << " labels line number L of the input file\n"; + + // Labels for annotation lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; + OS << " labels the only match result for a pattern of type T from " + << "line L of\n" + << " the check file\n"; + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; + OS << " labels the Nth match result for a pattern of type T from line " + << "L of\n" + << " the check file\n"; + + // Markers on annotation lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; + OS << " marks good match (reported if -v)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; + OS << " marks bad match, such as:\n" + << " - CHECK-NEXT on same line as previous match (error)\n" + << " - CHECK-NOT found (error)\n" + << " - CHECK-DAG overlapping match (discarded, reported if " + << "-vv)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; + OS << " marks search range when no match is found, such as:\n" + << " - CHECK-NEXT not found (error)\n" + << " - CHECK-NOT not found (success, reported if -vv)\n" + << " - CHECK-DAG not found after discarded matches (error)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; + OS << " marks fuzzy match when no match is found\n"; + + // Colors. + OS << " - colors "; + WithColor(OS, raw_ostream::GREEN, true) << "success"; + OS << ", "; + WithColor(OS, raw_ostream::RED, true) << "error"; + OS << ", "; + WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; + OS << ", "; + WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; + OS << ", "; + WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; + OS << "\n\n" + << "If you are not seeing color above or in input dumps, try: -color\n"; +} + +/// An annotation for a single input line. +struct InputAnnotation { + /// The check file line (one-origin indexing) where the directive that + /// produced this annotation is located. + unsigned CheckLine; + /// The index of the match result for this check. + unsigned CheckDiagIndex; + /// The label for this annotation. + std::string Label; + /// What input line (one-origin indexing) this annotation marks. This might + /// be different from the starting line of the original diagnostic if this is + /// a non-initial fragment of a diagnostic that has been broken across + /// multiple lines. + unsigned InputLine; + /// The column range (one-origin indexing, open end) in which to to mark the + /// input line. If InputEndCol is UINT_MAX, treat it as the last column + /// before the newline. + unsigned InputStartCol, InputEndCol; + /// The marker to use. + MarkerStyle Marker; + /// Whether this annotation represents a good match for an expected pattern. + bool FoundAndExpectedMatch; +}; + +/// Get an abbreviation for the check type. +std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { + switch (Ty) { + case Check::CheckPlain: + if (Ty.getCount() > 1) + return "count"; + return "check"; + case Check::CheckNext: + return "next"; + case Check::CheckSame: + return "same"; + case Check::CheckNot: + return "not"; + case Check::CheckDAG: + return "dag"; + case Check::CheckLabel: + return "label"; + case Check::CheckEmpty: + return "empty"; + case Check::CheckEOF: + return "eof"; + case Check::CheckBadNot: + return "bad-not"; + case Check::CheckBadCount: + return "bad-count"; + case Check::CheckNone: + llvm_unreachable("invalid FileCheckType"); + } + llvm_unreachable("unknown FileCheckType"); +} + +static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags, + std::vector<InputAnnotation> &Annotations, + unsigned &LabelWidth) { + // How many diagnostics has the current check seen so far? + unsigned CheckDiagCount = 0; + // What's the widest label? + LabelWidth = 0; + for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; + ++DiagItr) { + InputAnnotation A; + + // Build label, which uniquely identifies this check result. + A.CheckLine = DiagItr->CheckLine; + llvm::raw_string_ostream Label(A.Label); + Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" + << DiagItr->CheckLine; + A.CheckDiagIndex = UINT_MAX; + auto DiagNext = std::next(DiagItr); + if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && + DiagItr->CheckLine == DiagNext->CheckLine) + A.CheckDiagIndex = CheckDiagCount++; + else if (CheckDiagCount) { + A.CheckDiagIndex = CheckDiagCount; + CheckDiagCount = 0; + } + if (A.CheckDiagIndex != UINT_MAX) + Label << "'" << A.CheckDiagIndex; + else + A.CheckDiagIndex = 0; + Label.flush(); + LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); + + A.Marker = GetMarker(DiagItr->MatchTy); + A.FoundAndExpectedMatch = + DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; + + // Compute the mark location, and break annotation into multiple + // annotations if it spans multiple lines. + A.InputLine = DiagItr->InputStartLine; + A.InputStartCol = DiagItr->InputStartCol; + if (DiagItr->InputStartLine == DiagItr->InputEndLine) { + // Sometimes ranges are empty in order to indicate a specific point, but + // that would mean nothing would be marked, so adjust the range to + // include the following character. + A.InputEndCol = + std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); + Annotations.push_back(A); + } else { + assert(DiagItr->InputStartLine < DiagItr->InputEndLine && + "expected input range not to be inverted"); + A.InputEndCol = UINT_MAX; + Annotations.push_back(A); + for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; + L <= E; ++L) { + // If a range ends before the first column on a line, then it has no + // characters on that line, so there's nothing to render. + if (DiagItr->InputEndCol == 1 && L == E) + break; + InputAnnotation B; + B.CheckLine = A.CheckLine; + B.CheckDiagIndex = A.CheckDiagIndex; + B.Label = A.Label; + B.InputLine = L; + B.Marker = A.Marker; + B.Marker.Lead = '~'; + B.Marker.Note = ""; + B.InputStartCol = 1; + if (L != E) + B.InputEndCol = UINT_MAX; + else + B.InputEndCol = DiagItr->InputEndCol; + B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; + Annotations.push_back(B); + } + } + } +} + +static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, + StringRef InputFileText, + std::vector<InputAnnotation> &Annotations, + unsigned LabelWidth) { + OS << "Full input was:\n<<<<<<\n"; + + // Sort annotations. + // + // First, sort in the order of input lines to make it easier to find relevant + // annotations while iterating input lines in the implementation below. + // FileCheck diagnostics are not always reported and recorded in the order of + // input lines due to, for example, CHECK-DAG and CHECK-NOT. + // + // Second, for annotations for the same input line, sort in the order of the + // FileCheck directive's line in the check file (where there's at most one + // directive per line) and then by the index of the match result for that + // directive. The rationale of this choice is that, for any input line, this + // sort establishes a total order of annotations that, with respect to match + // results, is consistent across multiple lines, thus making match results + // easier to track from one line to the next when they span multiple lines. + std::sort(Annotations.begin(), Annotations.end(), + [](const InputAnnotation &A, const InputAnnotation &B) { + if (A.InputLine != B.InputLine) + return A.InputLine < B.InputLine; + if (A.CheckLine != B.CheckLine) + return A.CheckLine < B.CheckLine; + // FIXME: Sometimes CHECK-LABEL reports its match twice with + // other diagnostics in between, and then diag index incrementing + // fails to work properly, and then this assert fails. We should + // suppress one of those diagnostics or do a better job of + // computing this index. For now, we just produce a redundant + // CHECK-LABEL annotation. + // assert(A.CheckDiagIndex != B.CheckDiagIndex && + // "expected diagnostic indices to be unique within a " + // " check line"); + return A.CheckDiagIndex < B.CheckDiagIndex; + }); + + // Compute the width of the label column. + const unsigned char *InputFilePtr = InputFileText.bytes_begin(), + *InputFileEnd = InputFileText.bytes_end(); + unsigned LineCount = InputFileText.count('\n'); + if (InputFileEnd[-1] != '\n') + ++LineCount; + unsigned LineNoWidth = std::log10(LineCount) + 1; + // +3 below adds spaces (1) to the left of the (right-aligned) line numbers + // on input lines and (2) to the right of the (left-aligned) labels on + // annotation lines so that input lines and annotation lines are more + // visually distinct. For example, the spaces on the annotation lines ensure + // that input line numbers and check directive line numbers never align + // horizontally. Those line numbers might not even be for the same file. + // One space would be enough to achieve that, but more makes it even easier + // to see. + LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; + + // Print annotated input lines. + auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); + for (unsigned Line = 1; + InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; + ++Line) { + const unsigned char *InputFileLine = InputFilePtr; + + // Print right-aligned line number. + WithColor(OS, raw_ostream::BLACK, true) + << format_decimal(Line, LabelWidth) << ": "; + + // For the case where -v and colors are enabled, find the annotations for + // good matches for expected patterns in order to highlight everything + // else in the line. There are no such annotations if -v is disabled. + std::vector<InputAnnotation> FoundAndExpectedMatches; + if (Req.Verbose && WithColor(OS).colorsEnabled()) { + for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; + ++I) { + if (I->FoundAndExpectedMatch) + FoundAndExpectedMatches.push_back(*I); + } + } + + // Print numbered line with highlighting where there are no matches for + // expected patterns. + bool Newline = false; + { + WithColor COS(OS); + bool InMatch = false; + if (Req.Verbose) + COS.changeColor(raw_ostream::CYAN, true, true); + for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { + bool WasInMatch = InMatch; + InMatch = false; + for (auto M : FoundAndExpectedMatches) { + if (M.InputStartCol <= Col && Col < M.InputEndCol) { + InMatch = true; + break; + } + } + if (!WasInMatch && InMatch) + COS.resetColor(); + else if (WasInMatch && !InMatch) + COS.changeColor(raw_ostream::CYAN, true, true); + if (*InputFilePtr == '\n') + Newline = true; + else + COS << *InputFilePtr; + ++InputFilePtr; + } + } + OS << '\n'; + unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; + + // Print any annotations. + while (AnnotationItr != AnnotationEnd && + AnnotationItr->InputLine == Line) { + WithColor COS(OS, AnnotationItr->Marker.Color, true); + // The two spaces below are where the ": " appears on input lines. + COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; + unsigned Col; + for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) + COS << ' '; + COS << AnnotationItr->Marker.Lead; + // If InputEndCol=UINT_MAX, stop at InputLineWidth. + for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; + ++Col) + COS << '~'; + const std::string &Note = AnnotationItr->Marker.Note; + if (!Note.empty()) { + // Put the note at the end of the input line. If we were to instead + // put the note right after the marker, subsequent annotations for the + // same input line might appear to mark this note instead of the input + // line. + for (; Col <= InputLineWidth; ++Col) + COS << ' '; + COS << ' ' << Note; + } + COS << '\n'; + ++AnnotationItr; + } + } + + OS << ">>>>>>\n"; +} + +int main(int argc, char **argv) { + // Enable use of ANSI color codes because FileCheck is using them to + // highlight text. + llvm::sys::Process::UseANSIEscapeCodes(true); + + InitLLVM X(argc, argv); + cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, + "FILECHECK_OPTS"); + if (DumpInput == DumpInputHelp) { + DumpInputAnnotationHelp(outs()); + return 0; + } + if (CheckFilename.empty()) { + errs() << "<check-file> not specified\n"; + return 2; + } + + FileCheckRequest Req; + for (auto Prefix : CheckPrefixes) + Req.CheckPrefixes.push_back(Prefix); + + for (auto CheckNot : ImplicitCheckNot) + Req.ImplicitCheckNot.push_back(CheckNot); + + bool GlobalDefineError = false; + for (auto G : GlobalDefines) { + size_t EqIdx = G.find('='); + if (EqIdx == std::string::npos) { + errs() << "Missing equal sign in command-line definition '-D" << G + << "'\n"; + GlobalDefineError = true; + continue; + } + if (EqIdx == 0) { + errs() << "Missing variable name in command-line definition '-D" << G + << "'\n"; + GlobalDefineError = true; + continue; + } + Req.GlobalDefines.push_back(G); + } + if (GlobalDefineError) + return 2; + + Req.AllowEmptyInput = AllowEmptyInput; + Req.EnableVarScope = EnableVarScope; + Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; + Req.Verbose = Verbose; + Req.VerboseVerbose = VerboseVerbose; + Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; + Req.MatchFullLines = MatchFullLines; + + if (VerboseVerbose) + Req.Verbose = true; + + FileCheck FC(Req); + if (!FC.ValidateCheckPrefixes()) { + errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " + "start with a letter and contain only alphanumeric characters, " + "hyphens and underscores\n"; + return 2; + } + + Regex PrefixRE = FC.buildCheckPrefixRegex(); + std::string REError; + if (!PrefixRE.isValid(REError)) { + errs() << "Unable to combine check-prefix strings into a prefix regular " + "expression! This is likely a bug in FileCheck's verification of " + "the check-prefix strings. Regular expression parsing failed " + "with the following error: " + << REError << "\n"; + return 2; + } + + SourceMgr SM; + + // Read the expected strings from the check file. + ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr = + MemoryBuffer::getFileOrSTDIN(CheckFilename); + if (std::error_code EC = CheckFileOrErr.getError()) { + errs() << "Could not open check file '" << CheckFilename + << "': " << EC.message() << '\n'; + return 2; + } + MemoryBuffer &CheckFile = *CheckFileOrErr.get(); + + SmallString<4096> CheckFileBuffer; + StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); + + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + CheckFileText, CheckFile.getBufferIdentifier()), + SMLoc()); + + if (FC.readCheckFile(SM, CheckFileText, PrefixRE)) + return 2; + + // Open the file to check and add it to SourceMgr. + ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (std::error_code EC = InputFileOrErr.getError()) { + errs() << "Could not open input file '" << InputFilename + << "': " << EC.message() << '\n'; + return 2; + } + MemoryBuffer &InputFile = *InputFileOrErr.get(); + + if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { + errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; + DumpCommandLine(argc, argv); + return 2; + } + + SmallString<4096> InputFileBuffer; + StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); + + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + InputFileText, InputFile.getBufferIdentifier()), + SMLoc()); + + if (DumpInput == DumpInputDefault) + DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; + + std::vector<FileCheckDiag> Diags; + int ExitCode = FC.checkInput(SM, InputFileText, + DumpInput == DumpInputNever ? nullptr : &Diags) + ? EXIT_SUCCESS + : 1; + if (DumpInput == DumpInputAlways || + (ExitCode == 1 && DumpInput == DumpInputFail)) { + errs() << "\n" + << "Input file: " + << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue()) + << "\n" + << "Check file: " << CheckFilename << "\n" + << "\n" + << "-dump-input=help describes the format of the following dump.\n" + << "\n"; + std::vector<InputAnnotation> Annotations; + unsigned LabelWidth; + BuildInputAnnotations(Diags, Annotations, LabelWidth); + DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); + } + + return ExitCode; +} |