[llvm] [FileCheck] Add variable substitution for diff option (PR #189273)

Shivam Gupta via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 18 04:01:51 PDT 2026


https://github.com/xgupta updated https://github.com/llvm/llvm-project/pull/189273

>From 86524fd6d8fe93020feee44e3998763afaaa3389 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Sat, 18 Apr 2026 16:17:24 +0530
Subject: [PATCH 1/2] [FileCheck] Add split view diff option for FileCheck

---
 llvm/docs/CommandGuide/FileCheck.rst          |   1 +
 llvm/include/llvm/FileCheck/FileCheck.h       |   2 +-
 llvm/lib/FileCheck/FileCheck.cpp              | 103 +++++++++++++++---
 .../FileCheck/diff/diff-label-fuzzy-match.txt |  19 +++-
 .../FileCheck/diff/diff-long-line-trunc.txt   |  12 ++
 .../diff/diff-resync-after-noise.txt          |  19 +++-
 llvm/utils/FileCheck/FileCheck.cpp            |   3 +-
 7 files changed, 128 insertions(+), 31 deletions(-)
 create mode 100644 llvm/test/FileCheck/diff/diff-long-line-trunc.txt

diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index d99cbbdd9f36f..ec787db7fbf02 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -117,6 +117,7 @@ and from the command line.
 
   * ``none`` – Use the standard FileCheck diagnostic messages.
   * ``unidiff`` – Display mismatches using a unified diff format.
+  * ``split`` – Display mismatches using a side-by-side split diff view.
 
 .. option:: --dump-input <value>
 
diff --git a/llvm/include/llvm/FileCheck/FileCheck.h b/llvm/include/llvm/FileCheck/FileCheck.h
index 7896a942a3466..84d006d616ddf 100644
--- a/llvm/include/llvm/FileCheck/FileCheck.h
+++ b/llvm/include/llvm/FileCheck/FileCheck.h
@@ -28,7 +28,7 @@ class SourceMgr;
 template <typename T> class SmallVectorImpl;
 
 // Diff the output on failures.
-enum DiffFormatType { None, Unified };
+enum DiffFormatType { None, Unified, Split };
 
 /// Contains info about various FileCheck options.
 struct FileCheckRequest {
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 307be0b6e2faf..dfa5235c53d8e 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2762,11 +2762,45 @@ static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
           getLineText(LineNo + 1)};
 }
 
+// Extracts a fixed-width "window" from string S, centered around DiffPos.
+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 (Start + Width < S.size() && View.size() > 3)
+    View.replace(View.size() - 3, 3, "...");
+
+  return View;
+}
+
 // Renders a diagnostic diff via llvm::errs().
-static void renderDiff(unsigned ExpectedLineNo, unsigned ActualLineNo,
-                       StringRef ExpectedLine, StringRef ActualLine,
-                       const DiffContext &Ctx) {
+static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
+                       unsigned ActualLineNo, StringRef ExpectedLine,
+                       StringRef ActualLine, const DiffContext &Ctx) {
   auto &OS = errs();
+  constexpr unsigned ColWidth = 45;
+  constexpr StringRef Sep = " | ";
+
+  // Find the index from where the expected and actual text diverge.
+  size_t DiffPos = 0;
+  size_t MinLen = std::min(ExpectedLine.size(), ActualLine.size());
+  while (DiffPos < MinLen && ExpectedLine[DiffPos] == ActualLine[DiffPos])
+    ++DiffPos;
+
+  auto GetView = [&](StringRef S) {
+    return getCenteredView(S, DiffPos, ColWidth - 1);
+  };
 
   // Header
   OS.changeColor(raw_ostream::CYAN);
@@ -2775,25 +2809,58 @@ static void renderDiff(unsigned ExpectedLineNo, unsigned ActualLineNo,
 
   // Before Context
   if (!Ctx.LineBefore.empty()) {
-    OS << " " << Ctx.LineBefore << "\n";
+    if (Mode == Split)
+      OS << "  " << GetView(Ctx.LineBefore) << Sep << GetView(Ctx.LineBefore)
+         << "\n";
+    else
+      OS << " " << Ctx.LineBefore << "\n";
   }
 
   // Mismatch
-  OS.changeColor(raw_ostream::RED);
-  OS << "-" << ExpectedLine << "\n";
+  if (Mode == Split) {
+    OS << "  ";
 
-  OS.changeColor(raw_ostream::GREEN);
-  OS << "+" << ActualLine.ltrim() << "\n";
-  OS.resetColor();
+    OS.changeColor(raw_ostream::RED);
+    OS << GetView(ExpectedLine);
+    OS.resetColor();
+
+    // Use a bold yellow '!' to signify that the text might match,
+    // but it was found on the wrong line.
+    bool IsMoved = (ExpectedLineNo != ActualLineNo);
+
+    if (IsMoved) {
+      OS.changeColor(raw_ostream::YELLOW, true);
+      OS << " ! ";
+    } else {
+      OS << " | ";
+    }
+    OS.resetColor();
+
+    OS.changeColor(raw_ostream::GREEN);
+    OS << StringRef(GetView(ActualLine)).ltrim() << "\n";
+    OS.resetColor();
+  } else {
+    OS.changeColor(raw_ostream::RED);
+    OS << "-" << ExpectedLine << "\n";
+
+    OS.changeColor(raw_ostream::GREEN);
+    OS << "+" << ActualLine.ltrim() << "\n";
+    OS.resetColor();
+  }
 
   // After Context
   if (!Ctx.LineAfter.empty()) {
-    OS << " " << Ctx.LineAfter << "\n";
+    if (Mode == Split)
+      OS << "  " << GetView(Ctx.LineAfter) << Sep << GetView(Ctx.LineAfter)
+         << "\n";
+    else
+      OS << " " << Ctx.LineAfter << "\n";
   }
 }
 
-static bool printDiff(const FileCheckString &CheckStr, StringRef ActualLine,
-                      SourceMgr &SM, std::vector<FileCheckDiag> *Diags,
+static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
+                      StringRef ActualLine, SourceMgr &SM,
+                      std::vector<FileCheckDiag> *Diags,
                       unsigned OverwriteActualLine = 0) {
   SMLoc PatternLoc = CheckStr.Pat.getLoc();
   unsigned ExpectedLineNo = SM.getLineAndColumn(PatternLoc).first;
@@ -2820,7 +2887,8 @@ static bool printDiff(const FileCheckString &CheckStr, StringRef ActualLine,
   unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
   DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
 
-  renderDiff(ExpectedLineNo, ActualLineNo, ExpectedLine, ActualLine, Context);
+  renderDiff(Mode, ExpectedLineNo, ActualLineNo, ExpectedLine, ActualLine,
+             Context);
 
   errs() << '\n';
   return true;
@@ -2829,6 +2897,7 @@ static bool printDiff(const FileCheckString &CheckStr, StringRef ActualLine,
 // 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) {
@@ -2874,7 +2943,7 @@ static bool handleDiffFailure(const FileCheckString &CheckStr,
     TargetLineNo = SM.getLineAndColumn(CurrentLoc).first;
   }
 
-  printDiff(CheckStr, TargetLine, SM, Diags, TargetLineNo);
+  printDiff(Req.DiffFormat, CheckStr, TargetLine, SM, Diags, TargetLineNo);
   TotalMismatches++;
 
   // Advance CheckRegion past the current line to recover for the next CHECK.
@@ -2940,8 +3009,8 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
       // Handle failure
       if (MatchPos == StringRef::npos) {
         if (IsDiffFormat) {
-          handleDiffFailure(CheckStr, CheckRegion, SM, Diags, OS, HeaderPrinted,
-                            TotalMismatches);
+          handleDiffFailure(CheckStr, CheckRegion, SM, Req, Diags, OS,
+                            HeaderPrinted, TotalMismatches);
         }
         ChecksFailed = true;
         i = j;
@@ -2957,7 +3026,7 @@ bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
             (CurrentLineEnd != StringRef::npos)
                 ? CheckRegion.drop_front(CurrentLineEnd + 1)
                 : CheckRegion;
-        handleDiffFailure(CheckStr, NextLineRegion, SM, Diags, OS,
+        handleDiffFailure(CheckStr, NextLineRegion, SM, Req, Diags, OS,
                           HeaderPrinted, TotalMismatches);
         ChecksFailed = true;
         i = j;
diff --git a/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt b/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
index a04f2e84ac70b..1a011a61032ca 100644
--- a/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
+++ b/llvm/test/FileCheck/diff/diff-label-fuzzy-match.txt
@@ -1,6 +1,8 @@
 ; 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
+; RUN:   | FileCheck %s --check-prefix=UNI
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=SPLIT
 
 ;--- input.txt
 define void @foo() {
@@ -11,8 +13,13 @@ define void @foo() {
 ; 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:  }
+; UNI:      @@ -[[#]] +[[#]] @@
+; UNI:      define void @foo() {
+; UNI-NEXT: -call void @work_test()
+; UNI-NEXT: +call void @work_task()
+; UNI-NEXT:  }
+
+; SPLIT:      @@ -[[#]] +[[#]] @@
+; SPLIT-NEXT: define void @foo() {              | define void @foo() {
+; SPLIT-NEXT: {{.*}}call void @work_test(){{.*}}|{{.*}}call void @work_task()
+; SPLIT-NEXT: }
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..f854ff3956864
--- /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:      @@ -1 +1 @@
+; DIFF-NEXT: {{.*}}...{{.*}}WINDOW_LOGIC_EXPECTED_VALUE{{.*}}|{{.*}}...{{.*}}WINDOW_LOGIC_ACTUAL_VALUE
diff --git a/llvm/test/FileCheck/diff/diff-resync-after-noise.txt b/llvm/test/FileCheck/diff/diff-resync-after-noise.txt
index 854ee188b2944..8fd4d73b9e83d 100644
--- a/llvm/test/FileCheck/diff/diff-resync-after-noise.txt
+++ b/llvm/test/FileCheck/diff/diff-resync-after-noise.txt
@@ -1,6 +1,8 @@
 ; 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
+; RUN:   | FileCheck %s --check-prefix=UNI
+; RUN: %ProtectFileCheckOutput not FileCheck %t/check.txt --input-file=%t/input.txt --diff=split 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=SPLIT
 
 ;--- input.txt
 HEADER
@@ -23,8 +25,13 @@ Noise6
 ; CHECK-NEXT: WRONG_NEXT
 ; CHECK:       END_OF_FUNCTION
 
-; DIFF:      @@ -[[#]] +[[#]] @@
-; DIFF-NEXT:  MATCH_ME
-; DIFF-NEXT: -WRONG_NEXT
-; DIFF-NEXT: +NEXT_LINE
-; DIFF-NEXT:  Noise5
+; UNI:      @@ -[[#]] +[[#]] @@
+; UNI-NEXT:  MATCH_ME
+; UNI-NEXT: -WRONG_NEXT
+; UNI-NEXT: +NEXT_LINE
+; UNI-NEXT:  Noise5
+
+; SPLIT:      @@ -[[#]] +[[#]] @@
+; SPLIT-NEXT: MATCH_ME              | MATCH_ME
+; SPLIT-NEXT: {{.*}}WRONG_NEXT{{.*}}!{{.*}}NEXT_LINE
+; SPLIT-NEXT: Noise5                | Noise5
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index f200e94becaa2..f315fda533931 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -118,7 +118,8 @@ static cl::opt<DiffFormatType> DiffFormat(
     cl::ValueOptional,
     cl::values(clEnumValN(None, "none", "Standard FileCheck diagnostics"),
                clEnumValN(Unified, "unidiff", "Outputs a Unified diff"),
-               clEnumValN(Unified, "", "")));
+               clEnumValN(Unified, "", ""),
+               clEnumValN(Split, "split", "Outputs a Split diff")));
 
 // The order of DumpInputValue members affects their precedence, as documented
 // for -dump-input below.

>From 9ba4bd7c9e8d796bdcdd91340569d651aae0b1a2 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Sat, 18 Apr 2026 16:31:05 +0530
Subject: [PATCH 2/2] [FileCheck] Add variable substitution for diff option

---
 llvm/docs/CommandGuide/FileCheck.rst          |  4 ++
 llvm/include/llvm/FileCheck/FileCheck.h       |  8 ++-
 llvm/lib/FileCheck/FileCheck.cpp              | 50 +++++++++++++++++--
 llvm/lib/FileCheck/FileCheckImpl.h            |  2 +
 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 ++++++
 .../FileCheck/diff/diff-substitutions.txt     | 23 +++++++++
 .../diff/diff-variable-chain-subs.txt         | 18 +++++++
 llvm/utils/FileCheck/FileCheck.cpp            |  6 ++-
 10 files changed, 153 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/FileCheck/diff/diff-multi-subs.txt
 create mode 100644 llvm/test/FileCheck/diff/diff-nested-vars-subs.txt
 create mode 100644 llvm/test/FileCheck/diff/diff-numeric-expr.txt
 create mode 100644 llvm/test/FileCheck/diff/diff-substitutions.txt
 create 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 ec787db7fbf02..4e6d8eb369295 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -118,6 +118,10 @@ and from the command line.
   * ``none`` – Use the standard FileCheck diagnostic messages.
   * ``unidiff`` – Display mismatches using a unified diff format.
   * ``split`` – Display mismatches using a side-by-side split diff view.
+  * ``unidiff-no-substitutions`` – Same as ``unidiff`` but shows the raw
+    pattern without applying variable substitutions.
+  * ``split-no-substitutions`` – Same as ``split`` 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 84d006d616ddf..446447aca7d3f 100644
--- a/llvm/include/llvm/FileCheck/FileCheck.h
+++ b/llvm/include/llvm/FileCheck/FileCheck.h
@@ -28,7 +28,13 @@ class SourceMgr;
 template <typename T> class SmallVectorImpl;
 
 // Diff the output on failures.
-enum DiffFormatType { None, Unified, Split };
+enum DiffFormatType {
+  None,
+  Unified,
+  Split,
+  SplitNoSubstitution,
+  UnifiedNoSubstitution
+};
 
 /// Contains info about various FileCheck options.
 struct FileCheckRequest {
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index dfa5235c53d8e..243ded6362234 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -2784,6 +2784,41 @@ static std::string getCenteredView(StringRef S, size_t DiffPos, size_t Width) {
   return View;
 }
 
+std::string Pattern::getSubstitutedRegex(StringRef PatternText) const {
+  std::string Result = PatternText.str();
+
+  // Iterate through substitutions and 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 via llvm::errs().
 static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
                        unsigned ActualLineNo, StringRef ExpectedLine,
@@ -2792,6 +2827,8 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
   constexpr unsigned ColWidth = 45;
   constexpr StringRef Sep = " | ";
 
+  bool IsSplit = (Mode == Split || Mode == SplitNoSubstitution);
+
   // Find the index from where the expected and actual text diverge.
   size_t DiffPos = 0;
   size_t MinLen = std::min(ExpectedLine.size(), ActualLine.size());
@@ -2809,7 +2846,7 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
 
   // Before Context
   if (!Ctx.LineBefore.empty()) {
-    if (Mode == Split)
+    if (IsSplit)
       OS << "  " << GetView(Ctx.LineBefore) << Sep << GetView(Ctx.LineBefore)
          << "\n";
     else
@@ -2817,7 +2854,7 @@ static void renderDiff(DiffFormatType Mode, unsigned ExpectedLineNo,
   }
 
   // Mismatch
-  if (Mode == Split) {
+  if (IsSplit) {
     OS << "  ";
 
     OS.changeColor(raw_ostream::RED);
@@ -2867,6 +2904,13 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
   const char *PatPtr = PatternLoc.getPointer();
   StringRef ExpectedLine = StringRef(PatPtr).split('\n').first.rtrim();
 
+  std::string ExpectedText;
+  if (Mode == DiffFormatType::SplitNoSubstitution ||
+      Mode == DiffFormatType::UnifiedNoSubstitution)
+    ExpectedText = ExpectedLine.str();
+  else
+    ExpectedText = CheckStr.Pat.getSubstitutedRegex(ExpectedLine);
+
   // Resolve the Actual (Input) line number.
   // Priority: 1. OverwriteActualLine (Found via Fuzzy match)
   //           2. Direct pointer resolution via SourceMgr.
@@ -2887,7 +2931,7 @@ static bool printDiff(DiffFormatType Mode, const FileCheckString &CheckStr,
   unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
   DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
 
-  renderDiff(Mode, ExpectedLineNo, ActualLineNo, ExpectedLine, ActualLine,
+  renderDiff(Mode, ExpectedLineNo, ActualLineNo, ExpectedText, ActualLine,
              Context);
 
   errs() << '\n';
diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h
index 58a03d280b658..5b47b87076638 100644
--- a/llvm/lib/FileCheck/FileCheckImpl.h
+++ b/llvm/lib/FileCheck/FileCheckImpl.h
@@ -740,6 +740,8 @@ 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-multi-subs.txt b/llvm/test/FileCheck/diff/diff-multi-subs.txt
new file mode 100644
index 0000000000000..40d72113a44a1
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-multi-subs.txt
@@ -0,0 +1,15 @@
+; 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:      @@ -2 +2 @@
+; 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
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-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-substitutions.txt b/llvm/test/FileCheck/diff/diff-substitutions.txt
new file mode 100644
index 0000000000000..11c8a68484f1f
--- /dev/null
+++ b/llvm/test/FileCheck/diff/diff-substitutions.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=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
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/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index f315fda533931..bfb1e541d57ed 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -119,7 +119,11 @@ static cl::opt<DiffFormatType> DiffFormat(
     cl::values(clEnumValN(None, "none", "Standard FileCheck diagnostics"),
                clEnumValN(Unified, "unidiff", "Outputs a Unified diff"),
                clEnumValN(Unified, "", ""),
-               clEnumValN(Split, "split", "Outputs a Split diff")));
+               clEnumValN(Split, "split", "Outputs a Split diff"),
+               clEnumValN(UnifiedNoSubstitution, "unidiff-no-substitutions",
+                          "Outputs a Unified diff with no substitutions"),
+               clEnumValN(SplitNoSubstitution, "split-no-substitutions",
+                          "Outputs a Split diff with no substitutions")));
 
 // The order of DumpInputValue members affects their precedence, as documented
 // for -dump-input below.



More information about the llvm-commits mailing list