[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