[llvm] [FileCheck] --dump-input: Elide middle of underlines for no-match (PR #192908)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 22 23:01:25 PDT 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/192908
>From 8c4563495fd58854d45f309b3e240ad765176abd Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 19 Apr 2026 23:21:05 -0700
Subject: [PATCH 1/2] [FileCheck] --dump-input: Elide middle of underlines for
no-match
When a CHECK fails to find its pattern, the diagnostic's input range may
span multiple lines due to `printFuzzyMatch`.
The default --dump-input=fail renders a `~~~` continuation on every
spanned input line, producing a wall of underlines that obscures the input
rather than highlighting it.
This PR modifies the https://reviews.llvm.org/D52999 feature by dropping
such middle `~~~` for "not found" diagnostics (Lead='X').
Related to #77257 improvement. Aided by Claude Opus 4.7.
Test blocks are regenerated to reflect the reduced annotation footprint.
---
.../test/FileCheck/dump-input/annotations.txt | 7 --
llvm/test/FileCheck/dump-input/context.txt | 57 +++++++-------
llvm/test/FileCheck/dump-input/filter.txt | 74 ++-----------------
llvm/utils/FileCheck/FileCheck.cpp | 11 ++-
4 files changed, 44 insertions(+), 105 deletions(-)
diff --git a/llvm/test/FileCheck/dump-input/annotations.txt b/llvm/test/FileCheck/dump-input/annotations.txt
index 367ea3377e6ae..7787e562c2ca2 100644
--- a/llvm/test/FileCheck/dump-input/annotations.txt
+++ b/llvm/test/FileCheck/dump-input/annotations.txt
@@ -30,9 +30,7 @@
; ALIGN-NEXT:check:1 ^~~~~
; ALIGN-NEXT:check:2'0 X~~~~~~ error: no match found
; ALIGN-NEXT: 2: goodbye
-; ALIGN-NEXT:check:2'0 ~~~~~~~~
; ALIGN-NEXT: 3: world
-; ALIGN-NEXT:check:2'0 ~~~~~~
; ALIGN-NEXT: 4: unicorn
; ALIGN-NEXT:check:2'0 ~~~~~~~~
; ALIGN-NEXT:check:2'1 ? possible intended match
@@ -74,7 +72,6 @@
; CHK-V-NEXT: check:1 ^~~~~
; CHK-NEXT: check:2'0 X error: no match found
; CHK-NEXT: 2: again
-; CHK-NEXT: check:2'0 ~~~~~~
; CHK-NEXT: 3: whirled
; CHK-NEXT: check:2'0 ~~~~~~~~
; CHK-NEXT: check:2'1 ? possible intended match
@@ -283,7 +280,6 @@
; EMP-V-NEXT: empty:2 ^
; EMP-NEXT: empty:3 X error: no match found
; EMP-NEXT: 3: world
-; EMP-NEXT: empty:3 ~~~~~~
; EMP-NEXT: 4: label
; EMP-V-NEXT: label:4 ^~~~~
; EMP-NEXT: empty:3 ~~~~~
@@ -356,7 +352,6 @@
; NOT-NEXT: 1: hello
; NOT-VV-NEXT: not:1 X~~~~~
; NOT-NEXT: 2: world
-; NOT-VV-NEXT: not:1 ~~~~~~
; NOT-NEXT: not:2 !~~~~ error: no match expected
; NOT-NEXT: 3: again
; NOT-VV-NEXT: not:1 ~~~~~~
@@ -389,7 +384,6 @@
; NOT2-NEXT: 1: hello
; NOT2-VV-NEXT: not:1 X~~~~~
; NOT2-NEXT: 2: world
-; NOT2-VV-NEXT: not:1 ~~~~~~
; NOT2-NEXT: not:2 !~~~~ error: no match expected
; NOT2-NEXT: 3: again
; NOT2-V-NEXT: check:3 ^~~
@@ -573,7 +567,6 @@
; LAB-V-NEXT:label:2'1 ^~~~~~
; LAB-NEXT:check:3 X error: no match found
; LAB-NEXT: 3: textA
-; LAB-NEXT:check:3 ~~~~~~
; LAB-NEXT: 4: labelB
; LAB-V-NEXT:label:4 ^~~~~~
; LAB-NEXT:check:3 ~~~~~~
diff --git a/llvm/test/FileCheck/dump-input/context.txt b/llvm/test/FileCheck/dump-input/context.txt
index 78092a909dbd8..d1a41187fc406 100644
--- a/llvm/test/FileCheck/dump-input/context.txt
+++ b/llvm/test/FileCheck/dump-input/context.txt
@@ -116,47 +116,48 @@
; RUN: echo ' CHECK-NEXT: world' >> %t.wide.chk
; W5: <<<<<<
-; W5: 9: lab1 hello
+; W5: 1: foo8
+; W5-NEXT: 2: foo7
+; W5-NEXT: 3: foo6
+; W5-NEXT: 4: foo5
+; W5-NEXT: 5: foo4
+; W5-NEXT: 6: foo3
+; W5-NEXT: 7: foo2
+; W5-NEXT: 8: foo1
+; W5-NEXT: 9: lab1 hello
; W5-NEXT: label:1'0 ^~~~
; W5-NEXT: label:1'1 ^~~~
; W5-NEXT: check:2 ^~~~~
; W5-NEXT: check:3'0 X error: no match found
; W5-NEXT: 10: foo1
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: check:3'1 ? possible intended match
; W5-NEXT: 11: foo2
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 12: foo3
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 13: foo4
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 14: foo5
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 15: foo6
-; W5-NEXT: check:3'0 ~~~~~
-; W6-NEXT: 16: foo7
-; W6-NEXT: check:3'0 ~~~~~
-; WM-NEXT: .
-; WM-NEXT: .
-; WM-NEXT: .
-; W6-NEXT: 17: foo7
-; W6-NEXT: check:3'0 ~~~~~
-; W6-NEXT: 18: foo6
-; W6-NEXT: check:3'0 ~~~~~
+; W5-NEXT: 16: foo7
+; W5-NEXT: 17: foo7
+; W5-NEXT: 18: foo6
; W5-NEXT: 19: foo5
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 20: foo4
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 21: foo3
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 22: foo2
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 23: foo1
-; W5-NEXT: check:3'0 ~~~~~
; W5-NEXT: 24: lab2 world
; W5-NEXT: label:4 ^~~~
; W5-NEXT: check:3'0 ~~~~
; W5-NEXT: next:5 !~~~~ error: match on wrong line
+; W5-NEXT: 25: foo1
+; W5-NEXT: 26: foo2
+; W5-NEXT: 27: foo3
+; W5-NEXT: 28: foo4
+; W5-NEXT: 29: foo5
+; W6-NEXT: 30: foo6
+; W5-NEXT: .
+; W5-NEXT: .
+; W5-NEXT: .
+; W5-NEXT: >>>>>>
;--------------------------------------------------
; Check -dump-input-context=<bad value>.
@@ -276,19 +277,19 @@ BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-context option: '[[VAL
; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
;--------------------------------------------------
-; Check how annotations on input lines that might be elided by ellipses affect
-; whether they are actually elided.
+; Check that -dump-input-context reaches the expected lines when the failing
+; CHECK's search range spans many input lines.
;--------------------------------------------------
-; At -dump-input-context=5, the ellipsis is useful but only when annotations on
-; elided input lines are considered.
+; Search-range diagnostics (Lead='X') mark only the start and the end of the
+; scanned region, so intermediate input lines only become visible when
+; -dump-input-context reaches them. context=5 and context=6 differ by one
+; trailing line (line 30) included at the larger context.
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -dump-input=fail -vv %t.wide.chk < %t.wide.in 2>&1 \
; RUN: -dump-input-context=5 \
-; RUN: | FileCheck %s -match-full-lines -check-prefixes=W5,WM
+; RUN: | FileCheck %s -match-full-lines -check-prefix=W5
-; At -dump-input-context=6, the ellipsis is not useful even when annotations on
-; elided input lines are considered.
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -dump-input=fail -vv %t.wide.chk < %t.wide.in 2>&1 \
; RUN: -dump-input-context=6 \
diff --git a/llvm/test/FileCheck/dump-input/filter.txt b/llvm/test/FileCheck/dump-input/filter.txt
index 6ea858d2126e6..bf05b5dbcb741 100644
--- a/llvm/test/FileCheck/dump-input/filter.txt
+++ b/llvm/test/FileCheck/dump-input/filter.txt
@@ -70,48 +70,27 @@
; ALL-NEXT: check:2 ^~~~~
; ALL-NEXT: check:3'0 X error: no match found
; ALL-NEXT: 13: foo0
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 14: foo1
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 15: foo2
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 16: foo3
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 17: foo4
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 18: foo5
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 19: foo6
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 20: foo7
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 21: foo8
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 22: foo9
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 23: word
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: check:3'1 ? possible intended match
; ALL-NEXT: 24: foo0
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 25: foo1
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 26: foo2
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 27: foo3
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 28: foo4
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 29: foo5
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 30: foo6
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 31: foo7
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 32: foo8
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 33: foo9
-; ALL-NEXT: check:3'0 ~~~~~
; ALL-NEXT: 34: end
; ALL-NEXT: check:3'0 ~~~~
; ALL-NEXT: >>>>>>
@@ -130,48 +109,21 @@
; ANNOTATION-FULL-NEXT: check:2 ^~~~~
; ANNOTATION-FULL-NEXT: check:3'0 X error: no match found
; ANNOTATION-FULL-NEXT: 13: foo0
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 14: foo1
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 15: foo2
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 16: foo3
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 17: foo4
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 18: foo5
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 19: foo6
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 20: foo7
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
; ANNOTATION-FULL-NEXT: 21: foo8
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 22: foo9
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 23: word
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: check:3'1 ? possible intended match
; ANNOTATION-FULL-NEXT: 24: foo0
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 25: foo1
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 26: foo2
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 27: foo3
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 28: foo4
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 29: foo5
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 30: foo6
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
-; ANNOTATION-FULL-NEXT: 31: foo7
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
; ANNOTATION-FULL-NEXT: 32: foo8
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 33: foo9
-; ANNOTATION-FULL-NEXT: check:3'0 ~~~~~
; ANNOTATION-FULL-NEXT: 34: end
; ANNOTATION-FULL-NEXT: check:3'0 ~~~~
; ANNOTATION-FULL-NEXT: >>>>>>
@@ -190,23 +142,16 @@
; ANNOTATION-NEXT: check:2 ^~~~~
; ANNOTATION-NEXT: check:3'0 X error: no match found
; ANNOTATION-NEXT: 13: foo0
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: 14: foo1
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: 21: foo8
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: 22: foo9
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: 23: word
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: check:3'1 ? possible intended match
; ANNOTATION-NEXT: 24: foo0
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: 25: foo1
-; ANNOTATION-NEXT: check:3'0 ~~~~~
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
@@ -222,23 +167,16 @@
; ERROR-NEXT: check:2 ^~~~~
; ERROR-NEXT: check:3'0 X error: no match found
; ERROR-NEXT: 13: foo0
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: 14: foo1
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: .
; ERROR-NEXT: .
; ERROR-NEXT: .
; ERROR-NEXT: 21: foo8
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: 22: foo9
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: 23: word
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: check:3'1 ? possible intended match
; ERROR-NEXT: 24: foo0
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: 25: foo1
-; ERROR-NEXT: check:3'0 ~~~~~
; ERROR-NEXT: .
; ERROR-NEXT: .
; ERROR-NEXT: .
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 12fdbfd45279d..50af6b8f19a91 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -458,12 +458,19 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
"expected input range not to be inverted");
A.InputEndCol = UINT_MAX;
Annotations.push_back(A);
- for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
- L <= E; ++L) {
+ // A diagnostic can cover multiple input lines due to [[:space:]] matching
+ // a new line or a "not found" diagnostic (Lead='X').
+ unsigned E = DiagItr->InputEndLine;
+ unsigned LastRenderable = DiagItr->InputEndCol == 1 ? E - 1 : E;
+ for (unsigned L = DiagItr->InputStartLine + 1; L <= E; ++L) {
// If a range ends before the first column on a line, then it has no
// characters on that line, so there's nothing to render.
if (DiagItr->InputEndCol == 1 && L == E)
break;
+ // For a "not found", emit only the last-renderable-line marker to avoid
+ // `~~~` continuation obscuring the content.
+ if (A.Marker.Lead == 'X' && L != LastRenderable)
+ continue;
InputAnnotation B;
B.DiagIndex = A.DiagIndex;
B.Label = A.Label;
>From 1903fdd45aad1cb13c61cc68822c2f0b869a8cb7 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 22 Apr 2026 23:01:14 -0700
Subject: [PATCH 2/2] comments 2 and 3
---
llvm/test/FileCheck/dump-input/context.txt | 9 +++++++--
llvm/test/FileCheck/dump-input/filter.txt | 8 ++++++++
llvm/utils/FileCheck/FileCheck.cpp | 10 ++++++++--
3 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/llvm/test/FileCheck/dump-input/context.txt b/llvm/test/FileCheck/dump-input/context.txt
index d1a41187fc406..0719afaadbfb9 100644
--- a/llvm/test/FileCheck/dump-input/context.txt
+++ b/llvm/test/FileCheck/dump-input/context.txt
@@ -105,8 +105,13 @@
; CE-NEXT: .
; C0-NEXT: >>>>>>
-; Now build an alternate set of checks where input lines that might be elided by
-; ellipses have annotations.
+; Now build an alternate set of checks that exercises --dump-input-context
+; when the failing CHECK's search range covers many input lines. The
+; "not found" diagnostic's `~~~` underlines are elided to a start + end
+; marker, so intermediate input lines are pulled into visibility only by
+; --dump-input-context (or by unrelated annotations like MatchFuzzy).
+; The "annotations factor into the ellipsis-worthwhile calculation"
+; property is still exercised by filter.txt's ANNOTATION-FULL block.
; RUN: cp %t.in %t.wide.in
; RUN: echo 'CHECK-LABEL: lab1' > %t.wide.chk
diff --git a/llvm/test/FileCheck/dump-input/filter.txt b/llvm/test/FileCheck/dump-input/filter.txt
index bf05b5dbcb741..94b043e6ba7a8 100644
--- a/llvm/test/FileCheck/dump-input/filter.txt
+++ b/llvm/test/FileCheck/dump-input/filter.txt
@@ -155,6 +155,10 @@
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: 32: foo8
+; ANNOTATION-NEXT: 33: foo9
+; ANNOTATION-NEXT: 34: end
+; ANNOTATION-NEXT: check:3'0 ~~~~
; ANNOTATION-NEXT: >>>>>>
; ERROR: <<<<<<
@@ -180,6 +184,10 @@
; ERROR-NEXT: .
; ERROR-NEXT: .
; ERROR-NEXT: .
+; ERROR-NEXT: 32: foo8
+; ERROR-NEXT: 33: foo9
+; ERROR-NEXT: 34: end
+; ERROR-NEXT: check:3'0 ~~~~
; ERROR-NEXT: >>>>>>
;--------------------------------------------------
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 50af6b8f19a91..b81c607f45a1a 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -462,6 +462,7 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
// a new line or a "not found" diagnostic (Lead='X').
unsigned E = DiagItr->InputEndLine;
unsigned LastRenderable = DiagItr->InputEndCol == 1 ? E - 1 : E;
+ bool ElideMiddle = A.Marker.Lead == 'X';
for (unsigned L = DiagItr->InputStartLine + 1; L <= E; ++L) {
// If a range ends before the first column on a line, then it has no
// characters on that line, so there's nothing to render.
@@ -469,12 +470,17 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
break;
// For a "not found", emit only the last-renderable-line marker to avoid
// `~~~` continuation obscuring the content.
- if (A.Marker.Lead == 'X' && L != LastRenderable)
+ if (ElideMiddle && L != LastRenderable)
continue;
InputAnnotation B;
B.DiagIndex = A.DiagIndex;
B.Label = A.Label;
- B.IsFirstLine = false;
+ // The elided end marker is the only annotation for this diagnostic
+ // after the first-line marker, so treat it as a first-line annotation
+ // for filter purposes. Otherwise --dump-input-filter=error (the
+ // default) would keep the start line visible but not pull the end
+ // line into view, hiding the close of the search range.
+ B.IsFirstLine = ElideMiddle && L == LastRenderable;
B.InputLine = L;
B.Marker = A.Marker;
B.Marker.Lead = '~';
More information about the llvm-commits
mailing list