[llvm] [FileCheck] Add a diff output option for FileCheck (PR #187120)
Shivam Gupta via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 31 00:27:41 PDT 2026
https://github.com/xgupta updated https://github.com/llvm/llvm-project/pull/187120
>From 327c81350c5f4f6cb4e42c8ccec9bc63ddecc703 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Sun, 15 Mar 2026 02:01:51 +0530
Subject: [PATCH 01/14] [FileCheck] Add a diff output option for FileCheck
This patch introduces a --diff option to FileCheck to address the
readability issues highlighted in issue #77257. Traditional FileCheck
output can be difficult to parse, especially when dealing with
multiple substitutions or large input files with many mismatches
and additional context. This feature provides a more familiar,
scannable format for developers by rendering mismatches as diffs.
Fixes #77257
Assisted by Gemini AI
---
llvm/docs/CommandGuide/FileCheck.rst | 14 +
llvm/include/llvm/FileCheck/FileCheck.h | 10 +
llvm/lib/FileCheck/FileCheck.cpp | 454 ++++++++++++++++--
llvm/lib/FileCheck/FileCheckImpl.h | 11 +-
llvm/test/FileCheck/diff/diff-complex.txt | 13 +
.../test/FileCheck/diff/diff-fuzzy-lables.txt | 14 +
llvm/test/FileCheck/diff/diff-helloworld.txt | 8 +
.../FileCheck/diff/diff-label-boundary.txt | 12 +
llvm/test/FileCheck/diff/diff-labels.txt | 11 +
llvm/test/FileCheck/diff/diff-large.txt | 12 +
llvm/test/FileCheck/diff/diff-long-line.txt | 7 +
llvm/test/FileCheck/diff/diff-mid.txt | 13 +
.../test/FileCheck/diff/diff-missing-line.txt | 11 +
llvm/test/FileCheck/diff/diff-multi-subs.txt | 9 +
llvm/test/FileCheck/diff/diff-multihunk.txt | 13 +
llvm/test/FileCheck/diff/diff-nested-vars.txt | 10 +
llvm/test/FileCheck/diff/diff-next.txt | 11 +
llvm/test/FileCheck/diff/diff-noise.txt | 26 +
.../diff/diff-numeric-expression.txt | 10 +
llvm/test/FileCheck/diff/diff-overlap.txt | 15 +
llvm/test/FileCheck/diff/diff-resync.txt | 13 +
.../FileCheck/diff/diff-substitutions.txt | 16 +
.../FileCheck/diff/diff-variable-chain.txt | 11 +
llvm/utils/FileCheck/FileCheck.cpp | 16 +-
24 files changed, 705 insertions(+), 35 deletions(-)
create mode 100644 llvm/test/FileCheck/diff/diff-complex.txt
create mode 100644 llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
create mode 100644 llvm/test/FileCheck/diff/diff-helloworld.txt
create mode 100644 llvm/test/FileCheck/diff/diff-label-boundary.txt
create mode 100644 llvm/test/FileCheck/diff/diff-labels.txt
create mode 100644 llvm/test/FileCheck/diff/diff-large.txt
create mode 100644 llvm/test/FileCheck/diff/diff-long-line.txt
create mode 100644 llvm/test/FileCheck/diff/diff-mid.txt
create mode 100644 llvm/test/FileCheck/diff/diff-missing-line.txt
create mode 100644 llvm/test/FileCheck/diff/diff-multi-subs.txt
create mode 100644 llvm/test/FileCheck/diff/diff-multihunk.txt
create mode 100644 llvm/test/FileCheck/diff/diff-nested-vars.txt
create mode 100644 llvm/test/FileCheck/diff/diff-next.txt
create mode 100644 llvm/test/FileCheck/diff/diff-noise.txt
create mode 100644 llvm/test/FileCheck/diff/diff-numeric-expression.txt
create mode 100644 llvm/test/FileCheck/diff/diff-overlap.txt
create mode 100644 llvm/test/FileCheck/diff/diff-resync.txt
create mode 100644 llvm/test/FileCheck/diff/diff-substitutions.txt
create mode 100644 llvm/test/FileCheck/diff/diff-variable-chain.txt
diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index 22101d3ef2ac7..c02613e440ac2 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -110,6 +110,20 @@ and from the command line.
-verify``. With this option FileCheck will verify that input does not contain
warnings not covered by any ``CHECK:`` patterns.
+.. option:: --diff <value>
+
+ Controls how mismatches between the check patterns and the input are
+ reported. The default is ``standard``, which uses the standard FileCheck
+ diagnostic output.
+
+ * ``standard`` – Use the standard FileCheck diagnostic messages.
+ * ``split`` – Display mismatches using a side-by-side split diff view.
+ * ``unidiff`` – Display mismatches using a unified diff format.
+ * ``split-no-substitutions`` – Same as ``split`` but shows the raw
+ pattern without applying variable substitutions.
+ * ``unidiff-no-substitutions`` – Same as ``unidiff`` but shows the raw
+ pattern without applying variable substitutions.
+
.. option:: --dump-input <value>
Dump input to stderr, adding annotations representing currently enabled
diff --git a/llvm/include/llvm/FileCheck/FileCheck.h b/llvm/include/llvm/FileCheck/FileCheck.h
index b44ed8ed3f839..b8db1194d7340 100644
--- a/llvm/include/llvm/FileCheck/FileCheck.h
+++ b/llvm/include/llvm/FileCheck/FileCheck.h
@@ -27,6 +27,15 @@ class MemoryBuffer;
class SourceMgr;
template <typename T> class SmallVectorImpl;
+// Diff the output on failures.
+enum DiffFormatType {
+ Standard,
+ Split,
+ Unified,
+ SplitNoSubstitution,
+ UnifiedNoSubstitution
+};
+
/// Contains info about various FileCheck options.
struct FileCheckRequest {
std::vector<StringRef> CheckPrefixes;
@@ -43,6 +52,7 @@ struct FileCheckRequest {
bool AllowDeprecatedDagOverlap = false;
bool Verbose = false;
bool VerboseVerbose = false;
+ DiffFormatType DiffMode = DiffFormatType::Standard;
};
namespace Check {
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index d50e5d9cb088b..ea2a5684ddd3d 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/WithColor.h"
#include <cstdint>
#include <list>
#include <set>
@@ -1261,7 +1262,8 @@ unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
SMRange Range,
FileCheckDiag::MatchType MatchTy,
- std::vector<FileCheckDiag> *Diags) const {
+ std::vector<FileCheckDiag> *Diags,
+ const FileCheckRequest &Req) const {
// Print what we know about substitutions.
if (!Substitutions.empty()) {
for (const auto &Substitution : Substitutions) {
@@ -1287,8 +1289,10 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
if (Diags)
Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy,
SMRange(Range.Start, Range.Start), OS.str());
- else
- SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
+ else {
+ if (Req.DiffMode == DiffFormatType::Standard)
+ SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
+ }
}
}
}
@@ -1368,7 +1372,8 @@ static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
}
void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
- std::vector<FileCheckDiag> *Diags) const {
+ std::vector<FileCheckDiag> *Diags,
+ const FileCheckRequest &Req) const {
// Attempt to find the closest/best fuzzy match. Usually an error happens
// because some string in the output didn't exactly match. In these cases, we
// would like to show the user a best guess at what "should have" matched, to
@@ -1411,8 +1416,9 @@ void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
SMRange MatchRange =
ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
getCheckTy(), Buffer, Best, 0, Diags);
- SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
- "possible intended match here");
+ if (Req.DiffMode == DiffFormatType::Standard)
+ SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
+ "possible intended match here");
// FIXME: If we wanted to be really friendly we would show why the match
// failed, as it can be hard to spot simple one character differences.
@@ -2057,7 +2063,7 @@ static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
Buffer, MatchResult.TheMatch->Pos,
MatchResult.TheMatch->Len, Diags);
if (Diags) {
- Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
+ Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags, Req);
Pat.printVariableDefs(SM, MatchTy, Diags);
}
if (!PrintDiag) {
@@ -2078,7 +2084,7 @@ static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
{MatchRange});
// Print additional information, which can be useful even if there are errors.
- Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);
+ Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr, Req);
Pat.printVariableDefs(SM, MatchTy, nullptr);
// Print errors and add them to Diags. We report these errors after the match
@@ -2102,7 +2108,7 @@ static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
StringRef Prefix, SMLoc Loc, const Pattern &Pat,
int MatchedCount, StringRef Buffer, Error MatchError,
- bool VerboseVerbose,
+ bool VerboseVerbose, const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) {
// Print any pattern errors, and record them to be added to Diags later.
bool HasError = ExpectedMatch;
@@ -2148,7 +2154,7 @@ static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
for (StringRef ErrorMsg : ErrorMsgs)
Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange,
ErrorMsg);
- Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags);
+ Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags, Req);
}
if (!PrintDiag) {
assert(!HasError && "expected to report more diagnostics for error");
@@ -2165,18 +2171,20 @@ static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
if (Pat.getCount() > 1)
Message +=
formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
- SM.PrintMessage(Loc,
- ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
- Message);
- SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note,
- "scanning from here");
+ if (Req.DiffMode == DiffFormatType::Standard) {
+ SM.PrintMessage(
+ Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
+ Message);
+ SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note,
+ "scanning from here");
+ }
}
// Print additional information, which can be useful even after a pattern
// error.
- Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr);
+ Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr, Req);
if (ExpectedMatch)
- Pat.printFuzzyMatch(SM, Buffer, Diags);
+ Pat.printFuzzyMatch(SM, Buffer, Diags, Req);
return ErrorReported::reportedOrSuccess(HasError);
}
@@ -2192,7 +2200,7 @@ static Error reportMatchResult(bool ExpectedMatch, const SourceMgr &SM,
return printMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
std::move(MatchResult), Req, Diags);
return printNoMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
- std::move(MatchResult.TheError), Req.VerboseVerbose,
+ std::move(MatchResult.TheError), Req.VerboseVerbose, Req,
Diags);
}
@@ -2275,7 +2283,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
// If this check is a "CHECK-NEXT", verify that the previous match was on
// the previous line (i.e. that there is one newline between them).
- if (CheckNext(SM, SkippedRegion)) {
+ if (CheckNext(SM, SkippedRegion, Req)) {
ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
Diags, Req.Verbose);
@@ -2300,7 +2308,8 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
return FirstMatchPos;
}
-bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
+bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer,
+ const FileCheckRequest &Req) const {
if (Pat.getCheckTy() != Check::CheckNext &&
Pat.getCheckTy() != Check::CheckEmpty)
return false;
@@ -2312,8 +2321,7 @@ bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
// Count the number of newlines between the previous match and this one.
const char *FirstNewLine = nullptr;
unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
-
- if (NumNewLines == 0) {
+ if (NumNewLines == 0 && Req.DiffMode == DiffFormatType::Standard) {
SM.PrintMessage(Loc, SourceMgr::DK_Error,
CheckName + ": is on the same line as previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
@@ -2323,7 +2331,7 @@ bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
return true;
}
- if (NumNewLines != 1) {
+ if (NumNewLines != 1 && Req.DiffMode == DiffFormatType::Standard) {
SM.PrintMessage(Loc, SourceMgr::DK_Error,
CheckName +
": is not on the line after the previous match");
@@ -2724,9 +2732,361 @@ void FileCheckPatternContext::clearLocalVars() {
GlobalNumericVariableTable.erase(Var);
}
+/// Extract a fixed-width "window" from string \p S, centered around \p DiffPos.
+///
+/// This is used for side-by-side diffing of long lines (e.g., metadata
+/// or long symbol names). It ensures that the "point of divergence" is visible
+/// within the provided \p Width. If the string is truncated, ellipses (...)
+/// are inserted at the boundaries.
+static std::string getCenteredView(StringRef S, size_t DiffPos, size_t Width) {
+ if (S.size() <= Width)
+ return S.str() + std::string(Width - S.size(), ' ');
+
+ size_t HalfWidth = Width / 2;
+ size_t Start = (DiffPos > HalfWidth) ? DiffPos - HalfWidth : 0;
+
+ if (Start + Width > S.size())
+ Start = (S.size() > Width) ? S.size() - Width : 0;
+
+ std::string View = S.substr(Start, Width).str();
+
+ if (Start > 0 && Width > 3)
+ View.replace(0, 3, "...");
+
+ if (S.size() > Start + Width && View.size() > 3)
+ View.replace(View.size() - 3, 3, "...");
+
+ return View;
+}
+
+struct DiffContext {
+ StringRef Line;
+ StringRef LineBefore;
+ StringRef LineAfter;
+};
+
+/// Populates a \c DiffContext by fetching the line at \p LineNo
+/// as well as the lines before and after it from the \p SourceMgr. This
+/// provides the "surrounding context" seen in standard diff tools.
+static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
+ unsigned BufID) {
+ const MemoryBuffer *Buffer = SM.getMemoryBuffer(BufID);
+ StringRef BufText = Buffer->getBuffer();
+
+ /// Helper lambda to safely extract a single line's text from the buffer.
+ auto getLineText = [&](unsigned L) -> StringRef {
+ if (L == 0)
+ return "";
+
+ SMLoc LineLoc = SM.FindLocForLineAndColumn(BufID, L, 1);
+ if (!LineLoc.isValid())
+ return "";
+
+ const char *Ptr = LineLoc.getPointer();
+ const char *End = BufText.end();
+ const char *LineEnd = Ptr;
+
+ while (LineEnd < End && *LineEnd != '\n' && *LineEnd != '\r')
+ LineEnd++;
+
+ return StringRef(Ptr, LineEnd - Ptr).trim();
+ };
+
+ return {getLineText(LineNo), getLineText(LineNo - 1),
+ getLineText(LineNo + 1)};
+}
+
+/// Converts a raw pattern string (e.g., "val [[VAL]]") into a
+/// human-readable diagnostic string (e.g., "val 42") by replacing
+/// "[[...]]" tags with the values stored in the \c Substitutions vector.
+std::string Pattern::getSubstitutedRegex(StringRef PatternText) const {
+ std::string Result = PatternText.str();
+
+ // We iterate through substitutions. Since they are stored in the order
+ // they appear in the pattern, we can replace tags from left to right.
+ for (const auto &Substitution : Substitutions) {
+ Expected<std::string> ValueOrErr = Substitution->getResultForDiagnostics();
+ std::string CleanValue;
+
+ if (!ValueOrErr) {
+ consumeError(ValueOrErr.takeError());
+ CleanValue = "<UNDEFINED>";
+ } else {
+ CleanValue = *ValueOrErr;
+ // Numeric substitutions arrive wrapped in quotes so we need to strip
+ // them.
+ if (CleanValue.size() >= 2 && CleanValue.front() == '"' &&
+ CleanValue.back() == '"')
+ CleanValue = CleanValue.substr(1, CleanValue.size() - 2);
+ }
+
+ // Find the first occurrence of a variable tag
+ size_t Start = Result.find("[[");
+ if (Start == std::string::npos)
+ break;
+
+ size_t End = Result.find("]]", Start);
+ if (End == std::string::npos)
+ break;
+
+ size_t TagLen = (End + 2) - Start;
+ Result.replace(Start, TagLen, CleanValue);
+ }
+ return Result;
+}
+
+/// Renders a diagnostic diff to \c llvm::errs().
+///
+/// Supports two modes:
+/// 1. Split View: A side-by-side comparison (Expected | Actual) using a
+/// sliding window centered on the first difference.
+/// 2. Unified View: A standard top-to-bottom (-Expected / +Actual) format.
+static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
+ unsigned ActualLineNo, StringRef ActualLine,
+ const std::string &ExpectedText, const DiffContext &Ctx,
+ unsigned OverwriteActualLine) {
+ auto &OS = llvm::errs();
+
+ constexpr unsigned ColWidth = 45;
+ constexpr StringRef Sep = " | ";
+
+ bool IsSplit = (Mode == Split || Mode == SplitNoSubstitution);
+
+ // Identify the first index where the expected and actual text diverge.
+ size_t DiffPos = 0;
+ size_t MinLen = std::min(ExpectedText.size(), ActualLine.size());
+ while (DiffPos < MinLen && ExpectedText[DiffPos] == ActualLine[DiffPos])
+ ++DiffPos;
+
+ auto GetView = [&](StringRef S) {
+ return getCenteredView(S, DiffPos, ColWidth - 1);
+ };
+
+ // Header
+ OS.changeColor(raw_ostream::CYAN);
+ if (IsSplit)
+ OS << "@@ L:" << ExpectedLineNo << " R:" << ActualLineNo << " @@\n";
+ else
+ OS << "@@ -" << ExpectedLineNo << " +" << ActualLineNo << " @@\n";
+ OS.resetColor();
+
+ // Before Context
+ if (!Ctx.LineBefore.empty()) {
+ if (IsSplit)
+ OS << " " << GetView(Ctx.LineBefore) << Sep << GetView(Ctx.LineBefore)
+ << "\n";
+ else
+ OS << " " << Ctx.LineBefore << "\n";
+ }
+
+ // Mismatch
+ if (IsSplit) {
+ OS << " ";
+
+ OS.changeColor(raw_ostream::RED);
+ OS << GetView(ExpectedText);
+ OS.resetColor();
+
+ bool IsWrongLine = (OverwriteActualLine != 0);
+
+ if (IsWrongLine) {
+ // Use a bold yellow '!' to signify that the text might match,
+ // but it was found on the wrong line.
+ OS.changeColor(raw_ostream::YELLOW, true);
+ OS << " ! ";
+ OS.resetColor();
+ } else {
+ OS << " | ";
+ }
+
+ OS.changeColor(raw_ostream::GREEN);
+ OS << GetView(ActualLine) << "\n";
+ OS.resetColor();
+
+ } else {
+ OS.changeColor(raw_ostream::RED);
+ OS << "-" << ExpectedText << "\n";
+
+ OS.changeColor(raw_ostream::GREEN);
+ OS << "+" << ActualLine << "\n";
+ OS.resetColor();
+ }
+
+ // After Context
+ if (!Ctx.LineAfter.empty()) {
+ if (IsSplit)
+ OS << " " << GetView(Ctx.LineAfter) << Sep << GetView(Ctx.LineAfter)
+ << "\n";
+ else
+ OS << " " << Ctx.LineAfter << "\n";
+ }
+}
+
+/// Prepares and prints a visual comparison between a CHECK pattern and the
+/// input.
+///
+/// This function acts as a bridge between the FileCheck engine and the diff
+/// renderer. It resolves the line numbers for both the pattern (Expected) and
+/// the input (Actual), performs variable substitution if requested, and fetches
+/// the surrounding context lines.
+static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
+ StringRef CheckRegion, SourceMgr &SM,
+ const FileCheckRequest &Req,
+ std::vector<FileCheckDiag> *Diags,
+ unsigned OverwriteActualLine = 0) {
+ StringRef ActualLine = CheckRegion.split('\n').first;
+ if (CheckRegion.empty()) {
+ ActualLine = "<EOF>";
+ }
+
+ SMLoc PatternLoc = CheckStr.Pat.getLoc();
+ unsigned ExpectedLineNo = SM.getLineAndColumn(PatternLoc).first;
+ const char *PatPtr = PatternLoc.getPointer();
+ StringRef FullExpectedLine = StringRef(PatPtr).split('\n').first.trim();
+
+ std::string ExpectedText;
+ if (Mode == DiffFormatType::SplitNoSubstitution ||
+ Mode == DiffFormatType::UnifiedNoSubstitution)
+ ExpectedText = FullExpectedLine.str(); // Raw pattern: [[VAR]]
+ else
+ ExpectedText = CheckStr.Pat.getSubstitutedRegex(
+ FullExpectedLine); // Substituted: value
+
+ // Resolve the Actual (Input) line number.
+ // Priority: 1. OverwriteActualLine (explicit override)
+ // 2. Fuzzy Match Diag (where FileCheck 'thinks' the line was)
+ // 3. Direct pointer resolution via SourceMgr.
+ SMLoc InputLoc = SMLoc::getFromPointer(CheckRegion.data());
+ unsigned ActualLineNo = OverwriteActualLine;
+
+ if (ActualLineNo == 0) {
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+ bool FoundFuzzy = false;
+
+ // Search backward to find most recent fuzzy match for this pattern.
+ if (Diags) {
+ for (const auto &D : llvm::reverse(*Diags)) {
+ if (D.CheckLoc == PatternLoc &&
+ D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ ActualLineNo = D.InputStartLine;
+ FoundFuzzy = true;
+ break;
+ }
+ }
+ }
+ // If no diagnostic match was found, calculate the line number directly
+ // from the InputLoc pointer using the SourceManager.
+ if (!FoundFuzzy)
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+
+ // if we are at an empty line, usually the relevant context is the line just
+ // before it.
+ if (ActualLine.empty() && ActualLineNo > 1)
+ ActualLineNo--;
+ }
+
+ // Gather contextual diff to print (a line above and a line below).
+ unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
+ DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
+
+ renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
+ Context, OverwriteActualLine);
+
+ llvm::errs() << "\n";
+ return true;
+}
+
+/// Handles a mismatch by attempting to resynchronize the pattern with the
+/// input.
+///
+/// When a pattern fails to match at the current location, this function
+/// explores several recovery strategies:
+/// 1. Stray Line Detection: Check if the pattern matches perfectly on the very
+/// next line (suggesting an unexpected line was inserted).
+/// 2. Search/Resync: Search forward in the region to find a perfect match
+/// later on (suggesting a block of unexpected code).
+/// 3. Fuzzy Matching: Use existing diagnostics to find "near misses" (typos)
+/// on the current or nearby lines.
+///
+/// It then calls \c renderDiff via \c printDiff and advances \p CheckRegion
+/// to the appropriate recovery point
+static bool handleDiffFailure(const FileCheckString &CheckStr,
+ StringRef &CheckRegion, SourceMgr &SM,
+ FileCheckRequest &Req,
+ std::vector<FileCheckDiag> *Diags,
+ raw_ostream &OS, bool &HeaderPrinted,
+ unsigned &TotalMismatches) {
+ CheckRegion = CheckRegion.ltrim("\n\r");
+ if (CheckRegion.empty())
+ return false;
+
+ SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
+ unsigned CurrentLineNo = SM.getLineAndColumn(CurrentLoc).first;
+ size_t EOL = CheckRegion.find('\n');
+ StringRef MismatchLine =
+ (EOL == StringRef::npos) ? CheckRegion : CheckRegion.substr(0, EOL);
+
+ // Try to find where this pattern actually appears next
+ std::vector<FileCheckDiag> ResyncDiags;
+ size_t ResyncMatchLen = 0;
+ size_t ResyncPos = CheckStr.Check(SM, CheckRegion, /*Search=*/true,
+ ResyncMatchLen, Req, &ResyncDiags);
+
+ StringRef TargetLine = MismatchLine;
+ unsigned TargetLineNo = CurrentLineNo;
+ bool AdvanceToResync = false;
+
+ if (ResyncPos == 0) {
+ // Perfect match right here (usually triggered by CHECK-NEXT noise)
+ TargetLine = MismatchLine;
+ } else if (ResyncPos != StringRef::npos) {
+ // Found it further down. Is it just one line away? (Stray line case)
+ size_t NextLinePos = CheckRegion.find('\n');
+ if (NextLinePos != StringRef::npos && ResyncPos == NextLinePos + 1) {
+ // This is a stray line. Diff the expected vs the stray line, then skip
+ // stray.
+ TargetLine = MismatchLine;
+ AdvanceToResync = false;
+ } else if (CheckStr.Pat.getCheckTy() != llvm::Check::CheckNext) {
+ // Regular CHECK: skip the "noise" and sync to the match
+ SMLoc MatchLoc = SMLoc::getFromPointer(CheckRegion.data() + ResyncPos);
+ TargetLineNo = SM.getLineAndColumn(MatchLoc).first;
+ TargetLine = CheckRegion.substr(ResyncPos).split('\n').first;
+ AdvanceToResync = true;
+ }
+ } else {
+ // Fallback to Fuzzy matching if no perfect match exists
+ for (const auto &D : llvm::reverse(ResyncDiags)) {
+ if (D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ TargetLineNo = D.InputStartLine;
+ SMLoc FuzzyLoc = SM.FindLocForLineAndColumn(
+ SM.FindBufferContainingLoc(CurrentLoc), TargetLineNo, 1);
+ TargetLine = StringRef(FuzzyLoc.getPointer()).split('\n').first.trim();
+ break;
+ }
+ }
+ }
+
+ printDiff(Req.DiffMode, CheckStr, TargetLine, SM, Req, &ResyncDiags,
+ TargetLineNo);
+ TotalMismatches++;
+
+ // Updates \p CheckRegion to advance the search pointer past the error.
+ if (AdvanceToResync && ResyncPos != StringRef::npos)
+ CheckRegion = CheckRegion.substr(ResyncPos + ResyncMatchLen);
+ else if (EOL != StringRef::npos)
+ CheckRegion = CheckRegion.substr(EOL + 1);
+ else
+ CheckRegion = "";
+
+ return true;
+}
+
bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
std::vector<FileCheckDiag> *Diags) {
bool ChecksFailed = false;
+ unsigned TotalMismatches = 0;
+ auto &OS = llvm::errs();
unsigned i = 0, j = 0, e = CheckStrings.size();
while (true) {
@@ -2759,21 +3119,55 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
if (i != 0 && Req.EnableVarScope)
PatternContext->clearLocalVars();
+ bool HeaderPrinted = false;
for (; i != j; ++i) {
const FileCheckString &CheckStr = CheckStrings[i];
// Check each string within the scanned region, including a second check
// of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
size_t MatchLen = 0;
- size_t MatchPos =
- CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
-
- if (MatchPos == StringRef::npos) {
- ChecksFailed = true;
- i = j;
- break;
+ size_t MatchPos = StringRef::npos;
+ // Determine if the pattern requires strict adjacency (CHECK-NEXT/EMPTY).
+ bool IsStrict = CheckStr.Pat.getCheckTy() == Check::CheckNext ||
+ CheckStr.Pat.getCheckTy() == Check::CheckEmpty;
+
+ if (Req.DiffMode != DiffFormatType::Standard) {
+ // Try to match the pattern (Search only if not strict)
+ MatchPos =
+ CheckStr.Check(SM, CheckRegion, !IsStrict, MatchLen, Req, Diags);
+ } else {
+ MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
}
+ if (Req.DiffMode != DiffFormatType::Standard) {
+ if (MatchPos == StringRef::npos) {
+ // Case 1: No match at all. Always diff.
+ handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
+ HeaderPrinted, TotalMismatches);
+ ChecksFailed = true;
+ i = j;
+ break;
+ } else if (IsStrict && MatchPos > 0) {
+ // Case 2: Match found, but is it "Strictly" next?
+ // Check if the skipped characters are JUST whitespace/newlines.
+ StringRef Skipped = CheckRegion.slice(0, MatchPos);
+ if (!Skipped.trim().empty()) {
+ // There is actual text (noise) between the last match and this one.
+ handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
+ HeaderPrinted, TotalMismatches);
+ ChecksFailed = true;
+ i = j;
+ break;
+ }
+ }
+ } else {
+ // Standard Mode Logic
+ if (MatchPos == StringRef::npos) {
+ ChecksFailed = true;
+ i = j;
+ break;
+ }
+ }
CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
}
diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h
index 5851cfc4b5d5c..5b47b87076638 100644
--- a/llvm/lib/FileCheck/FileCheckImpl.h
+++ b/llvm/lib/FileCheck/FileCheckImpl.h
@@ -734,9 +734,13 @@ class Pattern {
/// Prints the value of successful substitutions.
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
SMRange MatchRange, FileCheckDiag::MatchType MatchTy,
- std::vector<FileCheckDiag> *Diags) const;
+ std::vector<FileCheckDiag> *Diags,
+ const FileCheckRequest &Req) const;
void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
- std::vector<FileCheckDiag> *Diags) const;
+ std::vector<FileCheckDiag> *Diags,
+ const FileCheckRequest &Req) const;
+
+ std::string getSubstitutedRegex(StringRef PatternText) const;
bool hasVariable() const {
return !(Substitutions.empty() && VariableDefs.empty());
@@ -874,7 +878,8 @@ struct FileCheckString {
/// Verifies that there is a single line in the given \p Buffer. Errors are
/// reported against \p SM.
- bool CheckNext(const SourceMgr &SM, StringRef Buffer) const;
+ bool CheckNext(const SourceMgr &SM, StringRef Buffer,
+ const FileCheckRequest &Req) const;
/// Verifies that there is no newline in the given \p Buffer. Errors are
/// reported against \p SM.
bool CheckSame(const SourceMgr &SM, StringRef Buffer) const;
diff --git a/llvm/test/FileCheck/diff/diff-complex.txt b/llvm/test/FileCheck/diff/diff-complex.txt
new file mode 100644
index 0000000000000..e4df50dbdf936
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-complex.txt
@@ -0,0 +1,13 @@
+; RUN: printf "HEADER\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET_BLOCK\nStray1\nStray2\nMATCH_ME\nNEXT_LINE\nNoise5\nNoise6\n" > %t.input
+; RUN: not FileCheck --diff=unidiff --input-file %t.input %s --check-prefix=COMPLEX 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; COMPLEX-LABEL: HEADER
+; COMPLEX: TARGET_BLOCK
+; COMPLEX: MATCH_ME
+; COMPLEX-NEXT: WRONG_NEXT
+; COMPLEX: END_OF_FUNCTION
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: MATCH_ME
+; DIFF-NEXT: -WRONG_NEXT
+; DIFF-NEXT: +NEXT_LINE
diff --git a/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt b/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
new file mode 100644
index 0000000000000..2ba3dae29c3a6
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
@@ -0,0 +1,14 @@
+; RUN: printf "define void @foo() {\n call void @work_task()\n}\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK-LABEL: define void @foo()
+; CHECK: call void @work_test()
+
+; Need to fixed this
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF: define void @foo() {
+; DIFF-NEXT: -call void @work_test()
+; DIFF-NEXT: +call void @work_task()
+; DIFF-NEXT: }
+
diff --git a/llvm/test/FileCheck/diff/diff-helloworld.txt b/llvm/test/FileCheck/diff/diff-helloworld.txt
new file mode 100644
index 0000000000000..074c15bdcfe86
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-helloworld.txt
@@ -0,0 +1,8 @@
+; RUN: printf "hello world\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck -check-prefix=UNI-DIFF %s
+
+; CHECK: hello {{U}}niverse
+
+; UNI-DIFF: @@ -4 +1 @@
+; UNI-DIFF-NEXT: -hello {{[{][{]U[}][}]}}niverse
+; UNI-DIFF-NEXT: +hello world
diff --git a/llvm/test/FileCheck/diff/diff-label-boundary.txt b/llvm/test/FileCheck/diff/diff-label-boundary.txt
new file mode 100644
index 0000000000000..2d3cc114f254e
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-label-boundary.txt
@@ -0,0 +1,12 @@
+; RUN: printf "FUNCTION_A\n BAD_OP\nFUNCTION_B\n GOOD_OP\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK-LABEL: FUNCTION_A
+; CHECK: CORRECT_OP
+; CHECK-LABEL: FUNCTION_B
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: FUNCTION_A
+; DIFF-NEXT: -CORRECT_OP
+; DIFF-NEXT: +BAD_OP
+; DIFF-NEXT: FUNCTION_B
diff --git a/llvm/test/FileCheck/diff/diff-labels.txt b/llvm/test/FileCheck/diff/diff-labels.txt
new file mode 100644
index 0000000000000..0ef08ce6f5f63
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-labels.txt
@@ -0,0 +1,11 @@
+; RUN: printf "FUNC_A\n mov r0, #1\nFUNC_B\n mov r1, #2\n" > %t.labels.input
+; RUN: not FileCheck --diff=unidiff --input-file %t.labels.input %s --check-prefix=LABELS 2>&1 | FileCheck %s -check-prefix=DIFF_LABELS
+
+; LABELS-LABEL: FUNC_A
+; LABELS-NEXT: mov r0, #5
+; LABELS-LABEL: FUNC_B
+
+; DIFF_LABELS: @@ -[[#]] +[[#]] @@
+; DIFF_LABELS-NEXT: FUNC_A
+; DIFF_LABELS-NEXT: -mov r0, #5
+; DIFF_LABELS-NEXT: +mov r0, #1
diff --git a/llvm/test/FileCheck/diff/diff-large.txt b/llvm/test/FileCheck/diff/diff-large.txt
new file mode 100644
index 0000000000000..9bb50aec00850
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-large.txt
@@ -0,0 +1,12 @@
+; RUN: python3 -c "for i in range(5000): print(f'DATA_{i}')" > %t.input
+; RUN: echo "CHECK: DATA_0" > %t.check
+; RUN: echo "CHECK: DATA_2500_WRONG" >> %t.check
+; RUN: echo "CHECK: DATA_4999" >> %t.check
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %t.check 2>&1 | FileCheck %s --check-prefix=DIFF
+
+; DIFF: @@ -2 +21 @@
+; DIFF-NEXT: DATA_19
+; DIFF-NEXT: -DATA_2500_WRONG
+; DIFF-NEXT: +DATA_20
+; DIFF-NEXT: DATA_21
+
diff --git a/llvm/test/FileCheck/diff/diff-long-line.txt b/llvm/test/FileCheck/diff/diff-long-line.txt
new file mode 100644
index 0000000000000..3d771c34c4e2c
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-long-line.txt
@@ -0,0 +1,7 @@
+; RUN: printf "THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_ACTUAL_VALUE\n" > %t.input
+; RUN: not FileCheck --diff=split --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_EXPECTED_VALUE
+
+; DIFF: @@ L:[[#]] R:[[#]] @@
+; DIFF-NEXT: {{.*}}...{{.*}}WINDOW_LOGIC_EXPECTED_VALUE{{.*}}!{{.*}}...{{.*}}WINDOW_LOGIC_ACTUAL_VALUE
diff --git a/llvm/test/FileCheck/diff/diff-mid.txt b/llvm/test/FileCheck/diff/diff-mid.txt
new file mode 100644
index 0000000000000..d83438b80ecdf
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-mid.txt
@@ -0,0 +1,13 @@
+; RUN: printf "START\nWRONG\nEND\n" > %t.mid.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.mid.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: START
+; CHECK: MIDDLE
+; CHECK: END
+
+; We should fix this.
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: WRONG
+; DIFF-NEXT: -MIDDLE
+; DIFF-NEXT: +END
diff --git a/llvm/test/FileCheck/diff/diff-missing-line.txt b/llvm/test/FileCheck/diff/diff-missing-line.txt
new file mode 100644
index 0000000000000..a7b79a4a055c3
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-missing-line.txt
@@ -0,0 +1,11 @@
+; RUN: printf "ALPHA\nGAMMA\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: ALPHA
+; CHECK: BETA
+; CHECK: GAMMA
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: ALPHA
+; DIFF-NEXT: -BETA
+; DIFF-NEXT: +GAMMA
diff --git a/llvm/test/FileCheck/diff/diff-multi-subs.txt b/llvm/test/FileCheck/diff/diff-multi-subs.txt
new file mode 100644
index 0000000000000..8f37bb90a99b3
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-multi-subs.txt
@@ -0,0 +1,9 @@
+; RUN: printf "SET REG=rax VAL=0\nUSE rax, 1\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=split --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: SET REG=[[REG:[a-z]+]] VAL=[[VAL:[0-9]+]]
+; CHECK: USE [[REG]], [[VAL]]
+
+; DIFF: @@ L:[[#]] R:[[#]] @@
+; DIFF-NEXT: {{.*}}SET REG=rax VAL=0{{.*}}| SET REG=rax VAL=0{{.*}}
+; DIFF-NEXT: {{.*}}USE rax, 0{{.*}}!{{.*}}USE rax, 1{{.*}}
diff --git a/llvm/test/FileCheck/diff/diff-multihunk.txt b/llvm/test/FileCheck/diff/diff-multihunk.txt
new file mode 100644
index 0000000000000..55f4933d6c211
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-multihunk.txt
@@ -0,0 +1,13 @@
+; RUN: printf "BLOCK1_FAIL\nKEEP_THIS\nBLOCK2_FAIL\n" > %t.multi.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.multi.input %s 2>&1 | FileCheck %s -check-prefix=UNI-DIFF
+;
+; --- TEST DATA ---
+; CHECK: BLOCK1_PASS
+; CHECK: KEEP_THIS
+; CHECK: BLOCK2_PASS
+
+; --- VERIFY ---
+; UNI-DIFF: @@ -[[#]] +1 @@
+; UNI-DIFF-NEXT: -BLOCK1_PASS
+; UNI-DIFF-NEXT: +BLOCK1_FAIL
+; UNI-DIFF-NEXT: KEEP_THIS
diff --git a/llvm/test/FileCheck/diff/diff-nested-vars.txt b/llvm/test/FileCheck/diff/diff-nested-vars.txt
new file mode 100644
index 0000000000000..406d029bb9931
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-nested-vars.txt
@@ -0,0 +1,10 @@
+; RUN: printf "SET [[42]]\nDATA: [[42]]\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: SET [[INNER:.*]]
+; CHECK: DATA: WRONG_VALUE
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: SET {{\[\[}}42{{\]\]}}
+; DIFF-NEXT: -DATA: WRONG_VALUE
+; DIFF-NEXT: +DATA: {{\[\[}}42{{\]\]}}
diff --git a/llvm/test/FileCheck/diff/diff-next.txt b/llvm/test/FileCheck/diff/diff-next.txt
new file mode 100644
index 0000000000000..a6022690b4404
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-next.txt
@@ -0,0 +1,11 @@
+; RUN: printf "LINE1\nSTRAY\nLINE2\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: LINE1
+; CHECK-NEXT: LINE2
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: LINE1
+; DIFF-NEXT: -LINE2
+; DIFF-NEXT: +STRAY
+; DIFF-NEXT: LINE2
diff --git a/llvm/test/FileCheck/diff/diff-noise.txt b/llvm/test/FileCheck/diff/diff-noise.txt
new file mode 100644
index 0000000000000..4c1b8eacab98d
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-noise.txt
@@ -0,0 +1,26 @@
+; --- Test 1: High Noise Ratio ---
+; RUN: printf "START\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET\nEND\n" > %t.noise.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.noise.input %s --check-prefix=TEST1 2>&1 | FileCheck %s -check-prefix=DIFF1
+
+; TEST1: START
+; TEST1-NEXT: TARGET
+; TEST1: END
+
+; DIFF1: @@ -[[#]] +[[#]] @@
+; DIFF1-NEXT: START
+; DIFF1-NEXT: -TARGET
+; DIFF1-NEXT: +Noise1
+; DIFF1-NEXT: Noise2
+
+; --- Test 2: CHECK-NEXT stray line ---
+; RUN: printf "HEADER\nSTRAY\nNEXT\n" > %t.next.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.next.input %s --check-prefix=TEST2 2>&1 | FileCheck %s -check-prefix=DIFF2
+
+; TEST2-LABEL: HEADER
+; TEST2-NEXT: NEXT
+
+; DIFF2: @@ -[[#]] +[[#]] @@
+; DIFF2-NEXT: HEADER
+; DIFF2-NEXT: -NEXT
+; DIFF2-NEXT: +STRAY
+; DIFF2-NEXT: NEXT
diff --git a/llvm/test/FileCheck/diff/diff-numeric-expression.txt b/llvm/test/FileCheck/diff/diff-numeric-expression.txt
new file mode 100644
index 0000000000000..d8a81b4e37aa6
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-numeric-expression.txt
@@ -0,0 +1,10 @@
+; RUN: printf "BASE: 10\nNEXT: 12\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: BASE: [[#VAL:]]
+; CHECK: NEXT: [[#VAL+1]]
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: BASE: 10
+; DIFF-NEXT: -NEXT: 11
+; DIFF-NEXT: +NEXT: 12
diff --git a/llvm/test/FileCheck/diff/diff-overlap.txt b/llvm/test/FileCheck/diff/diff-overlap.txt
new file mode 100644
index 0000000000000..49733a0c74434
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-overlap.txt
@@ -0,0 +1,15 @@
+; RUN: printf "LINE1\nBAD_LINE\nLINE3\nLINE4\nBAD_LINE\nLINE6\n" > %t.overlap.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.overlap.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: LINE1
+; CHECK: LINE2
+; CHECK: LINE3
+; CHECK: LINE4
+; CHECK: LINE5
+; CHECK: LINE6
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: LINE1
+; DIFF-NEXT: -LINE2
+; DIFF-NEXT: +BAD_LINE
+; DIFF-NEXT: LINE3
diff --git a/llvm/test/FileCheck/diff/diff-resync.txt b/llvm/test/FileCheck/diff/diff-resync.txt
new file mode 100644
index 0000000000000..e4df50dbdf936
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-resync.txt
@@ -0,0 +1,13 @@
+; RUN: printf "HEADER\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET_BLOCK\nStray1\nStray2\nMATCH_ME\nNEXT_LINE\nNoise5\nNoise6\n" > %t.input
+; RUN: not FileCheck --diff=unidiff --input-file %t.input %s --check-prefix=COMPLEX 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; COMPLEX-LABEL: HEADER
+; COMPLEX: TARGET_BLOCK
+; COMPLEX: MATCH_ME
+; COMPLEX-NEXT: WRONG_NEXT
+; COMPLEX: END_OF_FUNCTION
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: MATCH_ME
+; DIFF-NEXT: -WRONG_NEXT
+; DIFF-NEXT: +NEXT_LINE
diff --git a/llvm/test/FileCheck/diff/diff-substitutions.txt b/llvm/test/FileCheck/diff/diff-substitutions.txt
new file mode 100644
index 0000000000000..085780759d874
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-substitutions.txt
@@ -0,0 +1,16 @@
+; RUN: printf "START 42\nFAIL 100\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck -check-prefix=SUBST %s
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff-no-substitutions --input-file %t.input %s 2>&1 | FileCheck -check-prefix=RAW %s
+
+; CHECK: START [[VAL:[0-9]+]]
+; CHECK: FAIL [[VAL]]
+
+; SUBST: @@ -[[#]] +2 @@
+; SUBST-NEXT: START 42
+; SUBST-NEXT: -FAIL 42
+; SUBST-NEXT: +FAIL 100
+
+; RAW: @@ -[[#]] +2 @@
+; RAW-NEXT: START 42
+; RAW-NEXT: -FAIL {{\[\[VAL\]\]}}
+; RAW-NEXT: +FAIL 100
diff --git a/llvm/test/FileCheck/diff/diff-variable-chain.txt b/llvm/test/FileCheck/diff/diff-variable-chain.txt
new file mode 100644
index 0000000000000..e370046c18daa
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-variable-chain.txt
@@ -0,0 +1,11 @@
+; RUN: printf "ID: 0x100\nTYPE: apple\nUSE: 0x100 (orange)\n" > %t.input
+; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+
+; CHECK: ID: [[ID:0x[0-9a-f]+]]
+; CHECK: TYPE: [[TYPE:[a-z]+]]
+; CHECK: USE: [[ID]] ([[TYPE]])
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: TYPE: apple
+; DIFF-NEXT: -USE: 0x100 (apple)
+; DIFF-NEXT: +USE: 0x100 (orange)
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 12fdbfd45279d..b1433257a8661 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -113,6 +113,16 @@ static cl::opt<bool> VerboseVerbose(
"issues, or add it to the input dump if enabled. Implies\n"
"-v.\n"));
+static cl::opt<DiffFormatType> DiffMode(
+ "diff", cl::desc("Show mismatches using a diff-style format.\n"),
+ cl::values(clEnumValN(Standard, "standard", "Outputs a Standard diff"),
+ clEnumValN(Split, "split", "Outputs a Split diff"),
+ clEnumValN(Unified, "unidiff", "Outputs a Unified diff"),
+ clEnumValN(SplitNoSubstitution, "split-no-substitutions",
+ "Outputs a Split diff with no substitutions"),
+ clEnumValN(UnifiedNoSubstitution, "unidiff-no-substitutions",
+ "Outputs a Unified diff with no substitutions")));
+
// The order of DumpInputValue members affects their precedence, as documented
// for -dump-input below.
enum DumpInputValue {
@@ -787,6 +797,7 @@ int main(int argc, char **argv) {
if (GlobalDefineError)
return 2;
+ Req.DiffMode = DiffMode;
Req.AllowEmptyInput = AllowEmptyInput;
Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
Req.EnableVarScope = EnableVarScope;
@@ -858,8 +869,9 @@ int main(int argc, char **argv) {
DumpInput == DumpInputNever ? nullptr : &Diags)
? EXIT_SUCCESS
: 1;
- if (DumpInput == DumpInputAlways ||
- (ExitCode == 1 && DumpInput == DumpInputFail)) {
+ if ((DumpInput == DumpInputAlways ||
+ (ExitCode == 1 && DumpInput == DumpInputFail)) &&
+ Req.DiffMode == DiffFormatType::Standard) {
errs() << "\n"
<< "Input file: " << InputFilename << "\n"
<< "Check file: " << CheckFilename << "\n"
>From 18f6d47d96831e47f286c67360b44bfd9925881c Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Thu, 26 Mar 2026 04:23:28 +0530
Subject: [PATCH 02/14] add mismatched summary
---
llvm/lib/FileCheck/FileCheck.cpp | 6 ++++++
llvm/test/FileCheck/diff/diff-fuzzy-lables.txt | 1 -
llvm/test/FileCheck/diff/diff-large.txt | 1 -
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index ea2a5684ddd3d..96d84f4910c77 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -3175,6 +3175,12 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
break;
}
+ if (Req.DiffMode != DiffFormatType::Standard && TotalMismatches > 0) {
+ OS.changeColor(llvm::raw_ostream::YELLOW, true);
+ OS << "FileCheck: Found " << TotalMismatches << " unique textual mismatch"
+ << (TotalMismatches > 1 ? "es." : ".") << "\n";
+ OS.resetColor();
+ }
// Success if no checks failed.
return !ChecksFailed;
}
diff --git a/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt b/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
index 2ba3dae29c3a6..d343b7d9a287c 100644
--- a/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
+++ b/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
@@ -11,4 +11,3 @@
; DIFF-NEXT: -call void @work_test()
; DIFF-NEXT: +call void @work_task()
; DIFF-NEXT: }
-
diff --git a/llvm/test/FileCheck/diff/diff-large.txt b/llvm/test/FileCheck/diff/diff-large.txt
index 9bb50aec00850..5246d2f0752ad 100644
--- a/llvm/test/FileCheck/diff/diff-large.txt
+++ b/llvm/test/FileCheck/diff/diff-large.txt
@@ -9,4 +9,3 @@
; DIFF-NEXT: -DATA_2500_WRONG
; DIFF-NEXT: +DATA_20
; DIFF-NEXT: DATA_21
-
>From f26ec9474d12b5b6ce0efdb9a2d8460423478f9f Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Thu, 26 Mar 2026 04:43:20 +0530
Subject: [PATCH 03/14] header print
---
llvm/lib/FileCheck/FileCheck.cpp | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 96d84f4910c77..8d08d24c933b2 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -3020,8 +3020,22 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
if (CheckRegion.empty())
return false;
+ if (!HeaderPrinted) {
+ StringRef CheckFile =
+ SM.getMemoryBuffer(SM.getMainFileID())->getBufferIdentifier();
+ unsigned InputBufID =
+ SM.FindBufferContainingLoc(SMLoc::getFromPointer(CheckRegion.data()));
+ StringRef InputFile = SM.getMemoryBuffer(InputBufID)->getBufferIdentifier();
+ OS.changeColor(raw_ostream::WHITE, true);
+ OS << "--- " << CheckFile << "\n";
+ OS << "+++ " << InputFile << "\n";
+ OS.resetColor();
+ HeaderPrinted = true;
+ }
+
SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
unsigned CurrentLineNo = SM.getLineAndColumn(CurrentLoc).first;
+
size_t EOL = CheckRegion.find('\n');
StringRef MismatchLine =
(EOL == StringRef::npos) ? CheckRegion : CheckRegion.substr(0, EOL);
@@ -3086,6 +3100,7 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
std::vector<FileCheckDiag> *Diags) {
bool ChecksFailed = false;
unsigned TotalMismatches = 0;
+ bool HeaderPrinted = false;
auto &OS = llvm::errs();
unsigned i = 0, j = 0, e = CheckStrings.size();
@@ -3119,7 +3134,6 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
if (i != 0 && Req.EnableVarScope)
PatternContext->clearLocalVars();
- bool HeaderPrinted = false;
for (; i != j; ++i) {
const FileCheckString &CheckStr = CheckStrings[i];
>From 70e58b755d8f742777f094b8ac48fbde4e0bc8f6 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Thu, 26 Mar 2026 15:18:05 +0530
Subject: [PATCH 04/14] updated test caes for split-file
---
llvm/lib/FileCheck/FileCheck.cpp | 12 ++++----
llvm/test/FileCheck/diff/diff-complex.txt | 13 --------
.../test/FileCheck/diff/diff-fuzzy-lables.txt | 13 --------
llvm/test/FileCheck/diff/diff-helloworld.txt | 8 -----
.../FileCheck/diff/diff-label-boundary.txt | 12 ++++++--
.../FileCheck/diff/diff-label-fuzzy-match.txt | 18 +++++++++++
llvm/test/FileCheck/diff/diff-label-next.txt | 19 ++++++++++++
llvm/test/FileCheck/diff/diff-labels.txt | 11 -------
llvm/test/FileCheck/diff/diff-large.txt | 11 -------
.../FileCheck/diff/diff-long-line-trunc.txt | 12 ++++++++
llvm/test/FileCheck/diff/diff-long-line.txt | 7 -----
llvm/test/FileCheck/diff/diff-mid.txt | 13 --------
.../test/FileCheck/diff/diff-missing-line.txt | 11 -------
llvm/test/FileCheck/diff/diff-multi-subs.txt | 10 +++++--
llvm/test/FileCheck/diff/diff-multihunk.txt | 23 ++++++++------
.../FileCheck/diff/diff-nested-vars-subs.txt | 16 ++++++++++
llvm/test/FileCheck/diff/diff-nested-vars.txt | 10 -------
.../FileCheck/diff/diff-next-stray-line.txt | 18 +++++++++++
llvm/test/FileCheck/diff/diff-next.txt | 11 -------
llvm/test/FileCheck/diff/diff-noise.txt | 26 ----------------
.../test/FileCheck/diff/diff-numeric-expr.txt | 16 ++++++++++
.../diff/diff-numeric-expression.txt | 10 -------
llvm/test/FileCheck/diff/diff-overlap.txt | 15 ----------
.../FileCheck/diff/diff-regex-escaping.txt | 13 ++++++++
.../diff/diff-resync-after-noise.txt | 30 +++++++++++++++++++
.../FileCheck/diff/diff-resync-high-noise.txt | 23 ++++++++++++++
.../FileCheck/diff/diff-resync-overlap.txt | 25 ++++++++++++++++
llvm/test/FileCheck/diff/diff-resync.txt | 13 --------
.../FileCheck/diff/diff-skipped-expected.txt | 17 +++++++++++
.../diff/diff-stress-large-input.txt | 15 ++++++++++
.../FileCheck/diff/diff-substitutions.txt | 13 ++++++--
.../diff/diff-variable-chain-subs.txt | 18 +++++++++++
.../FileCheck/diff/diff-variable-chain.txt | 11 -------
33 files changed, 288 insertions(+), 205 deletions(-)
delete mode 100644 llvm/test/FileCheck/diff/diff-complex.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-helloworld.txt
create mode 100644 llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
create mode 100644 llvm/test/FileCheck/diff/diff-label-next.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-labels.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-large.txt
create mode 100644 llvm/test/FileCheck/diff/diff-long-line-trunc.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-long-line.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-mid.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-missing-line.txt
create mode 100644 llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-nested-vars.txt
create mode 100644 llvm/test/FileCheck/diff/diff-next-stray-line.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-next.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-noise.txt
create mode 100644 llvm/test/FileCheck/diff/diff-numeric-expr.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-numeric-expression.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-overlap.txt
create mode 100644 llvm/test/FileCheck/diff/diff-regex-escaping.txt
create mode 100644 llvm/test/FileCheck/diff/diff-resync-after-noise.txt
create mode 100644 llvm/test/FileCheck/diff/diff-resync-high-noise.txt
create mode 100644 llvm/test/FileCheck/diff/diff-resync-overlap.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-resync.txt
create mode 100644 llvm/test/FileCheck/diff/diff-skipped-expected.txt
create mode 100644 llvm/test/FileCheck/diff/diff-stress-large-input.txt
create mode 100644 llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-variable-chain.txt
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 8d08d24c933b2..d282e17a21f0d 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2936,7 +2936,7 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
unsigned OverwriteActualLine = 0) {
StringRef ActualLine = CheckRegion.split('\n').first;
if (CheckRegion.empty()) {
- ActualLine = "<EOF>";
+ ActualLine = "";
}
SMLoc PatternLoc = CheckStr.Pat.getLoc();
@@ -3016,10 +3016,6 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
std::vector<FileCheckDiag> *Diags,
raw_ostream &OS, bool &HeaderPrinted,
unsigned &TotalMismatches) {
- CheckRegion = CheckRegion.ltrim("\n\r");
- if (CheckRegion.empty())
- return false;
-
if (!HeaderPrinted) {
StringRef CheckFile =
SM.getMemoryBuffer(SM.getMainFileID())->getBufferIdentifier();
@@ -3033,8 +3029,12 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
HeaderPrinted = true;
}
+ CheckRegion = CheckRegion.ltrim("\n\r");
+
SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
- unsigned CurrentLineNo = SM.getLineAndColumn(CurrentLoc).first;
+ unsigned CurrentLineNo = 0;
+ if (!CheckRegion.empty())
+ CurrentLineNo = SM.getLineAndColumn(CurrentLoc).first;
size_t EOL = CheckRegion.find('\n');
StringRef MismatchLine =
diff --git a/llvm/test/FileCheck/diff/diff-complex.txt b/llvm/test/FileCheck/diff/diff-complex.txt
deleted file mode 100644
index e4df50dbdf936..0000000000000
--- a/llvm/test/FileCheck/diff/diff-complex.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: printf "HEADER\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET_BLOCK\nStray1\nStray2\nMATCH_ME\nNEXT_LINE\nNoise5\nNoise6\n" > %t.input
-; RUN: not FileCheck --diff=unidiff --input-file %t.input %s --check-prefix=COMPLEX 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; COMPLEX-LABEL: HEADER
-; COMPLEX: TARGET_BLOCK
-; COMPLEX: MATCH_ME
-; COMPLEX-NEXT: WRONG_NEXT
-; COMPLEX: END_OF_FUNCTION
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: MATCH_ME
-; DIFF-NEXT: -WRONG_NEXT
-; DIFF-NEXT: +NEXT_LINE
diff --git a/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt b/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
deleted file mode 100644
index d343b7d9a287c..0000000000000
--- a/llvm/test/FileCheck/diff/diff-fuzzy-lables.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: printf "define void @foo() {\n call void @work_task()\n}\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK-LABEL: define void @foo()
-; CHECK: call void @work_test()
-
-; Need to fixed this
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF: define void @foo() {
-; DIFF-NEXT: -call void @work_test()
-; DIFF-NEXT: +call void @work_task()
-; DIFF-NEXT: }
diff --git a/llvm/test/FileCheck/diff/diff-helloworld.txt b/llvm/test/FileCheck/diff/diff-helloworld.txt
deleted file mode 100644
index 074c15bdcfe86..0000000000000
--- a/llvm/test/FileCheck/diff/diff-helloworld.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-; RUN: printf "hello world\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck -check-prefix=UNI-DIFF %s
-
-; CHECK: hello {{U}}niverse
-
-; UNI-DIFF: @@ -4 +1 @@
-; UNI-DIFF-NEXT: -hello {{[{][{]U[}][}]}}niverse
-; UNI-DIFF-NEXT: +hello world
diff --git a/llvm/test/FileCheck/diff/diff-label-boundary.txt b/llvm/test/FileCheck/diff/diff-label-boundary.txt
index 2d3cc114f254e..6381e2a71f82e 100644
--- a/llvm/test/FileCheck/diff/diff-label-boundary.txt
+++ b/llvm/test/FileCheck/diff/diff-label-boundary.txt
@@ -1,6 +1,14 @@
-; RUN: printf "FUNCTION_A\n BAD_OP\nFUNCTION_B\n GOOD_OP\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+; RUN: split-file %s %t
+; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+;--- input.txt
+FUNCTION_A
+ BAD_OP
+FUNCTION_B
+ GOOD_OP
+
+;--- check.txt
; CHECK-LABEL: FUNCTION_A
; CHECK: CORRECT_OP
; CHECK-LABEL: FUNCTION_B
diff --git a/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt b/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
new file mode 100644
index 0000000000000..a04f2e84ac70b
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
@@ -0,0 +1,18 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+define void @foo() {
+ call void @work_task()
+}
+
+;--- check.txt
+; CHECK-LABEL: define void @foo()
+; CHECK: call void @work_test()
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF: define void @foo() {
+; DIFF-NEXT: -call void @work_test()
+; DIFF-NEXT: +call void @work_task()
+; DIFF-NEXT: }
diff --git a/llvm/test/FileCheck/diff/diff-label-next.txt b/llvm/test/FileCheck/diff/diff-label-next.txt
new file mode 100644
index 0000000000000..f2ea934376d6e
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-label-next.txt
@@ -0,0 +1,19 @@
+; RUN: split-file %s %t
+; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+FUNC_A
+ mov r0, #1
+FUNC_B
+ mov r1, #2
+
+;--- check.txt
+; CHECK-LABEL: FUNC_A
+; CHECK-NEXT: mov r0, #5
+; CHECK-LABEL: FUNC_B
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: FUNC_A
+; DIFF-NEXT: -mov r0, #5
+; DIFF-NEXT: +mov r0, #1
diff --git a/llvm/test/FileCheck/diff/diff-labels.txt b/llvm/test/FileCheck/diff/diff-labels.txt
deleted file mode 100644
index 0ef08ce6f5f63..0000000000000
--- a/llvm/test/FileCheck/diff/diff-labels.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-; RUN: printf "FUNC_A\n mov r0, #1\nFUNC_B\n mov r1, #2\n" > %t.labels.input
-; RUN: not FileCheck --diff=unidiff --input-file %t.labels.input %s --check-prefix=LABELS 2>&1 | FileCheck %s -check-prefix=DIFF_LABELS
-
-; LABELS-LABEL: FUNC_A
-; LABELS-NEXT: mov r0, #5
-; LABELS-LABEL: FUNC_B
-
-; DIFF_LABELS: @@ -[[#]] +[[#]] @@
-; DIFF_LABELS-NEXT: FUNC_A
-; DIFF_LABELS-NEXT: -mov r0, #5
-; DIFF_LABELS-NEXT: +mov r0, #1
diff --git a/llvm/test/FileCheck/diff/diff-large.txt b/llvm/test/FileCheck/diff/diff-large.txt
deleted file mode 100644
index 5246d2f0752ad..0000000000000
--- a/llvm/test/FileCheck/diff/diff-large.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-; RUN: python3 -c "for i in range(5000): print(f'DATA_{i}')" > %t.input
-; RUN: echo "CHECK: DATA_0" > %t.check
-; RUN: echo "CHECK: DATA_2500_WRONG" >> %t.check
-; RUN: echo "CHECK: DATA_4999" >> %t.check
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %t.check 2>&1 | FileCheck %s --check-prefix=DIFF
-
-; DIFF: @@ -2 +21 @@
-; DIFF-NEXT: DATA_19
-; DIFF-NEXT: -DATA_2500_WRONG
-; DIFF-NEXT: +DATA_20
-; DIFF-NEXT: DATA_21
diff --git a/llvm/test/FileCheck/diff/diff-long-line-trunc.txt b/llvm/test/FileCheck/diff/diff-long-line-trunc.txt
new file mode 100644
index 0000000000000..e57b23a2927c1
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-long-line-trunc.txt
@@ -0,0 +1,12 @@
+; RUN: split-file %s %t
+; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_ACTUAL_VALUE
+
+;--- check.txt
+; CHECK: THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_EXPECTED_VALUE
+
+; DIFF: @@ L:[[#]] R:[[#]] @@
+; DIFF-NEXT: {{.*}}...{{.*}}WINDOW_LOGIC_EXPECTED_VALUE{{.*}}!{{.*}}...{{.*}}WINDOW_LOGIC_ACTUAL_VALUE
diff --git a/llvm/test/FileCheck/diff/diff-long-line.txt b/llvm/test/FileCheck/diff/diff-long-line.txt
deleted file mode 100644
index 3d771c34c4e2c..0000000000000
--- a/llvm/test/FileCheck/diff/diff-long-line.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-; RUN: printf "THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_ACTUAL_VALUE\n" > %t.input
-; RUN: not FileCheck --diff=split --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_EXPECTED_VALUE
-
-; DIFF: @@ L:[[#]] R:[[#]] @@
-; DIFF-NEXT: {{.*}}...{{.*}}WINDOW_LOGIC_EXPECTED_VALUE{{.*}}!{{.*}}...{{.*}}WINDOW_LOGIC_ACTUAL_VALUE
diff --git a/llvm/test/FileCheck/diff/diff-mid.txt b/llvm/test/FileCheck/diff/diff-mid.txt
deleted file mode 100644
index d83438b80ecdf..0000000000000
--- a/llvm/test/FileCheck/diff/diff-mid.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: printf "START\nWRONG\nEND\n" > %t.mid.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.mid.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: START
-; CHECK: MIDDLE
-; CHECK: END
-
-; We should fix this.
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: WRONG
-; DIFF-NEXT: -MIDDLE
-; DIFF-NEXT: +END
diff --git a/llvm/test/FileCheck/diff/diff-missing-line.txt b/llvm/test/FileCheck/diff/diff-missing-line.txt
deleted file mode 100644
index a7b79a4a055c3..0000000000000
--- a/llvm/test/FileCheck/diff/diff-missing-line.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-; RUN: printf "ALPHA\nGAMMA\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: ALPHA
-; CHECK: BETA
-; CHECK: GAMMA
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: ALPHA
-; DIFF-NEXT: -BETA
-; DIFF-NEXT: +GAMMA
diff --git a/llvm/test/FileCheck/diff/diff-multi-subs.txt b/llvm/test/FileCheck/diff/diff-multi-subs.txt
index 8f37bb90a99b3..22cdf37a02bd7 100644
--- a/llvm/test/FileCheck/diff/diff-multi-subs.txt
+++ b/llvm/test/FileCheck/diff/diff-multi-subs.txt
@@ -1,6 +1,12 @@
-; RUN: printf "SET REG=rax VAL=0\nUSE rax, 1\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=split --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+;--- input.txt
+SET REG=rax VAL=0
+USE rax, 1
+
+;--- check.txt
; CHECK: SET REG=[[REG:[a-z]+]] VAL=[[VAL:[0-9]+]]
; CHECK: USE [[REG]], [[VAL]]
diff --git a/llvm/test/FileCheck/diff/diff-multihunk.txt b/llvm/test/FileCheck/diff/diff-multihunk.txt
index 55f4933d6c211..aa78446b0604d 100644
--- a/llvm/test/FileCheck/diff/diff-multihunk.txt
+++ b/llvm/test/FileCheck/diff/diff-multihunk.txt
@@ -1,13 +1,18 @@
-; RUN: printf "BLOCK1_FAIL\nKEEP_THIS\nBLOCK2_FAIL\n" > %t.multi.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.multi.input %s 2>&1 | FileCheck %s -check-prefix=UNI-DIFF
-;
-; --- TEST DATA ---
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+BLOCK1_FAIL
+KEEP_THIS
+BLOCK2_FAIL
+
+;--- check.txt
; CHECK: BLOCK1_PASS
; CHECK: KEEP_THIS
; CHECK: BLOCK2_PASS
-; --- VERIFY ---
-; UNI-DIFF: @@ -[[#]] +1 @@
-; UNI-DIFF-NEXT: -BLOCK1_PASS
-; UNI-DIFF-NEXT: +BLOCK1_FAIL
-; UNI-DIFF-NEXT: KEEP_THIS
+; DIFF: @@ -[[#]] +1 @@
+; DIFF-NEXT: -BLOCK1_PASS
+; DIFF-NEXT: +BLOCK1_FAIL
+; DIFF-NEXT: KEEP_THIS
diff --git a/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt b/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
new file mode 100644
index 0000000000000..7be1c1d9d96ab
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
@@ -0,0 +1,16 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+SET [[42]]
+DATA: [[42]]
+
+;--- check.txt
+; CHECK: SET [[INNER:.*]]
+; CHECK: DATA: WRONG_VALUE
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: SET {{\[\[}}42{{\]\]}}
+; DIFF-NEXT: -DATA: WRONG_VALUE
+; DIFF-NEXT: +DATA: {{\[\[}}42{{\]\]}}
diff --git a/llvm/test/FileCheck/diff/diff-nested-vars.txt b/llvm/test/FileCheck/diff/diff-nested-vars.txt
deleted file mode 100644
index 406d029bb9931..0000000000000
--- a/llvm/test/FileCheck/diff/diff-nested-vars.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-; RUN: printf "SET [[42]]\nDATA: [[42]]\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: SET [[INNER:.*]]
-; CHECK: DATA: WRONG_VALUE
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: SET {{\[\[}}42{{\]\]}}
-; DIFF-NEXT: -DATA: WRONG_VALUE
-; DIFF-NEXT: +DATA: {{\[\[}}42{{\]\]}}
diff --git a/llvm/test/FileCheck/diff/diff-next-stray-line.txt b/llvm/test/FileCheck/diff/diff-next-stray-line.txt
new file mode 100644
index 0000000000000..dd19a0e2c6dd2
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-next-stray-line.txt
@@ -0,0 +1,18 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+LINE1
+STRAY
+LINE2
+
+;--- check.txt
+; CHECK: LINE1
+; CHECK-NEXT: LINE2
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: LINE1
+; DIFF-NEXT: -LINE2
+; DIFF-NEXT: +STRAY
+; DIFF-NEXT: LINE2
diff --git a/llvm/test/FileCheck/diff/diff-next.txt b/llvm/test/FileCheck/diff/diff-next.txt
deleted file mode 100644
index a6022690b4404..0000000000000
--- a/llvm/test/FileCheck/diff/diff-next.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-; RUN: printf "LINE1\nSTRAY\nLINE2\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: LINE1
-; CHECK-NEXT: LINE2
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: LINE1
-; DIFF-NEXT: -LINE2
-; DIFF-NEXT: +STRAY
-; DIFF-NEXT: LINE2
diff --git a/llvm/test/FileCheck/diff/diff-noise.txt b/llvm/test/FileCheck/diff/diff-noise.txt
deleted file mode 100644
index 4c1b8eacab98d..0000000000000
--- a/llvm/test/FileCheck/diff/diff-noise.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-; --- Test 1: High Noise Ratio ---
-; RUN: printf "START\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET\nEND\n" > %t.noise.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.noise.input %s --check-prefix=TEST1 2>&1 | FileCheck %s -check-prefix=DIFF1
-
-; TEST1: START
-; TEST1-NEXT: TARGET
-; TEST1: END
-
-; DIFF1: @@ -[[#]] +[[#]] @@
-; DIFF1-NEXT: START
-; DIFF1-NEXT: -TARGET
-; DIFF1-NEXT: +Noise1
-; DIFF1-NEXT: Noise2
-
-; --- Test 2: CHECK-NEXT stray line ---
-; RUN: printf "HEADER\nSTRAY\nNEXT\n" > %t.next.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.next.input %s --check-prefix=TEST2 2>&1 | FileCheck %s -check-prefix=DIFF2
-
-; TEST2-LABEL: HEADER
-; TEST2-NEXT: NEXT
-
-; DIFF2: @@ -[[#]] +[[#]] @@
-; DIFF2-NEXT: HEADER
-; DIFF2-NEXT: -NEXT
-; DIFF2-NEXT: +STRAY
-; DIFF2-NEXT: NEXT
diff --git a/llvm/test/FileCheck/diff/diff-numeric-expr.txt b/llvm/test/FileCheck/diff/diff-numeric-expr.txt
new file mode 100644
index 0000000000000..36cfe98bea0d0
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-numeric-expr.txt
@@ -0,0 +1,16 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+BASE: 10
+NEXT: 12
+
+;--- check.txt
+; CHECK: BASE: [[#VAL:]]
+; CHECK: NEXT: [[#VAL+1]]
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: BASE: 10
+; DIFF-NEXT: -NEXT: 11
+; DIFF-NEXT: +NEXT: 12
diff --git a/llvm/test/FileCheck/diff/diff-numeric-expression.txt b/llvm/test/FileCheck/diff/diff-numeric-expression.txt
deleted file mode 100644
index d8a81b4e37aa6..0000000000000
--- a/llvm/test/FileCheck/diff/diff-numeric-expression.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-; RUN: printf "BASE: 10\nNEXT: 12\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: BASE: [[#VAL:]]
-; CHECK: NEXT: [[#VAL+1]]
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: BASE: 10
-; DIFF-NEXT: -NEXT: 11
-; DIFF-NEXT: +NEXT: 12
diff --git a/llvm/test/FileCheck/diff/diff-overlap.txt b/llvm/test/FileCheck/diff/diff-overlap.txt
deleted file mode 100644
index 49733a0c74434..0000000000000
--- a/llvm/test/FileCheck/diff/diff-overlap.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-; RUN: printf "LINE1\nBAD_LINE\nLINE3\nLINE4\nBAD_LINE\nLINE6\n" > %t.overlap.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.overlap.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: LINE1
-; CHECK: LINE2
-; CHECK: LINE3
-; CHECK: LINE4
-; CHECK: LINE5
-; CHECK: LINE6
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: LINE1
-; DIFF-NEXT: -LINE2
-; DIFF-NEXT: +BAD_LINE
-; DIFF-NEXT: LINE3
diff --git a/llvm/test/FileCheck/diff/diff-regex-escaping.txt b/llvm/test/FileCheck/diff/diff-regex-escaping.txt
new file mode 100644
index 0000000000000..8f3ebc6163f04
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-regex-escaping.txt
@@ -0,0 +1,13 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+hello world
+
+;--- check.txt
+; CHECK: hello {{U}}niverse
+
+; DIFF: @@ -[[#]] +1 @@
+; DIFF-NEXT: -hello {{[{][{]U[}][}]}}niverse
+; DIFF-NEXT: +hello world
diff --git a/llvm/test/FileCheck/diff/diff-resync-after-noise.txt b/llvm/test/FileCheck/diff/diff-resync-after-noise.txt
new file mode 100644
index 0000000000000..854ee188b2944
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-resync-after-noise.txt
@@ -0,0 +1,30 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+HEADER
+Noise1
+Noise2
+Noise3
+Noise4
+TARGET_BLOCK
+Stray1
+Stray2
+MATCH_ME
+NEXT_LINE
+Noise5
+Noise6
+
+;--- check.txt
+; CHECK-LABEL: HEADER
+; CHECK: TARGET_BLOCK
+; CHECK: MATCH_ME
+; CHECK-NEXT: WRONG_NEXT
+; CHECK: END_OF_FUNCTION
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: MATCH_ME
+; DIFF-NEXT: -WRONG_NEXT
+; DIFF-NEXT: +NEXT_LINE
+; DIFF-NEXT: Noise5
diff --git a/llvm/test/FileCheck/diff/diff-resync-high-noise.txt b/llvm/test/FileCheck/diff/diff-resync-high-noise.txt
new file mode 100644
index 0000000000000..d7ecfe48d3b78
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-resync-high-noise.txt
@@ -0,0 +1,23 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+START
+Noise1
+Noise2
+Noise3
+Noise4
+TARGET
+END
+
+;--- check.txt
+; CHECK: START
+; CHECK-NEXT: TARGET
+; CHECK: END
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: START
+; DIFF-NEXT: -TARGET
+; DIFF-NEXT: +Noise1
+; DIFF-NEXT: Noise2
diff --git a/llvm/test/FileCheck/diff/diff-resync-overlap.txt b/llvm/test/FileCheck/diff/diff-resync-overlap.txt
new file mode 100644
index 0000000000000..85408544e2073
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-resync-overlap.txt
@@ -0,0 +1,25 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+LINE1
+BAD_LINE
+LINE3
+LINE4
+BAD_LINE
+LINE6
+
+;--- check.txt
+; CHECK: LINE1
+; CHECK: LINE2
+; CHECK: LINE3
+; CHECK: LINE4
+; CHECK: LINE5
+; CHECK: LINE6
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: LINE1
+; DIFF-NEXT: -LINE2
+; DIFF-NEXT: +BAD_LINE
+; DIFF-NEXT: LINE3
diff --git a/llvm/test/FileCheck/diff/diff-resync.txt b/llvm/test/FileCheck/diff/diff-resync.txt
deleted file mode 100644
index e4df50dbdf936..0000000000000
--- a/llvm/test/FileCheck/diff/diff-resync.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: printf "HEADER\nNoise1\nNoise2\nNoise3\nNoise4\nTARGET_BLOCK\nStray1\nStray2\nMATCH_ME\nNEXT_LINE\nNoise5\nNoise6\n" > %t.input
-; RUN: not FileCheck --diff=unidiff --input-file %t.input %s --check-prefix=COMPLEX 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; COMPLEX-LABEL: HEADER
-; COMPLEX: TARGET_BLOCK
-; COMPLEX: MATCH_ME
-; COMPLEX-NEXT: WRONG_NEXT
-; COMPLEX: END_OF_FUNCTION
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: MATCH_ME
-; DIFF-NEXT: -WRONG_NEXT
-; DIFF-NEXT: +NEXT_LINE
diff --git a/llvm/test/FileCheck/diff/diff-skipped-expected.txt b/llvm/test/FileCheck/diff/diff-skipped-expected.txt
new file mode 100644
index 0000000000000..f8b4d2f303bb5
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-skipped-expected.txt
@@ -0,0 +1,17 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+ALPHA
+GAMMA
+
+;--- check.txt
+; CHECK: ALPHA
+; CHECK: BETA
+; CHECK: GAMMA
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: ALPHA
+; DIFF-NEXT: -BETA
+; DIFF-NEXT: +GAMMA
diff --git a/llvm/test/FileCheck/diff/diff-stress-large-input.txt b/llvm/test/FileCheck/diff/diff-stress-large-input.txt
new file mode 100644
index 0000000000000..2e411702f8aa3
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-stress-large-input.txt
@@ -0,0 +1,15 @@
+; RUN: split-file %s %t
+; RUN: python3 -c "for i in range(5000): print(f'DATA_{i}')" > %t/input.txt
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- check.txt
+; CHECK: DATA_0
+; CHECK: DATA_2500_WRONG
+; CHECK: DATA_4999
+
+; DIFF: @@ -2 +21 @@
+; DIFF-NEXT: DATA_19
+; DIFF-NEXT: -DATA_2500_WRONG
+; DIFF-NEXT: +DATA_20
+; DIFF-NEXT: DATA_21
diff --git a/llvm/test/FileCheck/diff/diff-substitutions.txt b/llvm/test/FileCheck/diff/diff-substitutions.txt
index 085780759d874..11c8a68484f1f 100644
--- a/llvm/test/FileCheck/diff/diff-substitutions.txt
+++ b/llvm/test/FileCheck/diff/diff-substitutions.txt
@@ -1,7 +1,14 @@
-; RUN: printf "START 42\nFAIL 100\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck -check-prefix=SUBST %s
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff-no-substitutions --input-file %t.input %s 2>&1 | FileCheck -check-prefix=RAW %s
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=SUBST
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff-no-substitutions 2>&1 \
+; RUN: | FileCheck %s --check-prefix=RAW
+;--- input.txt
+START 42
+FAIL 100
+
+;--- check.txt
; CHECK: START [[VAL:[0-9]+]]
; CHECK: FAIL [[VAL]]
diff --git a/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt b/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
new file mode 100644
index 0000000000000..4e172a4f28789
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
@@ -0,0 +1,18 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+ID: 0x100
+TYPE: apple
+USE: 0x100 (orange)
+
+;--- check.txt
+; CHECK: ID: [[ID:0x[0-9a-f]+]]
+; CHECK: TYPE: [[TYPE:[a-z]+]]
+; CHECK: USE: [[ID]] ([[TYPE]])
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: TYPE: apple
+; DIFF-NEXT: -USE: 0x100 (apple)
+; DIFF-NEXT: +USE: 0x100 (orange)
diff --git a/llvm/test/FileCheck/diff/diff-variable-chain.txt b/llvm/test/FileCheck/diff/diff-variable-chain.txt
deleted file mode 100644
index e370046c18daa..0000000000000
--- a/llvm/test/FileCheck/diff/diff-variable-chain.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-; RUN: printf "ID: 0x100\nTYPE: apple\nUSE: 0x100 (orange)\n" > %t.input
-; RUN: %ProtectFileCheckOutput not FileCheck --diff=unidiff --input-file %t.input %s 2>&1 | FileCheck %s -check-prefix=DIFF
-
-; CHECK: ID: [[ID:0x[0-9a-f]+]]
-; CHECK: TYPE: [[TYPE:[a-z]+]]
-; CHECK: USE: [[ID]] ([[TYPE]])
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: TYPE: apple
-; DIFF-NEXT: -USE: 0x100 (apple)
-; DIFF-NEXT: +USE: 0x100 (orange)
>From 6229e7b6ce1193327e5179f99ed1e24e658ae651 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 10:58:26 +0530
Subject: [PATCH 05/14] removed split view and substitution functions from the
PR
---
llvm/docs/CommandGuide/FileCheck.rst | 5 -
llvm/include/llvm/FileCheck/FileCheck.h | 8 +-
llvm/lib/FileCheck/FileCheck.cpp | 202 ++++--------------
llvm/lib/FileCheck/FileCheckImpl.h | 2 -
.../FileCheck/diff/diff-long-line-trunc.txt | 12 --
llvm/test/FileCheck/diff/diff-multi-subs.txt | 15 --
.../FileCheck/diff/diff-nested-vars-subs.txt | 16 --
.../test/FileCheck/diff/diff-numeric-expr.txt | 16 --
.../diff/diff-stress-large-input.txt | 2 +-
.../FileCheck/diff/diff-substitutions.txt | 23 --
.../diff/diff-variable-chain-subs.txt | 18 --
llvm/utils/FileCheck/FileCheck.cpp | 7 +-
12 files changed, 39 insertions(+), 287 deletions(-)
delete mode 100644 llvm/test/FileCheck/diff/diff-long-line-trunc.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-multi-subs.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-numeric-expr.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-substitutions.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index c02613e440ac2..1479c45046068 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -117,12 +117,7 @@ and from the command line.
diagnostic output.
* ``standard`` – Use the standard FileCheck diagnostic messages.
- * ``split`` – Display mismatches using a side-by-side split diff view.
* ``unidiff`` – Display mismatches using a unified diff format.
- * ``split-no-substitutions`` – Same as ``split`` but shows the raw
- pattern without applying variable substitutions.
- * ``unidiff-no-substitutions`` – Same as ``unidiff`` but shows the raw
- pattern without applying variable substitutions.
.. option:: --dump-input <value>
diff --git a/llvm/include/llvm/FileCheck/FileCheck.h b/llvm/include/llvm/FileCheck/FileCheck.h
index b8db1194d7340..e5af0539eb1cf 100644
--- a/llvm/include/llvm/FileCheck/FileCheck.h
+++ b/llvm/include/llvm/FileCheck/FileCheck.h
@@ -28,13 +28,7 @@ class SourceMgr;
template <typename T> class SmallVectorImpl;
// Diff the output on failures.
-enum DiffFormatType {
- Standard,
- Split,
- Unified,
- SplitNoSubstitution,
- UnifiedNoSubstitution
-};
+enum DiffFormatType { Standard, Unified };
/// Contains info about various FileCheck options.
struct FileCheckRequest {
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index d282e17a21f0d..63f5226f47483 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2732,42 +2732,13 @@ void FileCheckPatternContext::clearLocalVars() {
GlobalNumericVariableTable.erase(Var);
}
-/// Extract a fixed-width "window" from string \p S, centered around \p DiffPos.
-///
-/// This is used for side-by-side diffing of long lines (e.g., metadata
-/// or long symbol names). It ensures that the "point of divergence" is visible
-/// within the provided \p Width. If the string is truncated, ellipses (...)
-/// are inserted at the boundaries.
-static std::string getCenteredView(StringRef S, size_t DiffPos, size_t Width) {
- if (S.size() <= Width)
- return S.str() + std::string(Width - S.size(), ' ');
-
- size_t HalfWidth = Width / 2;
- size_t Start = (DiffPos > HalfWidth) ? DiffPos - HalfWidth : 0;
-
- if (Start + Width > S.size())
- Start = (S.size() > Width) ? S.size() - Width : 0;
-
- std::string View = S.substr(Start, Width).str();
-
- if (Start > 0 && Width > 3)
- View.replace(0, 3, "...");
-
- if (S.size() > Start + Width && View.size() > 3)
- View.replace(View.size() - 3, 3, "...");
-
- return View;
-}
-
struct DiffContext {
StringRef Line;
StringRef LineBefore;
StringRef LineAfter;
};
-/// Populates a \c DiffContext by fetching the line at \p LineNo
-/// as well as the lines before and after it from the \p SourceMgr. This
-/// provides the "surrounding context" seen in standard diff tools.
+// Provides the "surrounding context" seen in standard diff tools.
static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
unsigned BufID) {
const MemoryBuffer *Buffer = SM.getMemoryBuffer(BufID);
@@ -2796,139 +2767,42 @@ static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
getLineText(LineNo + 1)};
}
-/// Converts a raw pattern string (e.g., "val [[VAL]]") into a
-/// human-readable diagnostic string (e.g., "val 42") by replacing
-/// "[[...]]" tags with the values stored in the \c Substitutions vector.
-std::string Pattern::getSubstitutedRegex(StringRef PatternText) const {
- std::string Result = PatternText.str();
-
- // We iterate through substitutions. Since they are stored in the order
- // they appear in the pattern, we can replace tags from left to right.
- for (const auto &Substitution : Substitutions) {
- Expected<std::string> ValueOrErr = Substitution->getResultForDiagnostics();
- std::string CleanValue;
-
- if (!ValueOrErr) {
- consumeError(ValueOrErr.takeError());
- CleanValue = "<UNDEFINED>";
- } else {
- CleanValue = *ValueOrErr;
- // Numeric substitutions arrive wrapped in quotes so we need to strip
- // them.
- if (CleanValue.size() >= 2 && CleanValue.front() == '"' &&
- CleanValue.back() == '"')
- CleanValue = CleanValue.substr(1, CleanValue.size() - 2);
- }
-
- // Find the first occurrence of a variable tag
- size_t Start = Result.find("[[");
- if (Start == std::string::npos)
- break;
-
- size_t End = Result.find("]]", Start);
- if (End == std::string::npos)
- break;
-
- size_t TagLen = (End + 2) - Start;
- Result.replace(Start, TagLen, CleanValue);
- }
- return Result;
-}
-
-/// Renders a diagnostic diff to \c llvm::errs().
-///
-/// Supports two modes:
-/// 1. Split View: A side-by-side comparison (Expected | Actual) using a
-/// sliding window centered on the first difference.
-/// 2. Unified View: A standard top-to-bottom (-Expected / +Actual) format.
+// Renders a diagnostic diff to llvm::errs().
+//
+// Supported mode:
+// Unified View: A standard top-to-bottom (-Expected / +Actual) format.
static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
unsigned ActualLineNo, StringRef ActualLine,
const std::string &ExpectedText, const DiffContext &Ctx,
unsigned OverwriteActualLine) {
auto &OS = llvm::errs();
- constexpr unsigned ColWidth = 45;
- constexpr StringRef Sep = " | ";
-
- bool IsSplit = (Mode == Split || Mode == SplitNoSubstitution);
-
- // Identify the first index where the expected and actual text diverge.
- size_t DiffPos = 0;
- size_t MinLen = std::min(ExpectedText.size(), ActualLine.size());
- while (DiffPos < MinLen && ExpectedText[DiffPos] == ActualLine[DiffPos])
- ++DiffPos;
-
- auto GetView = [&](StringRef S) {
- return getCenteredView(S, DiffPos, ColWidth - 1);
- };
-
// Header
OS.changeColor(raw_ostream::CYAN);
- if (IsSplit)
- OS << "@@ L:" << ExpectedLineNo << " R:" << ActualLineNo << " @@\n";
- else
- OS << "@@ -" << ExpectedLineNo << " +" << ActualLineNo << " @@\n";
+ OS << "@@ -" << ExpectedLineNo << " +" << ActualLineNo << " @@\n";
OS.resetColor();
// Before Context
if (!Ctx.LineBefore.empty()) {
- if (IsSplit)
- OS << " " << GetView(Ctx.LineBefore) << Sep << GetView(Ctx.LineBefore)
- << "\n";
- else
- OS << " " << Ctx.LineBefore << "\n";
+ OS << " " << Ctx.LineBefore << "\n";
}
// Mismatch
- if (IsSplit) {
- OS << " ";
-
- OS.changeColor(raw_ostream::RED);
- OS << GetView(ExpectedText);
- OS.resetColor();
-
- bool IsWrongLine = (OverwriteActualLine != 0);
+ OS.changeColor(raw_ostream::RED);
+ OS << "-" << ExpectedText << "\n";
- if (IsWrongLine) {
- // Use a bold yellow '!' to signify that the text might match,
- // but it was found on the wrong line.
- OS.changeColor(raw_ostream::YELLOW, true);
- OS << " ! ";
- OS.resetColor();
- } else {
- OS << " | ";
- }
-
- OS.changeColor(raw_ostream::GREEN);
- OS << GetView(ActualLine) << "\n";
- OS.resetColor();
-
- } else {
- OS.changeColor(raw_ostream::RED);
- OS << "-" << ExpectedText << "\n";
-
- OS.changeColor(raw_ostream::GREEN);
- OS << "+" << ActualLine << "\n";
- OS.resetColor();
- }
+ OS.changeColor(raw_ostream::GREEN);
+ OS << "+" << ActualLine << "\n";
+ OS.resetColor();
// After Context
if (!Ctx.LineAfter.empty()) {
- if (IsSplit)
- OS << " " << GetView(Ctx.LineAfter) << Sep << GetView(Ctx.LineAfter)
- << "\n";
- else
- OS << " " << Ctx.LineAfter << "\n";
+ OS << " " << Ctx.LineAfter << "\n";
}
}
-/// Prepares and prints a visual comparison between a CHECK pattern and the
-/// input.
-///
-/// This function acts as a bridge between the FileCheck engine and the diff
-/// renderer. It resolves the line numbers for both the pattern (Expected) and
-/// the input (Actual), performs variable substitution if requested, and fetches
-/// the surrounding context lines.
+// Prepares and prints a visual comparison between a CHECK pattern and the
+// input.
static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
StringRef CheckRegion, SourceMgr &SM,
const FileCheckRequest &Req,
@@ -2945,39 +2819,35 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
StringRef FullExpectedLine = StringRef(PatPtr).split('\n').first.trim();
std::string ExpectedText;
- if (Mode == DiffFormatType::SplitNoSubstitution ||
- Mode == DiffFormatType::UnifiedNoSubstitution)
- ExpectedText = FullExpectedLine.str(); // Raw pattern: [[VAR]]
- else
- ExpectedText = CheckStr.Pat.getSubstitutedRegex(
- FullExpectedLine); // Substituted: value
+ ExpectedText = FullExpectedLine.str();
// Resolve the Actual (Input) line number.
// Priority: 1. OverwriteActualLine (explicit override)
// 2. Fuzzy Match Diag (where FileCheck 'thinks' the line was)
// 3. Direct pointer resolution via SourceMgr.
SMLoc InputLoc = SMLoc::getFromPointer(CheckRegion.data());
- unsigned ActualLineNo = OverwriteActualLine;
- if (ActualLineNo == 0) {
- ActualLineNo = SM.getLineAndColumn(InputLoc).first;
- bool FoundFuzzy = false;
-
- // Search backward to find most recent fuzzy match for this pattern.
- if (Diags) {
- for (const auto &D : llvm::reverse(*Diags)) {
- if (D.CheckLoc == PatternLoc &&
- D.MatchTy == FileCheckDiag::MatchFuzzy) {
- ActualLineNo = D.InputStartLine;
- FoundFuzzy = true;
- break;
- }
+
+unsigned ActualLineNo = OverwriteActualLine;
+
+if (ActualLineNo == 0) {
+ bool FoundFuzzy = false;
+
+ if (Diags) {
+ for (const auto &D : llvm::reverse(*Diags)) {
+ if (D.CheckLoc == PatternLoc &&
+ D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ ActualLineNo = D.InputStartLine;
+ FoundFuzzy = true;
+ break;
}
}
- // If no diagnostic match was found, calculate the line number directly
- // from the InputLoc pointer using the SourceManager.
- if (!FoundFuzzy)
- ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+ }
+
+ // If no diagnostic match was found, calculate the line number directly
+ // from the InputLoc pointer using the SourceManager.
+ if (ActualLineNo == 0)
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
// if we are at an empty line, usually the relevant context is the line just
// before it.
@@ -3085,7 +2955,7 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
TargetLineNo);
TotalMismatches++;
- // Updates \p CheckRegion to advance the search pointer past the error.
+ // Updates CheckRegion to advance the search pointer past the error.
if (AdvanceToResync && ResyncPos != StringRef::npos)
CheckRegion = CheckRegion.substr(ResyncPos + ResyncMatchLen);
else if (EOL != StringRef::npos)
diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h
index 5b47b87076638..58a03d280b658 100644
--- a/llvm/lib/FileCheck/FileCheckImpl.h
+++ b/llvm/lib/FileCheck/FileCheckImpl.h
@@ -740,8 +740,6 @@ class Pattern {
std::vector<FileCheckDiag> *Diags,
const FileCheckRequest &Req) const;
- std::string getSubstitutedRegex(StringRef PatternText) const;
-
bool hasVariable() const {
return !(Substitutions.empty() && VariableDefs.empty());
}
diff --git a/llvm/test/FileCheck/diff/diff-long-line-trunc.txt b/llvm/test/FileCheck/diff/diff-long-line-trunc.txt
deleted file mode 100644
index e57b23a2927c1..0000000000000
--- a/llvm/test/FileCheck/diff/diff-long-line-trunc.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-; RUN: split-file %s %t
-; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_ACTUAL_VALUE
-
-;--- check.txt
-; CHECK: THIS_IS_A_VERY_LONG_PREFIX_THAT_SHOULD_BE_TRUNCATED_BY_THE_SLIDING_WINDOW_LOGIC_EXPECTED_VALUE
-
-; DIFF: @@ L:[[#]] R:[[#]] @@
-; DIFF-NEXT: {{.*}}...{{.*}}WINDOW_LOGIC_EXPECTED_VALUE{{.*}}!{{.*}}...{{.*}}WINDOW_LOGIC_ACTUAL_VALUE
diff --git a/llvm/test/FileCheck/diff/diff-multi-subs.txt b/llvm/test/FileCheck/diff/diff-multi-subs.txt
deleted file mode 100644
index 22cdf37a02bd7..0000000000000
--- a/llvm/test/FileCheck/diff/diff-multi-subs.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-SET REG=rax VAL=0
-USE rax, 1
-
-;--- check.txt
-; CHECK: SET REG=[[REG:[a-z]+]] VAL=[[VAL:[0-9]+]]
-; CHECK: USE [[REG]], [[VAL]]
-
-; DIFF: @@ L:[[#]] R:[[#]] @@
-; DIFF-NEXT: {{.*}}SET REG=rax VAL=0{{.*}}| SET REG=rax VAL=0{{.*}}
-; DIFF-NEXT: {{.*}}USE rax, 0{{.*}}!{{.*}}USE rax, 1{{.*}}
diff --git a/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt b/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
deleted file mode 100644
index 7be1c1d9d96ab..0000000000000
--- a/llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-SET [[42]]
-DATA: [[42]]
-
-;--- check.txt
-; CHECK: SET [[INNER:.*]]
-; CHECK: DATA: WRONG_VALUE
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: SET {{\[\[}}42{{\]\]}}
-; DIFF-NEXT: -DATA: WRONG_VALUE
-; DIFF-NEXT: +DATA: {{\[\[}}42{{\]\]}}
diff --git a/llvm/test/FileCheck/diff/diff-numeric-expr.txt b/llvm/test/FileCheck/diff/diff-numeric-expr.txt
deleted file mode 100644
index 36cfe98bea0d0..0000000000000
--- a/llvm/test/FileCheck/diff/diff-numeric-expr.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-BASE: 10
-NEXT: 12
-
-;--- check.txt
-; CHECK: BASE: [[#VAL:]]
-; CHECK: NEXT: [[#VAL+1]]
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: BASE: 10
-; DIFF-NEXT: -NEXT: 11
-; DIFF-NEXT: +NEXT: 12
diff --git a/llvm/test/FileCheck/diff/diff-stress-large-input.txt b/llvm/test/FileCheck/diff/diff-stress-large-input.txt
index 2e411702f8aa3..68488d4b39a13 100644
--- a/llvm/test/FileCheck/diff/diff-stress-large-input.txt
+++ b/llvm/test/FileCheck/diff/diff-stress-large-input.txt
@@ -1,5 +1,5 @@
; RUN: split-file %s %t
-; RUN: python3 -c "for i in range(5000): print(f'DATA_{i}')" > %t/input.txt
+; RUN: %python -c "for i in range(5000): print(f'DATA_{i}')" > %t/input.txt
; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
; RUN: | FileCheck %s --check-prefix=DIFF
diff --git a/llvm/test/FileCheck/diff/diff-substitutions.txt b/llvm/test/FileCheck/diff/diff-substitutions.txt
deleted file mode 100644
index 11c8a68484f1f..0000000000000
--- a/llvm/test/FileCheck/diff/diff-substitutions.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
-; RUN: | FileCheck %s --check-prefix=SUBST
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff-no-substitutions 2>&1 \
-; RUN: | FileCheck %s --check-prefix=RAW
-
-;--- input.txt
-START 42
-FAIL 100
-
-;--- check.txt
-; CHECK: START [[VAL:[0-9]+]]
-; CHECK: FAIL [[VAL]]
-
-; SUBST: @@ -[[#]] +2 @@
-; SUBST-NEXT: START 42
-; SUBST-NEXT: -FAIL 42
-; SUBST-NEXT: +FAIL 100
-
-; RAW: @@ -[[#]] +2 @@
-; RAW-NEXT: START 42
-; RAW-NEXT: -FAIL {{\[\[VAL\]\]}}
-; RAW-NEXT: +FAIL 100
diff --git a/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt b/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
deleted file mode 100644
index 4e172a4f28789..0000000000000
--- a/llvm/test/FileCheck/diff/diff-variable-chain-subs.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-ID: 0x100
-TYPE: apple
-USE: 0x100 (orange)
-
-;--- check.txt
-; CHECK: ID: [[ID:0x[0-9a-f]+]]
-; CHECK: TYPE: [[TYPE:[a-z]+]]
-; CHECK: USE: [[ID]] ([[TYPE]])
-
-; DIFF: @@ -[[#]] +[[#]] @@
-; DIFF-NEXT: TYPE: apple
-; DIFF-NEXT: -USE: 0x100 (apple)
-; DIFF-NEXT: +USE: 0x100 (orange)
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index b1433257a8661..210e2f82ba990 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -116,12 +116,7 @@ static cl::opt<bool> VerboseVerbose(
static cl::opt<DiffFormatType> DiffMode(
"diff", cl::desc("Show mismatches using a diff-style format.\n"),
cl::values(clEnumValN(Standard, "standard", "Outputs a Standard diff"),
- clEnumValN(Split, "split", "Outputs a Split diff"),
- clEnumValN(Unified, "unidiff", "Outputs a Unified diff"),
- clEnumValN(SplitNoSubstitution, "split-no-substitutions",
- "Outputs a Split diff with no substitutions"),
- clEnumValN(UnifiedNoSubstitution, "unidiff-no-substitutions",
- "Outputs a Unified diff with no substitutions")));
+ clEnumValN(Unified, "unidiff", "Outputs a Unified diff")));
// The order of DumpInputValue members affects their precedence, as documented
// for -dump-input below.
>From b29835402896c306a288ce52159c54c0baab5fe8 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 12:05:17 +0530
Subject: [PATCH 06/14] simplify code [not for review]
Simplify CheckInput [Not for review]
---
llvm/lib/FileCheck/FileCheck.cpp | 93 ++++++++++++++------------------
1 file changed, 41 insertions(+), 52 deletions(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 63f5226f47483..b343a47bdadf9 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2827,31 +2827,30 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
// 3. Direct pointer resolution via SourceMgr.
SMLoc InputLoc = SMLoc::getFromPointer(CheckRegion.data());
+ unsigned ActualLineNo = OverwriteActualLine;
-unsigned ActualLineNo = OverwriteActualLine;
+ if (ActualLineNo == 0) {
+ bool FoundFuzzy = false;
-if (ActualLineNo == 0) {
- bool FoundFuzzy = false;
-
- if (Diags) {
- for (const auto &D : llvm::reverse(*Diags)) {
- if (D.CheckLoc == PatternLoc &&
- D.MatchTy == FileCheckDiag::MatchFuzzy) {
- ActualLineNo = D.InputStartLine;
- FoundFuzzy = true;
- break;
+ if (Diags) {
+ for (const auto &D : llvm::reverse(*Diags)) {
+ if (D.CheckLoc == PatternLoc &&
+ D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ ActualLineNo = D.InputStartLine;
+ FoundFuzzy = true;
+ break;
+ }
}
}
- }
- // If no diagnostic match was found, calculate the line number directly
- // from the InputLoc pointer using the SourceManager.
- if (ActualLineNo == 0)
- ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+ // If no Fuzzy match was found, calculate the line number directly
+ // from the InputLoc pointer using the SourceManager.
+ if (ActualLineNo == 0)
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
- // if we are at an empty line, usually the relevant context is the line just
- // before it.
- if (ActualLine.empty() && ActualLineNo > 1)
+ // if we are at an empty line (and not from fuzzy), usually the relevant
+ // context is the line just before it.
+ if (!FoundFuzzy && ActualLine.empty() && ActualLineNo > 1)
ActualLineNo--;
}
@@ -2971,6 +2970,7 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
bool ChecksFailed = false;
unsigned TotalMismatches = 0;
bool HeaderPrinted = false;
+ bool IsDiffMode = Req.DiffMode != DiffFormatType::Standard;
auto &OS = llvm::errs();
unsigned i = 0, j = 0, e = CheckStrings.size();
@@ -3004,49 +3004,38 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
if (i != 0 && Req.EnableVarScope)
PatternContext->clearLocalVars();
+ // Check each string within the scanned region, including a second check
+ // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
for (; i != j; ++i) {
const FileCheckString &CheckStr = CheckStrings[i];
- // Check each string within the scanned region, including a second check
- // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
- size_t MatchLen = 0;
- size_t MatchPos = StringRef::npos;
- // Determine if the pattern requires strict adjacency (CHECK-NEXT/EMPTY).
bool IsStrict = CheckStr.Pat.getCheckTy() == Check::CheckNext ||
CheckStr.Pat.getCheckTy() == Check::CheckEmpty;
+ bool AllowSearch = Req.DiffMode != DiffFormatType::Standard && !IsStrict;
- if (Req.DiffMode != DiffFormatType::Standard) {
- // Try to match the pattern (Search only if not strict)
- MatchPos =
- CheckStr.Check(SM, CheckRegion, !IsStrict, MatchLen, Req, Diags);
- } else {
- MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
- }
+ // Match the pattern and return its position within the current region.
+ // (Searches only if not strict and doing diff)
+ size_t MatchLen = 0;
+ size_t MatchPos =
+ CheckStr.Check(SM, CheckRegion, AllowSearch, MatchLen, Req, Diags);
- if (Req.DiffMode != DiffFormatType::Standard) {
- if (MatchPos == StringRef::npos) {
- // Case 1: No match at all. Always diff.
+ // Handle failure
+ if (MatchPos == StringRef::npos) {
+ if (IsDiffMode) {
handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
HeaderPrinted, TotalMismatches);
- ChecksFailed = true;
- i = j;
- break;
- } else if (IsStrict && MatchPos > 0) {
- // Case 2: Match found, but is it "Strictly" next?
- // Check if the skipped characters are JUST whitespace/newlines.
- StringRef Skipped = CheckRegion.slice(0, MatchPos);
- if (!Skipped.trim().empty()) {
- // There is actual text (noise) between the last match and this one.
- handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
- HeaderPrinted, TotalMismatches);
- ChecksFailed = true;
- i = j;
- break;
- }
}
- } else {
- // Standard Mode Logic
- if (MatchPos == StringRef::npos) {
+ ChecksFailed = true;
+ i = j;
+ break;
+ }
+
+ // Extra strict check (only in diff mode)
+ if (IsDiffMode && IsStrict && MatchPos > 0) {
+ StringRef Skipped = CheckRegion.slice(0, MatchPos);
+ if (!Skipped.trim().empty()) {
+ handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
+ HeaderPrinted, TotalMismatches);
ChecksFailed = true;
i = j;
break;
>From 943cc747d746fd8979ccdd61e937c178eafc9ad6 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 14:10:26 +0530
Subject: [PATCH 07/14] simplify handleDiffFailure [not for review)
---
llvm/lib/FileCheck/FileCheck.cpp | 134 ++++++------------
llvm/test/FileCheck/diff/diff-empty-check.txt | 19 +++
.../FileCheck/diff/diff-label-boundary.txt | 2 +-
llvm/test/FileCheck/diff/diff-label-next.txt | 2 +-
...ync-overlap.txt => diff-multi-failres.txt} | 0
...ultihunk.txt => diff-simple-multihunk.txt} | 0
6 files changed, 61 insertions(+), 96 deletions(-)
create mode 100644 llvm/test/FileCheck/diff/diff-empty-check.txt
rename llvm/test/FileCheck/diff/{diff-resync-overlap.txt => diff-multi-failres.txt} (100%)
rename llvm/test/FileCheck/diff/{diff-multihunk.txt => diff-simple-multihunk.txt} (100%)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index b343a47bdadf9..f054a8c58737b 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2792,7 +2792,7 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
OS << "-" << ExpectedText << "\n";
OS.changeColor(raw_ostream::GREEN);
- OS << "+" << ActualLine << "\n";
+ OS << "+" << ActualLine.ltrim() << "\n";
OS.resetColor();
// After Context
@@ -2822,27 +2822,12 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
ExpectedText = FullExpectedLine.str();
// Resolve the Actual (Input) line number.
- // Priority: 1. OverwriteActualLine (explicit override)
- // 2. Fuzzy Match Diag (where FileCheck 'thinks' the line was)
- // 3. Direct pointer resolution via SourceMgr.
+ // Priority: 1. OverwriteActualLine (Found via Fuzzy match)
+ // 2. Direct pointer resolution via SourceMgr.
SMLoc InputLoc = SMLoc::getFromPointer(CheckRegion.data());
unsigned ActualLineNo = OverwriteActualLine;
- if (ActualLineNo == 0) {
- bool FoundFuzzy = false;
-
- if (Diags) {
- for (const auto &D : llvm::reverse(*Diags)) {
- if (D.CheckLoc == PatternLoc &&
- D.MatchTy == FileCheckDiag::MatchFuzzy) {
- ActualLineNo = D.InputStartLine;
- FoundFuzzy = true;
- break;
- }
- }
- }
-
// If no Fuzzy match was found, calculate the line number directly
// from the InputLoc pointer using the SourceManager.
if (ActualLineNo == 0)
@@ -2850,47 +2835,35 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
// if we are at an empty line (and not from fuzzy), usually the relevant
// context is the line just before it.
- if (!FoundFuzzy && ActualLine.empty() && ActualLineNo > 1)
+ if (ActualLine.empty() && ActualLineNo > 1)
ActualLineNo--;
- }
- // Gather contextual diff to print (a line above and a line below).
- unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
- DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
+ // Gather contextual diff to print (a line above and a line below).
+ unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
+ DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
- renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
- Context, OverwriteActualLine);
+ renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
+ Context, OverwriteActualLine);
- llvm::errs() << "\n";
- return true;
+ llvm::errs() << "\n";
+ return true;
}
-/// Handles a mismatch by attempting to resynchronize the pattern with the
-/// input.
-///
-/// When a pattern fails to match at the current location, this function
-/// explores several recovery strategies:
-/// 1. Stray Line Detection: Check if the pattern matches perfectly on the very
-/// next line (suggesting an unexpected line was inserted).
-/// 2. Search/Resync: Search forward in the region to find a perfect match
-/// later on (suggesting a block of unexpected code).
-/// 3. Fuzzy Matching: Use existing diagnostics to find "near misses" (typos)
-/// on the current or nearby lines.
-///
-/// It then calls \c renderDiff via \c printDiff and advances \p CheckRegion
-/// to the appropriate recovery point
+// Report the mismatch on the current line and advance to the next line.
static bool handleDiffFailure(const FileCheckString &CheckStr,
StringRef &CheckRegion, SourceMgr &SM,
FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags,
raw_ostream &OS, bool &HeaderPrinted,
unsigned &TotalMismatches) {
+ // Print headers once per file.
if (!HeaderPrinted) {
StringRef CheckFile =
SM.getMemoryBuffer(SM.getMainFileID())->getBufferIdentifier();
unsigned InputBufID =
SM.FindBufferContainingLoc(SMLoc::getFromPointer(CheckRegion.data()));
StringRef InputFile = SM.getMemoryBuffer(InputBufID)->getBufferIdentifier();
+
OS.changeColor(raw_ostream::WHITE, true);
OS << "--- " << CheckFile << "\n";
OS << "+++ " << InputFile << "\n";
@@ -2898,66 +2871,42 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
HeaderPrinted = true;
}
+ // Identify the line that failed to match.
CheckRegion = CheckRegion.ltrim("\n\r");
-
+ size_t EOL = CheckRegion.find('\n');
SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
- unsigned CurrentLineNo = 0;
- if (!CheckRegion.empty())
- CurrentLineNo = SM.getLineAndColumn(CurrentLoc).first;
- size_t EOL = CheckRegion.find('\n');
- StringRef MismatchLine =
- (EOL == StringRef::npos) ? CheckRegion : CheckRegion.substr(0, EOL);
-
- // Try to find where this pattern actually appears next
- std::vector<FileCheckDiag> ResyncDiags;
- size_t ResyncMatchLen = 0;
- size_t ResyncPos = CheckStr.Check(SM, CheckRegion, /*Search=*/true,
- ResyncMatchLen, Req, &ResyncDiags);
-
- StringRef TargetLine = MismatchLine;
- unsigned TargetLineNo = CurrentLineNo;
- bool AdvanceToResync = false;
-
- if (ResyncPos == 0) {
- // Perfect match right here (usually triggered by CHECK-NEXT noise)
- TargetLine = MismatchLine;
- } else if (ResyncPos != StringRef::npos) {
- // Found it further down. Is it just one line away? (Stray line case)
- size_t NextLinePos = CheckRegion.find('\n');
- if (NextLinePos != StringRef::npos && ResyncPos == NextLinePos + 1) {
- // This is a stray line. Diff the expected vs the stray line, then skip
- // stray.
- TargetLine = MismatchLine;
- AdvanceToResync = false;
- } else if (CheckStr.Pat.getCheckTy() != llvm::Check::CheckNext) {
- // Regular CHECK: skip the "noise" and sync to the match
- SMLoc MatchLoc = SMLoc::getFromPointer(CheckRegion.data() + ResyncPos);
- TargetLineNo = SM.getLineAndColumn(MatchLoc).first;
- TargetLine = CheckRegion.substr(ResyncPos).split('\n').first;
- AdvanceToResync = true;
- }
- } else {
- // Fallback to Fuzzy matching if no perfect match exists
- for (const auto &D : llvm::reverse(ResyncDiags)) {
- if (D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ StringRef TargetLine;
+ unsigned TargetLineNo = 0;
+
+ // Check if the existing diagnostics already found a fuzzy match.
+ if (Diags) {
+ for (const auto &D : llvm::reverse(*Diags)) {
+ if (D.CheckLoc == CheckStr.Pat.getLoc() &&
+ D.MatchTy == FileCheckDiag::MatchFuzzy) {
TargetLineNo = D.InputStartLine;
- SMLoc FuzzyLoc = SM.FindLocForLineAndColumn(
- SM.FindBufferContainingLoc(CurrentLoc), TargetLineNo, 1);
- TargetLine = StringRef(FuzzyLoc.getPointer()).split('\n').first.trim();
+ // Get the actual text of that fuzzy match from the SourceMgr
+ unsigned BufID = SM.FindBufferContainingLoc(CurrentLoc);
+ SMLoc FuzzyLoc = SM.FindLocForLineAndColumn(BufID, TargetLineNo, 1);
+ TargetLine = StringRef(FuzzyLoc.getPointer()).split('\n').first;
break;
}
}
}
- printDiff(Req.DiffMode, CheckStr, TargetLine, SM, Req, &ResyncDiags,
- TargetLineNo);
+ // If no fuzzy match was found by the engine, just use the next line.
+ if (TargetLine.empty()) {
+ TargetLine =
+ (EOL == StringRef::npos) ? CheckRegion : CheckRegion.substr(0, EOL);
+ TargetLineNo = SM.getLineAndColumn(CurrentLoc).first;
+ }
+
+ // Render the diff for this specific line.
+ printDiff(Req.DiffMode, CheckStr, TargetLine, SM, Req, Diags, TargetLineNo);
TotalMismatches++;
- // Updates CheckRegion to advance the search pointer past the error.
- if (AdvanceToResync && ResyncPos != StringRef::npos)
- CheckRegion = CheckRegion.substr(ResyncPos + ResyncMatchLen);
- else if (EOL != StringRef::npos)
+ // Advance CheckRegion past the current line to recover for the next CHECK.
+ if (EOL != StringRef::npos)
CheckRegion = CheckRegion.substr(EOL + 1);
else
CheckRegion = "";
@@ -3011,13 +2960,10 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
bool IsStrict = CheckStr.Pat.getCheckTy() == Check::CheckNext ||
CheckStr.Pat.getCheckTy() == Check::CheckEmpty;
- bool AllowSearch = Req.DiffMode != DiffFormatType::Standard && !IsStrict;
- // Match the pattern and return its position within the current region.
- // (Searches only if not strict and doing diff)
size_t MatchLen = 0;
size_t MatchPos =
- CheckStr.Check(SM, CheckRegion, AllowSearch, MatchLen, Req, Diags);
+ CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
// Handle failure
if (MatchPos == StringRef::npos) {
diff --git a/llvm/test/FileCheck/diff/diff-empty-check.txt b/llvm/test/FileCheck/diff/diff-empty-check.txt
new file mode 100644
index 0000000000000..bba0b9993627d
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-empty-check.txt
@@ -0,0 +1,19 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+START_TRANS
+ [LOG] Transaction initiated...
+END_TRANS
+
+;--- check.txt
+; CHECK: START_TRANS
+; CHECK-EMPTY:
+; CHECK: END_TRANS
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: START_TRANS
+; DIFF-NEXT: -
+; DIFF-NEXT: +[LOG] Transaction initiated...
+; DIFF-NEXT: END_TRANS
diff --git a/llvm/test/FileCheck/diff/diff-label-boundary.txt b/llvm/test/FileCheck/diff/diff-label-boundary.txt
index 6381e2a71f82e..cb9595f760149 100644
--- a/llvm/test/FileCheck/diff/diff-label-boundary.txt
+++ b/llvm/test/FileCheck/diff/diff-label-boundary.txt
@@ -1,5 +1,5 @@
; RUN: split-file %s %t
-; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
; RUN: | FileCheck %s --check-prefix=DIFF
;--- input.txt
diff --git a/llvm/test/FileCheck/diff/diff-label-next.txt b/llvm/test/FileCheck/diff/diff-label-next.txt
index f2ea934376d6e..240cd14596da1 100644
--- a/llvm/test/FileCheck/diff/diff-label-next.txt
+++ b/llvm/test/FileCheck/diff/diff-label-next.txt
@@ -1,5 +1,5 @@
; RUN: split-file %s %t
-; RUN: not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
; RUN: | FileCheck %s --check-prefix=DIFF
;--- input.txt
diff --git a/llvm/test/FileCheck/diff/diff-resync-overlap.txt b/llvm/test/FileCheck/diff/diff-multi-failres.txt
similarity index 100%
rename from llvm/test/FileCheck/diff/diff-resync-overlap.txt
rename to llvm/test/FileCheck/diff/diff-multi-failres.txt
diff --git a/llvm/test/FileCheck/diff/diff-multihunk.txt b/llvm/test/FileCheck/diff/diff-simple-multihunk.txt
similarity index 100%
rename from llvm/test/FileCheck/diff/diff-multihunk.txt
rename to llvm/test/FileCheck/diff/diff-simple-multihunk.txt
>From d5453aa671d83e7134f9644f00113b818bc69178 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 15:36:49 +0530
Subject: [PATCH 08/14] sliced region [not for review]
---
llvm/lib/FileCheck/FileCheck.cpp | 17 ++++++----
llvm/test/FileCheck/diff/diff-multi-block.txt | 32 +++++++++++++++++++
2 files changed, 42 insertions(+), 7 deletions(-)
create mode 100644 llvm/test/FileCheck/diff/diff-multi-block.txt
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index f054a8c58737b..8fd8153311312 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2773,7 +2773,7 @@ static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
// Unified View: A standard top-to-bottom (-Expected / +Actual) format.
static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
unsigned ActualLineNo, StringRef ActualLine,
- const std::string &ExpectedText, const DiffContext &Ctx,
+ StringRef ExpectedText, const DiffContext &Ctx,
unsigned OverwriteActualLine) {
auto &OS = llvm::errs();
@@ -2816,10 +2816,7 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
SMLoc PatternLoc = CheckStr.Pat.getLoc();
unsigned ExpectedLineNo = SM.getLineAndColumn(PatternLoc).first;
const char *PatPtr = PatternLoc.getPointer();
- StringRef FullExpectedLine = StringRef(PatPtr).split('\n').first.trim();
-
- std::string ExpectedText;
- ExpectedText = FullExpectedLine.str();
+ StringRef ExpectedText = StringRef(PatPtr).split('\n').first.rtrim();
// Resolve the Actual (Input) line number.
// Priority: 1. OverwriteActualLine (Found via Fuzzy match)
@@ -2976,11 +2973,17 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
break;
}
- // Extra strict check (only in diff mode)
+ // Extra strict match (only in diff mode)
if (IsDiffMode && IsStrict && MatchPos > 0) {
StringRef Skipped = CheckRegion.slice(0, MatchPos);
if (!Skipped.trim().empty()) {
- handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
+ // Create a temporary view that starts with next new line.
+ size_t CurrentLineEnd = CheckRegion.find_first_of("\n\r");
+ StringRef NextLineRegion =
+ (CurrentLineEnd != StringRef::npos)
+ ? CheckRegion.drop_front(CurrentLineEnd + 1).ltrim(" \t\n\r")
+ : CheckRegion.ltrim(" \t\n\r");
+ handleDiffFailure(CheckStr, NextLineRegion, SM, Req, Diags, OS,
HeaderPrinted, TotalMismatches);
ChecksFailed = true;
i = j;
diff --git a/llvm/test/FileCheck/diff/diff-multi-block.txt b/llvm/test/FileCheck/diff/diff-multi-block.txt
new file mode 100644
index 0000000000000..2a53fb6fdd9c4
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-multi-block.txt
@@ -0,0 +1,32 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+define void @func_a() {
+ %result = add i32 %1, %2
+ ret void
+}
+define void @func_b() {
+ %val = load i32, ptr %p
+ store i32 %val, ptr %q
+ ret void
+}
+
+;--- check.txt
+; CHECK-LABEL: define void @func_a()
+; CHECK-NEXT: %result = mul i32 %1, %2
+; CHECK-LABEL: define void @func_b()
+; CHECK-NEXT: store i32 %val, ptr %q
+
+; DIFF: --- {{.*}}check.txt
+; DIFF-NEXT: +++ {{.*}}input.txt
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: define void @func_a() {
+; DIFF-NEXT: -%result = mul i32 %1, %2
+; DIFF-NEXT: +%result = add i32 %1, %2
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: define void @func_b() {
+; DIFF-NEXT: -store i32 %val, ptr %q
+; DIFF-NEXT: +%val = load i32, ptr %p
+; DIFF: FileCheck: Found 2 unique textual mismatches.
>From 0acc07000a13f66e62e7cf34f29ae8af215ad4ac Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 16:35:30 +0530
Subject: [PATCH 09/14] fixup! clang-format
---
llvm/lib/FileCheck/FileCheck.cpp | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 8fd8153311312..a354b9d383b5f 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2825,25 +2825,25 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
unsigned ActualLineNo = OverwriteActualLine;
- // If no Fuzzy match was found, calculate the line number directly
- // from the InputLoc pointer using the SourceManager.
- if (ActualLineNo == 0)
- ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+ // If no Fuzzy match was found, calculate the line number directly
+ // from the InputLoc pointer using the SourceManager.
+ if (ActualLineNo == 0)
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
- // if we are at an empty line (and not from fuzzy), usually the relevant
- // context is the line just before it.
- if (ActualLine.empty() && ActualLineNo > 1)
- ActualLineNo--;
+ // if we are at an empty line (and not from fuzzy), usually the relevant
+ // context is the line just before it.
+ if (ActualLine.empty() && ActualLineNo > 1)
+ ActualLineNo--;
- // Gather contextual diff to print (a line above and a line below).
- unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
- DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
+ // Gather contextual diff to print (a line above and a line below).
+ unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
+ DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
- renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
- Context, OverwriteActualLine);
+ renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
+ Context, OverwriteActualLine);
- llvm::errs() << "\n";
- return true;
+ llvm::errs() << "\n";
+ return true;
}
// Report the mismatch on the current line and advance to the next line.
>From 1ff91975f562fb1cbe1c2ba4f124a246053ef0e7 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 16:55:38 +0530
Subject: [PATCH 10/14] added multi-mismatch test case
---
.../FileCheck/diff/diff-multi-mismatch.txt | 36 +++++++++++++++++++
.../FileCheck/diff/diff-simple-multihunk.txt | 18 ----------
2 files changed, 36 insertions(+), 18 deletions(-)
create mode 100644 llvm/test/FileCheck/diff/diff-multi-mismatch.txt
delete mode 100644 llvm/test/FileCheck/diff/diff-simple-multihunk.txt
diff --git a/llvm/test/FileCheck/diff/diff-multi-mismatch.txt b/llvm/test/FileCheck/diff/diff-multi-mismatch.txt
new file mode 100644
index 0000000000000..ace3d7cd093bd
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-multi-mismatch.txt
@@ -0,0 +1,36 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+BLOCK1_FAIL
+MATCH 1
+MATCH 2
+MATCH 3
+MATCH 4
+MATCH 5
+MATCH 6
+MATCH 7
+BLOCK2_FAIL
+
+;--- check.txt
+; CHECK: BLOCK1_PASS
+; CHECK: MATCH 1
+; CHECK: MATCH 2
+; CHECK: MATCH 3
+; CHECK: MATCH 4
+; CHECK: MATCH 5
+; CHECK: MATCH 6
+; CHECK: MATCH 7
+; CHECK: BLOCK2_PASS
+
+; First Hunk
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: -BLOCK1_PASS
+; DIFF-NEXT: +BLOCK1_FAIL
+; DIFF-NEXT: MATCH 1
+
+; FIXME: We should continue to report failure even after first.
+; Second Hunk
+; DIFF-NOT: -BLOCK2_PASS
+; DIFF-NOT: +BLOCK2_FAIL
diff --git a/llvm/test/FileCheck/diff/diff-simple-multihunk.txt b/llvm/test/FileCheck/diff/diff-simple-multihunk.txt
deleted file mode 100644
index aa78446b0604d..0000000000000
--- a/llvm/test/FileCheck/diff/diff-simple-multihunk.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-; RUN: split-file %s %t
-; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
-; RUN: | FileCheck %s --check-prefix=DIFF
-
-;--- input.txt
-BLOCK1_FAIL
-KEEP_THIS
-BLOCK2_FAIL
-
-;--- check.txt
-; CHECK: BLOCK1_PASS
-; CHECK: KEEP_THIS
-; CHECK: BLOCK2_PASS
-
-; DIFF: @@ -[[#]] +1 @@
-; DIFF-NEXT: -BLOCK1_PASS
-; DIFF-NEXT: +BLOCK1_FAIL
-; DIFF-NEXT: KEEP_THIS
>From eea756688670905b53e050b4535d6ce7424752dc Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Fri, 27 Mar 2026 19:45:25 +0530
Subject: [PATCH 11/14] minor edits after rereviewing
---
llvm/lib/FileCheck/FileCheck.cpp | 42 +++++++++++++-------------------
1 file changed, 17 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index a354b9d383b5f..3cb1f71053e3c 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2771,10 +2771,9 @@ static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
//
// Supported mode:
// Unified View: A standard top-to-bottom (-Expected / +Actual) format.
-static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
- unsigned ActualLineNo, StringRef ActualLine,
- StringRef ExpectedText, const DiffContext &Ctx,
- unsigned OverwriteActualLine) {
+static void renderDiff(unsigned ExpectedLineNo, unsigned ActualLineNo,
+ StringRef ExpectedLine, StringRef ActualLine,
+ const DiffContext &Ctx) {
auto &OS = llvm::errs();
// Header
@@ -2789,7 +2788,7 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
// Mismatch
OS.changeColor(raw_ostream::RED);
- OS << "-" << ExpectedText << "\n";
+ OS << "-" << ExpectedLine << "\n";
OS.changeColor(raw_ostream::GREEN);
OS << "+" << ActualLine.ltrim() << "\n";
@@ -2803,25 +2802,18 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
// Prepares and prints a visual comparison between a CHECK pattern and the
// input.
-static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
- StringRef CheckRegion, SourceMgr &SM,
- const FileCheckRequest &Req,
- std::vector<FileCheckDiag> *Diags,
+static bool printDiff(const FileCheckString &CheckStr, StringRef ActualLine,
+ SourceMgr &SM, std::vector<FileCheckDiag> *Diags,
unsigned OverwriteActualLine = 0) {
- StringRef ActualLine = CheckRegion.split('\n').first;
- if (CheckRegion.empty()) {
- ActualLine = "";
- }
-
SMLoc PatternLoc = CheckStr.Pat.getLoc();
unsigned ExpectedLineNo = SM.getLineAndColumn(PatternLoc).first;
const char *PatPtr = PatternLoc.getPointer();
- StringRef ExpectedText = StringRef(PatPtr).split('\n').first.rtrim();
+ StringRef ExpectedLine = StringRef(PatPtr).split('\n').first.rtrim();
// Resolve the Actual (Input) line number.
// Priority: 1. OverwriteActualLine (Found via Fuzzy match)
// 2. Direct pointer resolution via SourceMgr.
- SMLoc InputLoc = SMLoc::getFromPointer(CheckRegion.data());
+ SMLoc InputLoc = SMLoc::getFromPointer(ActualLine.data());
unsigned ActualLineNo = OverwriteActualLine;
@@ -2839,8 +2831,7 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
- renderDiff(Mode, ExpectedLineNo, ActualLineNo, ActualLine, ExpectedText,
- Context, OverwriteActualLine);
+ renderDiff(ExpectedLineNo, ActualLineNo, ExpectedLine, ActualLine, Context);
llvm::errs() << "\n";
return true;
@@ -2849,7 +2840,6 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
// Report the mismatch on the current line and advance to the next line.
static bool handleDiffFailure(const FileCheckString &CheckStr,
StringRef &CheckRegion, SourceMgr &SM,
- FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags,
raw_ostream &OS, bool &HeaderPrinted,
unsigned &TotalMismatches) {
@@ -2899,7 +2889,7 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
}
// Render the diff for this specific line.
- printDiff(Req.DiffMode, CheckStr, TargetLine, SM, Req, Diags, TargetLineNo);
+ printDiff(CheckStr, TargetLine, SM, Diags, TargetLineNo);
TotalMismatches++;
// Advance CheckRegion past the current line to recover for the next CHECK.
@@ -2965,15 +2955,16 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
// Handle failure
if (MatchPos == StringRef::npos) {
if (IsDiffMode) {
- handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
- HeaderPrinted, TotalMismatches);
+ handleDiffFailure(CheckStr, CheckRegion, SM, Diags, OS, HeaderPrinted,
+ TotalMismatches);
}
ChecksFailed = true;
i = j;
break;
}
-
- // Extra strict match (only in diff mode)
+ // For strict checks in Diff Mode, any skipped non-whitespace text is
+ // treated as a stray line. Even if a match is found later, we report
+ // the gap as a mismatch.
if (IsDiffMode && IsStrict && MatchPos > 0) {
StringRef Skipped = CheckRegion.slice(0, MatchPos);
if (!Skipped.trim().empty()) {
@@ -2983,13 +2974,14 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
(CurrentLineEnd != StringRef::npos)
? CheckRegion.drop_front(CurrentLineEnd + 1).ltrim(" \t\n\r")
: CheckRegion.ltrim(" \t\n\r");
- handleDiffFailure(CheckStr, NextLineRegion, SM, Req, Diags, OS,
+ handleDiffFailure(CheckStr, NextLineRegion, SM, Diags, OS,
HeaderPrinted, TotalMismatches);
ChecksFailed = true;
i = j;
break;
}
}
+
CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
}
>From 2c375f87ad80351149db960513e5a20d35173102 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Sat, 28 Mar 2026 01:04:34 +0530
Subject: [PATCH 12/14] fixup! add split-file dep for check-llvm-filecheck
---
llvm/test/CMakeLists.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index 34213cd18e38a..63d64ffc7b7fc 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -66,6 +66,7 @@ set(LLVM_TEST_DEPENDS_COMMON
count
llvm-config
not
+ split-file
)
set(LLVM_TEST_DEPENDS
>From 7f0e38b9a4cc53e90e06bb9188e027c3b5f57104 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Tue, 31 Mar 2026 00:12:23 +0530
Subject: [PATCH 13/14] address review comments
---
llvm/lib/FileCheck/FileCheck.cpp | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 3cb1f71053e3c..5ef780c01fcba 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -1289,9 +1289,8 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
if (Diags)
Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy,
SMRange(Range.Start, Range.Start), OS.str());
- else {
- if (Req.DiffMode == DiffFormatType::Standard)
- SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
+ else if (Req.DiffMode == DiffFormatType::Standard) {
+ SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
}
}
}
@@ -2753,14 +2752,11 @@ static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
if (!LineLoc.isValid())
return "";
- const char *Ptr = LineLoc.getPointer();
- const char *End = BufText.end();
- const char *LineEnd = Ptr;
-
- while (LineEnd < End && *LineEnd != '\n' && *LineEnd != '\r')
- LineEnd++;
-
- return StringRef(Ptr, LineEnd - Ptr).trim();
+ StringRef FromLineStart(LineLoc.getPointer(),
+ BufText.end() - LineLoc.getPointer());
+ return FromLineStart
+ .take_while([](char C) { return C != '\n' && C != '\r'; })
+ .trim();
};
return {getLineText(LineNo), getLineText(LineNo - 1),
@@ -2883,8 +2879,7 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
// If no fuzzy match was found by the engine, just use the next line.
if (TargetLine.empty()) {
- TargetLine =
- (EOL == StringRef::npos) ? CheckRegion : CheckRegion.substr(0, EOL);
+ TargetLine = CheckRegion.substr(0, EOL);
TargetLineNo = SM.getLineAndColumn(CurrentLoc).first;
}
@@ -2981,7 +2976,6 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
break;
}
}
-
CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
}
>From 6c08c4ec4a46a8a02ba528136f614d85fc12a0c0 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Tue, 31 Mar 2026 12:56:59 +0530
Subject: [PATCH 14/14] address review comment for whitespace lines
---
llvm/lib/FileCheck/FileCheck.cpp | 26 ++++++++-----------
llvm/test/FileCheck/diff/diff-blank-stray.txt | 18 +++++++++++++
2 files changed, 29 insertions(+), 15 deletions(-)
create mode 100644 llvm/test/FileCheck/diff/diff-blank-stray.txt
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 5ef780c01fcba..b7cd6f87c7f20 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2855,7 +2855,6 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
}
// Identify the line that failed to match.
- CheckRegion = CheckRegion.ltrim("\n\r");
size_t EOL = CheckRegion.find('\n');
SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
@@ -2961,20 +2960,17 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
// treated as a stray line. Even if a match is found later, we report
// the gap as a mismatch.
if (IsDiffMode && IsStrict && MatchPos > 0) {
- StringRef Skipped = CheckRegion.slice(0, MatchPos);
- if (!Skipped.trim().empty()) {
- // Create a temporary view that starts with next new line.
- size_t CurrentLineEnd = CheckRegion.find_first_of("\n\r");
- StringRef NextLineRegion =
- (CurrentLineEnd != StringRef::npos)
- ? CheckRegion.drop_front(CurrentLineEnd + 1).ltrim(" \t\n\r")
- : CheckRegion.ltrim(" \t\n\r");
- handleDiffFailure(CheckStr, NextLineRegion, SM, Diags, OS,
- HeaderPrinted, TotalMismatches);
- ChecksFailed = true;
- i = j;
- break;
- }
+ // Create a temporary view that starts with next new line.
+ size_t CurrentLineEnd = CheckRegion.find_first_of("\n\r");
+ StringRef NextLineRegion =
+ (CurrentLineEnd != StringRef::npos)
+ ? CheckRegion.drop_front(CurrentLineEnd + 1)
+ : CheckRegion;
+ handleDiffFailure(CheckStr, NextLineRegion, SM, Diags, OS,
+ HeaderPrinted, TotalMismatches);
+ ChecksFailed = true;
+ i = j;
+ break;
}
CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
}
diff --git a/llvm/test/FileCheck/diff/diff-blank-stray.txt b/llvm/test/FileCheck/diff/diff-blank-stray.txt
new file mode 100644
index 0000000000000..6065c74b66f40
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-blank-stray.txt
@@ -0,0 +1,18 @@
+; RUN: split-file %s %t
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=unidiff 2>&1 \
+; RUN: | FileCheck %s --check-prefix=DIFF
+
+;--- input.txt
+LINE1
+
+
+
+LINE2
+
+;--- check.txt
+; CHECK: LINE1
+; CHECK-NEXT: LINE2
+
+; DIFF: @@ -[[#]] +[[#]] @@
+; DIFF-NEXT: -LINE2
+; DIFF-NEXT: +
More information about the llvm-commits
mailing list