[llvm-branch-commits] [llvm] [FileCheck] Annotate search ranges with { } in -dump-input (PR #198138)
Joel E. Denny via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri May 22 12:28:38 PDT 2026
https://github.com/jdenny-ornl updated https://github.com/llvm/llvm-project/pull/198138
>From e407a44495ddd10b96745443ea5a7391874ab3b0 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Sat, 16 May 2026 20:21:24 -0400
Subject: [PATCH 1/2] [FileCheck] Annotate search ranges with { } in
-dump-input
Example
=======
```
$ cat check
CHECK: start
CHECK-NEXT: end
$ FileCheck -v -dump-input-context=2 check < input |& tail -23
<<<<<<
1: start
check:1 ^~~~~
next:2'0 { search range start (exclusive)
2: foo0
3: foo1
.
.
.
21: foo19
22: foo20
23: end
next:2'1 !~~ error: match on wrong line
24: bar0
25: bar1
.
.
.
42: bar18
43: bar19
44: bar20
next:2'2 } search range end (exclusive)
>>>>>>
```
Without this patch, input lines 1-3 and 42-44 are not shown. However,
lines 1-3 are where the actual problem is because that is where the
`CHECK-NEXT` directive was expected to match but did not.
Search Ranges Are Helpful
=========================
In general, this patch marks any failed pattern's search range by
using the annotation style shown above, and these annotations are
filtered in when using `-dump-input-filter=error`, which is the
default filter. Seeing the search range can be helpful for
understanding the pattern's behavior. Moreover, the cause of the
pattern failure is often the input at the start or end of the search
range. For example:
- A `CHECK-NEXT` or `CHECK-SAME` match on the wrong line, as in the
above example.
- A `CHECK-NOT` unexpected match because a neighboring directive
matched at an unexpected point, affecting the search range.
- An unmatched `CHECK` because a subsequent `CHECK-LABEL` matched at
an unexpected point, affecting the search range end. (In this case,
the search range start and thus the prior directive's match is
already revealed without this patch.)
This patch introduces tests in
`llvm/test/FileCheck/dump-input/search-range-annotations` to
demonstrate those cases.
This patch is a replacement for D96653, which attempted to address the
above cases but in a less straight-forward and somewhat broken manner.
The idea of the current patch was discussed during that review.
Replacing `X~~`
===============
Without this patch, search ranges for unmatched patterns (whether a
success, as for `CHECK-NOT`, or a failure, as for `CHECK`) are already
marked with `X~~`. In those cases, this patch replaces those
annotations with the new annotations shown above. As described above,
this patch adds the new annotations to all other failed matched
patterns as well.
`X~~` is thus no longer used by `-dump-input`. The `X~~` style is
very noisy, especially for consecutive unmatched unexpected patterns
(like a `CHECK-NOT` block), all of which mark every line of their
identical search ranges. Switching to the new style significantly
reduces the noise in such cases. That noise was discussed recently in
issue #77257 and PRs linked from there, and that discussion led to the
resurrection of this patch.
Without this patch, `-dump-input-filter=error` filters in only the
start of a search range that spans multiple lines because an entire
`X~~` is one annotation. With this patch, it filters in both the
start and end of a search range because they are separate annotations
(or a single one-liner, as discussed below). With or without this
patch, a different argument to `-dump-input-filter` (and `-vv`) is
required to filter in the search range for an unmatched unexpected
pattern, like `CHECK-NOT`, because that is not an error.
One-Liners
==========
If a search range does not involve multiple input lines, this patch
keeps the `{` and `}` markers on the same output line like this:
```
<<<<<<
1: start foo end
check:1 ^~~~~
check:3 ^~~
not:2 { } search range (exclusive bounds)
>>>>>>
```
Exclusive Boundaries
====================
`{` and `}` are to be interpreted as exclusive bounds. That is, the
characters at those markers are not included in the search range, but
everything in between is. To try to avoid confusion, this patch adds
the word "exclusive" in every search range annotation.
When the search range starts or ends at a line boundary, the marker
cannot be placed at the first or last character of the line because
that would exclude that character. This patch instead places the
marker in the margin of the input dump, either before the line's first
character or after the line's last character (usually a newline),
rather than on the adjacent line. I have found this makes the
annotations easier to read (more apt have a one-liner, at least), and
I do not think it would make much sense to move a start annotation to
an imaginary line 0. For example, the `{` and `}` below appear in the
input dump margins, before the first character, `s`, and after the
space representing the newline:
```
<<<<<<
1: start
check:1'0 { } search range (exclusive bounds)
check:1'1 error: no match found
>>>>>>
```
Before trying exclusive bounds, I experimented with notations
involving inclusive bounds (e.g., `[` and `]`, or `[` and `)`). Such
notations either cannot distinguish empty ranges (without a cryptic
inversion like `][`) from one-character ranges, or they cannot
represent them with one-liners (because they must occupy the same
column), increasing verbosity. I ultimately decided I prefer
exclusive bounds because they are visually and semantically symmetric
while consistently concise and unambiguous.
For comparison, match ranges (e.g., `^~~`) cannot distinguish
one-character ranges from empty ranges. However, in my experience,
empty match ranges are uncommon, and usually it is easy to distinguish
them based on the pattern or directive (e.g., `CHECK-EMPTY`). In
contrast, empty search ranges can occur repeatedly when directive
matches are adjacent and `-implicit-check-not` is used.
---
llvm/include/llvm/FileCheck/FileCheck.h | 4 +-
.../test/FileCheck/dump-input/annotations.txt | 477 ++++++++++----
llvm/test/FileCheck/dump-input/context.txt | 204 +++---
llvm/test/FileCheck/dump-input/enable.txt | 49 +-
llvm/test/FileCheck/dump-input/filter.txt | 597 +++++++++++-------
.../check-label-follows.txt | 74 +++
.../check-next-same.txt | 61 ++
.../search-range-annotations/check-not.txt | 112 ++++
.../invalid-excluded-pattern.txt | 7 +-
.../invalid-expected-pattern.txt | 7 +-
.../matched-excluded-pattern.txt | 7 +-
.../matched-expected-pattern.txt | 9 +-
llvm/utils/FileCheck/FileCheck.cpp | 230 ++++++-
13 files changed, 1337 insertions(+), 501 deletions(-)
create mode 100644 llvm/test/FileCheck/dump-input/search-range-annotations/check-label-follows.txt
create mode 100644 llvm/test/FileCheck/dump-input/search-range-annotations/check-next-same.txt
create mode 100644 llvm/test/FileCheck/dump-input/search-range-annotations/check-not.txt
diff --git a/llvm/include/llvm/FileCheck/FileCheck.h b/llvm/include/llvm/FileCheck/FileCheck.h
index b000f5b0dac73..61486567c403b 100644
--- a/llvm/include/llvm/FileCheck/FileCheck.h
+++ b/llvm/include/llvm/FileCheck/FileCheck.h
@@ -302,7 +302,7 @@ class MatchNoneDiag : public MatchResultDiag {
/// \c MatchResultDiag.
class MatchNoteDiag : public FileCheckDiag {
private:
- MatchResultDiag *MRD;
+ const MatchResultDiag *MRD;
public:
MatchNoteDiag(FileCheckDiagKind Kind) : FileCheckDiag(Kind), MRD(nullptr) {}
@@ -316,7 +316,7 @@ class MatchNoteDiag : public FileCheckDiag {
/// Get the note's associated \c MatchResultDiag.
const MatchResultDiag &getMatchResultDiag() const override { return *MRD; }
/// Set the note's associated \c MatchResultDiag.
- void setMatchResultDiag(MatchResultDiag *MRDNew) {
+ void setMatchResultDiag(const MatchResultDiag *MRDNew) {
assert(!MRD && "expected setMatchResultDiag to be called only once");
MRD = MRDNew;
}
diff --git a/llvm/test/FileCheck/dump-input/annotations.txt b/llvm/test/FileCheck/dump-input/annotations.txt
index f928f3c3eaada..b8a92d090f77b 100644
--- a/llvm/test/FileCheck/dump-input/annotations.txt
+++ b/llvm/test/FileCheck/dump-input/annotations.txt
@@ -132,12 +132,12 @@
; CHK:<<<<<<
; CHK-NEXT: 1: hello
; CHK-V-NEXT:check:1 ^~~~~
-; CHK-NEXT:check:2'0 X error: no match found
+; CHK-NEXT:check:2'0 { search range start (exclusive)
+; CHK-NEXT:check:2'1 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
+; CHK-NEXT:check:2'2 ? possible intended match
+; CHK-NEXT:check:2'3 } search range end (exclusive)
; CHK-NEXT:>>>>>>
; CHK-NOT:{{.}}
@@ -166,25 +166,27 @@
; CNT-V-NEXT:count:1'0 ^~~~
; CNT-V-NEXT: 2: repete
; CNT-V-NEXT:count:1'1 ^~~~
-; CNT-V-NEXT:count:1'2 X error: no match found
+; CNT-V-NEXT:count:1'2 { search range start (exclusive)
+; CNT-V-NEXT:count:1'3 error: no match found
; CNT-V-NEXT: 3: repeat
-; CNT-V-NEXT:count:1'2 ~~~~~~~
-; CNT-V-NEXT:count:1'3 ? possible intended match
+; CNT-V-NEXT:count:1'4 ? possible intended match
+; CNT-V-NEXT:count:1'5 } search range end (exclusive)
; CNT-V-NEXT:>>>>>>
; CNT-V-NOT:{{.}}
; CNT-Q:<<<<<<
; CNT-Q-NEXT: 1: pete
; CNT-Q-NEXT: 2: repete
-; CNT-Q-NEXT:count:1'0 X error: no match found
+; CNT-Q-NEXT:count:1'0 { search range start (exclusive)
+; CNT-Q-NEXT:count:1'1 error: no match found
; CNT-Q-NEXT: 3: repeat
-; CNT-Q-NEXT:count:1'0 ~~~~~~~
-; CNT-Q-NEXT:count:1'1 ? possible intended match
+; CNT-Q-NEXT:count:1'2 ? possible intended match
+; CNT-Q-NEXT:count:1'3 } search range end (exclusive)
; CNT-Q-NEXT:>>>>>>
; CNT-Q-NOT:{{.}}
;--------------------------------------------------
-; CHECK-NEXT (also: EOF search-range, wrong-line match)
+; CHECK-NEXT (also: EOF search-range, single-char search range, wrong-line match)
;--------------------------------------------------
; Good match and no match.
@@ -209,7 +211,8 @@
; NXT-V-NEXT:check:1 ^~~~~
; NXT-NEXT: 2: again
; NXT-V-NEXT:next:2 ^~~~~
-; NXT-NEXT:next:3 X error: no match found
+; NXT-NEXT:next:3'0 { } search range (exclusive bounds)
+; NXT-NEXT:next:3'1 error: no match found
; NXT-NEXT:>>>>>>
; NXT-NOT:{{.}}
@@ -231,15 +234,16 @@
; NXT2-V-NEXT:check:1 ^~~~~
; NXT2-NEXT: 2: again
; NXT2-V-NEXT:next:2 ^~~~~
+; NXT2-NEXT:next:3'0 { search range start (exclusive)
; NXT2-NEXT: 3: yonder
; NXT2-NEXT: 4: world
-; NXT2-NEXT:next:3 !~~~~ error: match on wrong line
+; NXT2-NEXT:next:3'1 !~~~~ error: match on wrong line
+; NXT2-NEXT:next:3'2 } search range end (exclusive)
; NXT2-NEXT:>>>>>>
; NXT2-NOT:{{.}}
;--------------------------------------------------
-; CHECK-SAME (also: multiple annotations per line, single-char search range,
-; wrong-line match)
+; CHECK-SAME (also: multiple annotations per line, wrong-line match)
;--------------------------------------------------
; Good match and no match.
@@ -262,7 +266,8 @@
; SAM-NEXT: 1: hello world!
; SAM-V-NEXT:check:1 ^~~~~
; SAM-V-NEXT:same:2 ^~~~~
-; SAM-NEXT:same:3 X~ error: no match found
+; SAM-NEXT:same:3'0 { } search range (exclusive bounds)
+; SAM-NEXT:same:3'1 error: no match found
; SAM-NEXT:>>>>>>
; SAM-NOT:{{.}}
@@ -282,13 +287,15 @@
; SAM2-NEXT: 1: hello world!
; SAM2-V-NEXT:check:1 ^~~~~
; SAM2-V-NEXT:same:2 ^~~~~
+; SAM2-NEXT:same:3'0 { search range start (exclusive)
; SAM2-NEXT: 2: again
-; SAM2-NEXT:same:3 !~~~~ error: match on wrong line
+; SAM2-NEXT:same:3'1 !~~~~ error: match on wrong line
+; SAM2-NEXT:same:3'2 } search range end (exclusive)
; SAM2-NEXT:>>>>>>
; SAM2-NOT:{{.}}
;--------------------------------------------------
-; CHECK-EMPTY (also: search range ends at label, single-char match, wrong-line
+; CHECK-EMPTY (also: search range ends at label, empty match range, wrong-line
; match)
;--------------------------------------------------
@@ -322,12 +329,12 @@
; EMP-V-NEXT:check:1 ^~~~~
; EMP-NEXT: 2:
; EMP-V-NEXT:empty:2 ^
-; EMP-NEXT:empty:3 X error: no match found
+; EMP-NEXT:empty:3'0 { search range start (exclusive)
+; EMP-NEXT:empty:3'1 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 ~~~~~
+; EMP-NEXT:empty:3'2 } search range end (exclusive)
; EMP-NEXT:>>>>>>
; EMP-NOT:{{.}}
@@ -347,12 +354,24 @@
; Verbose diagnostics are suppressed but not errors.
; EMP2:{{.*}}error:{{.*}}
+; TODO: The search range of CHECK-EMPTY logically ends at the last character of
+; the file, as show below. However, it can actually match the empty string
+; immediately after its search range, as shown below. That is, -dump-input
+; seems to be doing the right thing: it makes no sense to show the search range
+; including a non-existent character. If it's worth the effort to change
+; anything here, then CHECK-EMPTY should probably report any match past the end
+; of the file as a failure to match at all. That would also include cases that
+; are currently successful matches, but that's a strange enough case that it
+; probably wouldn't be backward-incompatible for real test suites.
+
; EMP2:<<<<<<
; EMP2-NEXT: 1: hello
; EMP2-V-NEXT:check:1 ^~~~~
+; EMP2-NEXT:empty:2'0 { search range start (exclusive)
; EMP2-NEXT: 2: world
+; EMP2-NEXT:empty:2'2 } search range end (exclusive)
; EMP2-NEXT: 3:
-; EMP2-NEXT:empty:2 ! error: match on wrong line
+; EMP2-NEXT:empty:2'1 ! error: match on wrong line
; EMP2-NEXT:>>>>>>
; EMP2-NOT:{{.}}
@@ -379,12 +398,13 @@
; NOT:<<<<<<
; NOT-NEXT: 1: hello
-; NOT-VV-NEXT:not:1 X~~~~~
+; NOT-VV-NEXT:not:1'0 { search range start (exclusive)
+; NOT-NEXT:not:2'0 { search range start (exclusive)
; NOT-NEXT: 2: world
-; NOT-VV-NEXT:not:1 ~~~~~~
-; NOT-NEXT:not:2 !~~~~ error: no match expected
+; NOT-NEXT:not:2'1 !~~~~ error: no match expected
; NOT-NEXT: 3: again
-; NOT-VV-NEXT:not:1 ~~~~~~
+; NOT-VV-NEXT:not:1'1 } search range end (exclusive)
+; NOT-NEXT:not:2'2 } search range end (exclusive)
; NOT-VV-NEXT: 4:
; NOT-VV-NEXT:eof:2 ^
; NOT-NEXT:>>>>>>
@@ -404,13 +424,14 @@
; NOT2:<<<<<<
; NOT2-NEXT: 1: hello
-; NOT2-VV-NEXT:not:1 X~~~~~
+; NOT2-VV-NEXT:not:1'0 { search range start (exclusive)
+; NOT2-NEXT:not:2'0 { search range start (exclusive)
; NOT2-NEXT: 2: world
-; NOT2-VV-NEXT:not:1 ~~~~~~
-; NOT2-NEXT:not:2 !~~~~ error: no match expected
+; NOT2-NEXT:not:2'1 !~~~~ error: no match expected
; NOT2-NEXT: 3: again
; NOT2-V-NEXT:check:3 ^~~
-; NOT2-VV-NEXT:not:1 ~~
+; NOT2-VV-NEXT:not:1'1 } search range end (exclusive)
+; NOT2-NEXT:not:2'2 } search range end (exclusive)
; NOT2-NEXT:>>>>>>
; NOT2-NOT:{{.}}
@@ -440,14 +461,15 @@
; DAG-VV:<<<<<<
; DAG-VV-NEXT: 1: abc
; DAG-VV-NEXT:dag:2 ^~~
-; DAG-VV-NEXT:dag:3'0 !~~ discard: overlaps earlier match
+; DAG-VV-NEXT:dag:3'0 !~~ discard: overlaps earlier match
; DAG-VV-NEXT: 2: def
; DAG-VV-NEXT:dag:1 ^~~
-; DAG-VV-NEXT:dag:4'0 !~~ discard: overlaps earlier match
-; DAG-VV-NEXT:dag:4'1 X error: no match found
+; DAG-VV-NEXT:dag:4'0 !~~ discard: overlaps earlier match
+; DAG-VV-NEXT:dag:4'1 { search range start (exclusive)
+; DAG-VV-NEXT:dag:4'2 error: no match found
; DAG-VV-NEXT: 3: abc
; DAG-VV-NEXT:dag:3'1 ^~~
-; DAG-VV-NEXT:dag:4'1 ~~~~
+; DAG-VV-NEXT:dag:4'3 } search range end (exclusive)
; DAG-VV-NEXT:>>>>>>
; DAG-VV-NOT:{{.}}
@@ -456,19 +478,21 @@
; DAG-VQ-NEXT:dag:2 ^~~
; DAG-VQ-NEXT: 2: def
; DAG-VQ-NEXT:dag:1 ^~~
-; DAG-VQ-NEXT:dag:4 X error: no match found
+; DAG-VQ-NEXT:dag:4'0 { search range start (exclusive)
+; DAG-VQ-NEXT:dag:4'1 error: no match found
; DAG-VQ-NEXT: 3: abc
; DAG-VQ-NEXT:dag:3 ^~~
-; DAG-VQ-NEXT:dag:4 ~~~~
+; DAG-VQ-NEXT:dag:4'2 } search range end (exclusive)
; DAG-VQ-NEXT:>>>>>>
; DAG-VQ-NOT:{{.}}
; DAG-Q:<<<<<<
; DAG-Q-NEXT: 1: abc
; DAG-Q-NEXT: 2: def
-; DAG-Q-NEXT:dag:4 X error: no match found
+; DAG-Q-NEXT:dag:4'0 { search range start (exclusive)
+; DAG-Q-NEXT:dag:4'1 error: no match found
; DAG-Q-NEXT: 3: abc
-; DAG-Q-NEXT:dag:4 ~~~~
+; DAG-Q-NEXT:dag:4'2 } search range end (exclusive)
; DAG-Q-NEXT:>>>>>>
; DAG-Q-NOT:{{.}}
@@ -497,13 +521,14 @@
; DAG1L-VV-NEXT: 1: abc def abc def
; DAG1L-VV-NEXT:dag:1 ^~~
; DAG1L-VV-NEXT:dag:2 ^~
-; DAG1L-VV-NEXT:dag:3'0 !~~ discard: overlaps earlier match
+; DAG1L-VV-NEXT:dag:3'0 !~~ discard: overlaps earlier match
; DAG1L-VV-NEXT:dag:3'1 ^~~
-; DAG1L-VV-NEXT:dag:4'0 !~ discard: overlaps earlier match
+; DAG1L-VV-NEXT:dag:4'0 !~ discard: overlaps earlier match
; DAG1L-VV-NEXT:dag:4'1 ^~
-; DAG1L-VV-NEXT:dag:5'0 !~~ discard: overlaps earlier match
-; DAG1L-VV-NEXT:dag:5'1 !~~ discard: overlaps earlier match
-; DAG1L-VV-NEXT:dag:5'2 X~ error: no match found
+; DAG1L-VV-NEXT:dag:5'0 !~~ discard: overlaps earlier match
+; DAG1L-VV-NEXT:dag:5'1 !~~ discard: overlaps earlier match
+; DAG1L-VV-NEXT:dag:5'2 { } search range (exclusive bounds)
+; DAG1L-VV-NEXT:dag:5'3 error: no match found
; DAG1L-VV-NEXT:>>>>>>
; DAG1L-VV-NOT:{{.}}
@@ -513,13 +538,15 @@
; DAG1L-VQ-NEXT:dag:2 ^~
; DAG1L-VQ-NEXT:dag:3 ^~~
; DAG1L-VQ-NEXT:dag:4 ^~
-; DAG1L-VQ-NEXT:dag:5 X~ error: no match found
+; DAG1L-VQ-NEXT:dag:5'0 { } search range (exclusive bounds)
+; DAG1L-VQ-NEXT:dag:5'1 error: no match found
; DAG1L-VQ-NEXT:>>>>>>
; DAG1L-VQ-NOT:{{.}}
; DAG1L-Q:<<<<<<
; DAG1L-Q-NEXT: 1: abc def abc def
-; DAG1L-Q-NEXT:dag:5 X~ error: no match found
+; DAG1L-Q-NEXT:dag:5'0 { } search range (exclusive bounds)
+; DAG1L-Q-NEXT:dag:5'1 error: no match found
; DAG1L-Q-NEXT:>>>>>>
; DAG1L-Q-NOT:{{.}}
@@ -585,31 +612,34 @@
; LAB-V-NEXT: 2: labelA
; LAB-V-NEXT:label:2'0 ^~~~~~
; LAB-V-NEXT:label:2'1 ^~~~~~
-; LAB-V-NEXT:check:3 X error: no match found
+; LAB-V-NEXT:check:3'0 { search range start (exclusive)
+; LAB-V-NEXT:check:3'1 error: no match found
; LAB-V-NEXT: 3: textA
-; LAB-V-NEXT:check:3 ~~~~~~
; LAB-V-NEXT: 4: labelB
; LAB-V-NEXT:label:4 ^~~~~~
-; LAB-V-NEXT:check:3 ~~~~~~
+; LAB-V-NEXT:check:3'2 } search range end (exclusive)
; LAB-V-NEXT: 5: textB
; LAB-V-NEXT: 6: labelC
; LAB-V-NEXT:label:6'0 ^~~~~~
; LAB-V-NEXT:check:5 ^~~~~~
-; LAB-V-NEXT:label:6'1 X error: no match found
-; LAB-VV-NEXT:not:7 X
+; LAB-V-NEXT:label:6'1 {} search range (exclusive bounds)
+; LAB-V-NEXT:label:6'2 error: no match found
+; LAB-VV-NEXT:not:7'0 { search range start (exclusive)
; LAB-V-NEXT: 7: textC
-; LAB-VV-NEXT:not:7 ~~~~~~
+; LAB-VV-NEXT:not:7'1 } search range end (exclusive)
; LAB-V-NEXT: 8: labelD
; LAB-V-NEXT:label:8'0 ^~~~~~
; LAB-V-NEXT:label:8'1 ^~~~~~
+; LAB-V-NEXT:not:9'0 { search range start (exclusive)
; LAB-V-NEXT: 9: textD
-; LAB-V-NEXT:not:9 !~~~~ error: no match expected
+; LAB-V-NEXT:not:9'1 !~~~~ error: no match expected
+; LAB-V-NEXT:not:9'2 } search range end (exclusive)
; LAB-V-NEXT: 10: labelE
; LAB-V-NEXT:label:10'0 ^~~~~~
; LAB-V-NEXT:label:10'1 ^~~~~~
-; LAB-VV-NEXT:not:11 X
+; LAB-VV-NEXT:not:11'0 { search range start (exclusive)
; LAB-V-NEXT: 11: textE
-; LAB-VV-NEXT:not:11 ~~~~~~
+; LAB-VV-NEXT:not:11'1 } search range end (exclusive)
; LAB-V-NEXT: 12: labelF
; LAB-V-NEXT:label:12'0 ^~~~~~
; LAB-V-NEXT:label:12'1 ^~~~~~
@@ -619,18 +649,21 @@
; LAB-Q:<<<<<<
; LAB-Q-NEXT: 1: text
; LAB-Q-NEXT: 2: labelA
-; LAB-Q-NEXT:check:3 X error: no match found
+; LAB-Q-NEXT:check:3'0 { search range start (exclusive)
+; LAB-Q-NEXT:check:3'1 error: no match found
; LAB-Q-NEXT: 3: textA
-; LAB-Q-NEXT:check:3 ~~~~~~
; LAB-Q-NEXT: 4: labelB
-; LAB-Q-NEXT:check:3 ~~~~~~
+; LAB-Q-NEXT:check:3'2 } search range end (exclusive)
; LAB-Q-NEXT: 5: textB
; LAB-Q-NEXT: 6: labelC
-; LAB-Q-NEXT:label:6 X error: no match found
+; LAB-Q-NEXT:label:6'0 {} search range (exclusive bounds)
+; LAB-Q-NEXT:label:6'1 error: no match found
; LAB-Q-NEXT: 7: textC
; LAB-Q-NEXT: 8: labelD
+; LAB-Q-NEXT:not:9'0 { search range start (exclusive)
; LAB-Q-NEXT: 9: textD
-; LAB-Q-NEXT:not:9 !~~~~ error: no match expected
+; LAB-Q-NEXT:not:9'1 !~~~~ error: no match expected
+; LAB-Q-NEXT:not:9'2 } search range end (exclusive)
; LAB-Q-NEXT: 10: labelE
; LAB-Q-NEXT: 11: textE
; LAB-Q-NEXT: 12: labelF
@@ -668,17 +701,18 @@
; IMPNOT-VV:<<<<<<
; IMPNOT-VV-NEXT: 1: hello world again!
; IMPNOT-VV-NEXT:check:1 ^~~
-; IMPNOT-VV-NEXT:not:imp1'0 X
-; IMPNOT-VV-NEXT:not:imp2'0 X
-; IMPNOT-VV-NEXT:not:imp3'0 X
+; IMPNOT-VV-NEXT:not:imp1'0 {} search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp2'0 {} search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp3'0 {} search range (exclusive bounds)
; IMPNOT-VV-NEXT:check:2 ^~~
-; IMPNOT-VV-NEXT:not:imp1'1 X~~
-; IMPNOT-VV-NEXT:not:imp2'1 X~~
-; IMPNOT-VV-NEXT:not:imp3'1 X~~
+; IMPNOT-VV-NEXT:not:imp1'1 { } search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp2'1 { } search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp3'1 { } search range (exclusive bounds)
; IMPNOT-VV-NEXT:check:3 ^
-; IMPNOT-VV-NEXT:not:imp1'2 X~~~~~~~
-; IMPNOT-VV-NEXT:not:imp2'2 X~~~~~~~
-; IMPNOT-VV-NEXT:not:imp3'2 !~~~~ error: no match expected
+; IMPNOT-VV-NEXT:not:imp1'2 { } search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp2'2 { } search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp3'2 { } search range (exclusive bounds)
+; IMPNOT-VV-NEXT:not:imp3'3 !~~~~ error: no match expected
; IMPNOT-VV-NEXT:>>>>>>
; IMPNOT-VV-NOT:{{.}}
@@ -687,13 +721,15 @@
; IMPNOT-VQ-NEXT:check:1 ^~~
; IMPNOT-VQ-NEXT:check:2 ^~~
; IMPNOT-VQ-NEXT:check:3 ^
-; IMPNOT-VQ-NEXT:not:imp3 !~~~~ error: no match expected
+; IMPNOT-VQ-NEXT:not:imp3'0 { } search range (exclusive bounds)
+; IMPNOT-VQ-NEXT:not:imp3'1 !~~~~ error: no match expected
; IMPNOT-VQ-NEXT:>>>>>>
; IMPNOT-VQ-NOT:{{.}}
; IMPNOT-Q:<<<<<<
; IMPNOT-Q-NEXT: 1: hello world again!
-; IMPNOT-Q-NEXT:not:imp3 !~~~~ error: no match expected
+; IMPNOT-Q-NEXT:not:imp3'0 { } search range (exclusive bounds)
+; IMPNOT-Q-NEXT:not:imp3'1 !~~~~ error: no match expected
; IMPNOT-Q-NEXT:>>>>>>
; IMPNOT-Q-NOT:{{.}}
@@ -723,15 +759,16 @@
; SUBST-POS:<<<<<<
; SUBST-POS-NEXT: 1: def-match1 def-match2
; SUBST-POS-V-NEXT:check:1'0 ^~~~~~~~~~~~~~~~~~~~~
-; SUBST-POS-V-NEXT:check:1'1 with "DEF_MATCH1" equal to "def-match1"
-; SUBST-POS-V-NEXT:check:1'2 with "DEF_MATCH2" equal to "def-match2"
-; SUBST-POS-NEXT:check:2'0 X error: match failed for invalid pattern
-; SUBST-POS-NEXT:check:2'1 undefined variable: UNDEF
-; SUBST-POS-NEXT:check:2'2 with "DEF_MATCH1" equal to "def-match1"
-; SUBST-POS-NEXT:check:2'3 with "DEF_NOMATCH" equal to "foobar"
+; SUBST-POS-V-NEXT:check:1'1 with "DEF_MATCH1" equal to "def-match1"
+; SUBST-POS-V-NEXT:check:1'2 with "DEF_MATCH2" equal to "def-match2"
+; SUBST-POS-NEXT:check:2'0 { search range start (exclusive)
+; SUBST-POS-NEXT:check:2'1 error: match failed for invalid pattern
+; SUBST-POS-NEXT:check:2'2 undefined variable: UNDEF
+; SUBST-POS-NEXT:check:2'3 with "DEF_MATCH1" equal to "def-match1"
+; SUBST-POS-NEXT:check:2'4 with "DEF_NOMATCH" equal to "foobar"
; SUBST-POS-NEXT: 2: def-match1 def-nomatch
-; SUBST-POS-NEXT:check:2'0 ~~~~~~~~~~~~~~~~~~~~~~~
-; SUBST-POS-NEXT:check:2'4 ? possible intended match
+; SUBST-POS-NEXT:check:2'5 ? possible intended match
+; SUBST-POS-NEXT:check:2'6 } search range end (exclusive)
; SUBST-POS-NEXT:>>>>>>
;--------------------------------------------------
@@ -761,15 +798,18 @@
; SUBST-NEG:<<<<<<
; SUBST-NEG-NEXT: 1: def-match1 def-nomatch
-; SUBST-NEG-NEXT:not:1'0 X~~~~~~~~~~~~~~~~~~~~~~ error: match failed for invalid pattern
-; SUBST-NEG-NEXT:not:1'1 undefined variable: UNDEF
-; SUBST-NEG-NEXT:not:1'2 with "DEF_MATCH1" equal to "def-match1"
-; SUBST-NEG-NEXT:not:1'3 with "DEF_NOMATCH" equal to "foobar"
+; SUBST-NEG-NEXT:not:1'0 { search range start (exclusive)
+; SUBST-NEG-NEXT:not:1'1 error: match failed for invalid pattern
+; SUBST-NEG-NEXT:not:1'2 undefined variable: UNDEF
+; SUBST-NEG-NEXT:not:1'3 with "DEF_MATCH1" equal to "def-match1"
+; SUBST-NEG-NEXT:not:1'4 with "DEF_NOMATCH" equal to "foobar"
+; SUBST-NEG-NEXT:not:2'0 { search range start (exclusive)
; SUBST-NEG-NEXT: 2: def-match1 def-match2
-; SUBST-NEG-NEXT:not:1'0 ~~~~~~~~~~~~~~~~~~~~~~
-; SUBST-NEG-NEXT:not:2'0 !~~~~~~~~~~~~~~~~~~~~ error: no match expected
-; SUBST-NEG-NEXT:not:2'1 with "DEF_MATCH1" equal to "def-match1"
-; SUBST-NEG-NEXT:not:2'2 with "DEF_MATCH2" equal to "def-match2"
+; SUBST-NEG-NEXT:not:1'5 } search range end (exclusive)
+; SUBST-NEG-NEXT:not:2'1 !~~~~~~~~~~~~~~~~~~~~ error: no match expected
+; SUBST-NEG-NEXT:not:2'2 with "DEF_MATCH1" equal to "def-match1"
+; SUBST-NEG-NEXT:not:2'3 with "DEF_MATCH2" equal to "def-match2"
+; SUBST-NEG-NEXT:not:2'4 } search range end (exclusive)
; SUBST-NEG-NEXT: 3: END
; SUBST-NEG-V-NEXT:check:3 ^~~
; SUBST-NEG-NEXT:>>>>>>
@@ -808,23 +848,25 @@
; CAPTURE-NEG:<<<<<<
; CAPTURE-NEG-NEXT: 1: strvar: foo
; CAPTURE-NEG-V-NEXT:check:1'0 ^~~~~~~~~~~
- CAPTURE-NEG-V-NEXT:check:1'1 ^~~ captured var "STRVAR"
+ CAPTURE-NEG-V-NEXT:check:1'1 ^~~ captured var "STRVAR"
; CAPTURE-NEG-NEXT: 2: numvar no expr: 51
; CAPTURE-NEG-V-NEXT:check:2'0 ^~~~~~~~~~~~~~~~~~
-; CAPTURE-NEG-V-NEXT:check:2'1 ^~ captured var "NUMVAR_NO_EXPR"
+; CAPTURE-NEG-V-NEXT:check:2'1 ^~ captured var "NUMVAR_NO_EXPR"
; CAPTURE-NEG-NEXT: 3: numvar expr: -49
; CAPTURE-NEG-V-NEXT:check:3'0 ^~~~~~~~~~~~~~~~
-; CAPTURE-NEG-V-NEXT:check:3'1 with "%d,NUMVAR_EXPR:2-NUMVAR_NO_EXPR" equal to "-49"
-; CAPTURE-NEG-V-NEXT:check:3'2 ^~~ captured var "NUMVAR_EXPR"
+; CAPTURE-NEG-V-NEXT:check:3'1 with "%d,NUMVAR_EXPR:2-NUMVAR_NO_EXPR" equal to "-49"
+; CAPTURE-NEG-V-NEXT:check:3'2 ^~~ captured var "NUMVAR_EXPR"
; CAPTURE-NEG-NEXT: 4: many: foo 100 8 bar
; CAPTURE-NEG-V-NEXT:check:4'0 ^~~~~~~~~~~~~~~~~~~
-; CAPTURE-NEG-V-NEXT:check:4'1 ^~~ captured var "VAR1"
-; CAPTURE-NEG-V-NEXT:check:4'2 ^~~ captured var "VAR3"
-; CAPTURE-NEG-V-NEXT:check:4'3 ^ captured var "VAR2"
-; CAPTURE-NEG-V-NEXT:check:4'4 ^~~ captured var "VAR4"
+; CAPTURE-NEG-V-NEXT:check:4'1 ^~~ captured var "VAR1"
+; CAPTURE-NEG-V-NEXT:check:4'2 ^~~ captured var "VAR3"
+; CAPTURE-NEG-V-NEXT:check:4'3 ^ captured var "VAR2"
+; CAPTURE-NEG-V-NEXT:check:4'4 ^~~ captured var "VAR4"
+; CAPTURE-NEG-NEXT:not:5'0 { search range start (exclusive)
; CAPTURE-NEG-NEXT: 5: var in neg match: foo
-; CAPTURE-NEG-NEXT:not:5'0 !~~~~~~~~~~~~~~~~~~~~ error: no match expected
-; CAPTURE-NEG-NEXT:not:5'1 !~~ captured var "VAR"
+; CAPTURE-NEG-NEXT:not:5'1 !~~~~~~~~~~~~~~~~~~~~ error: no match expected
+; CAPTURE-NEG-NEXT:not:5'2 !~~ captured var "VAR"
+; CAPTURE-NEG-NEXT:not:5'3 } search range end (exclusive)
; CAPTURE-NEG-NEXT: 6: END
; CAPTURE-NEG-V-NEXT:check:6 ^~~
; CAPTURE-NEG-NEXT:>>>>>>
@@ -858,17 +900,13 @@
; Verbose diagnostics are suppressed but not errors.
; SUBST_NEXT:{{.*}}error:{{.*}}
-; SUBST_NEXT-V:<<<<<<
-; SUBST_NEXT-V-NEXT: 1: pre var
+; SUBST_NEXT:<<<<<<
+; SUBST_NEXT-NEXT: 1: pre var
; SUBST_NEXT-V-NEXT:check:1 ^~~
-; SUBST_NEXT-V-NEXT:next:2'0 !~~ error: match on wrong line
-; SUBST_NEXT-V-NEXT:next:2'1 with "VAR" equal to "var"
-; SUBST_NEXT-V-NEXT:>>>>>>
-
-; SUBST_NEXT-Q:<<<<<<
-; SUBST_NEXT-Q-NEXT: 1: pre var
-; SUBST_NEXT-Q-NEXT:next:2 !~~ error: match on wrong line
-; SUBST_NEXT-Q-NEXT:>>>>>>
+; SUBST_NEXT-NEXT:next:2'0 { } search range (exclusive bounds)
+; SUBST_NEXT-NEXT:next:2'1 !~~ error: match on wrong line
+; SUBST_NEXT-V-NEXT:next:2'2 with "VAR" equal to "var"
+; SUBST_NEXT-NEXT:>>>>>>
;- - - - - - - - - - - - - - - - - - - - - - - - -
; CHECK-SAME.
@@ -893,15 +931,19 @@
; SUBST_SAME-V:<<<<<<
; SUBST_SAME-V-NEXT: 1: pre
; SUBST_SAME-V-NEXT:check:1 ^~~
+; SUBST_SAME-V-NEXT:same:2'0 { search range start (exclusive)
; SUBST_SAME-V-NEXT: 2: var
-; SUBST_SAME-V-NEXT:same:2'0 !~~ error: match on wrong line
-; SUBST_SAME-V-NEXT:same:2'1 with "VAR" equal to "var"
+; SUBST_SAME-V-NEXT:same:2'1 !~~ error: match on wrong line
+; SUBST_SAME-V-NEXT:same:2'2 with "VAR" equal to "var"
+; SUBST_SAME-V-NEXT:same:2'3 } search range end (exclusive)
; SUBST_SAME-V-NEXT:>>>>>>
; SUBST_SAME-Q:<<<<<<
; SUBST_SAME-Q-NEXT: 1: pre
+; SUBST_SAME-Q-NEXT:same:2'0 { search range start (exclusive)
; SUBST_SAME-Q-NEXT: 2: var
-; SUBST_SAME-Q-NEXT:same:2 !~~ error: match on wrong line
+; SUBST_SAME-Q-NEXT:same:2'1 !~~ error: match on wrong line
+; SUBST_SAME-Q-NEXT:same:2'2 } search range end (exclusive)
; SUBST_SAME-Q-NEXT:>>>>>>
;- - - - - - - - - - - - - - - - - - - - - - - - -
@@ -926,11 +968,11 @@
; SUBST_DAG-VV:<<<<<<
; SUBST_DAG-VV-NEXT: 1: var
; SUBST_DAG-VV-NEXT:dag:1 ^~~
-; SUBST_DAG-VV-NEXT:dag:2'0 !~~ discard: overlaps earlier match
-; SUBST_DAG-VV-NEXT:dag:2'1 with "VAR" equal to "var"
+; SUBST_DAG-VV-NEXT:dag:2'0 !~~ discard: overlaps earlier match
+; SUBST_DAG-VV-NEXT:dag:2'1 with "VAR" equal to "var"
; SUBST_DAG-VV-NEXT: 2: var
; SUBST_DAG-VV-NEXT:dag:2'2 ^~~
-; SUBST_DAG-VV-NEXT:dag:2'3 with "VAR" equal to "var"
+; SUBST_DAG-VV-NEXT:dag:2'3 with "VAR" equal to "var"
; SUBST_DAG-VV-NEXT: 3: END
; SUBST_DAG-VV-NEXT:check:3 ^~~
; SUBST_DAG-VV-NEXT:>>>>>>
@@ -940,7 +982,7 @@
; SUBST_DAG-VQ-NEXT:dag:1 ^~~
; SUBST_DAG-VQ-NEXT: 2: var
; SUBST_DAG-VQ-NEXT:dag:2'0 ^~~
-; SUBST_DAG-VQ-NEXT:dag:2'1 with "VAR" equal to "var"
+; SUBST_DAG-VQ-NEXT:dag:2'1 with "VAR" equal to "var"
; SUBST_DAG-VQ-NEXT: 3: END
; SUBST_DAG-VQ-NEXT:check:3 ^~~
; SUBST_DAG-VQ-NEXT:>>>>>>
@@ -950,3 +992,212 @@
; SUBST_DAG-Q-NEXT: 2: var
; SUBST_DAG-Q-NEXT: 3: END
; SUBST_DAG-Q-NEXT:>>>>>>
+
+;--------------------------------------------------
+; Check multiline marker where start/end columns vary across lines.
+;--------------------------------------------------
+
+; RUN: echo '<start' > %t.in
+; RUN: echo '' >> %t.in
+; RUN: echo '1' >> %t.in
+; RUN: echo '12' >> %t.in
+; RUN: echo 'end>' >> %t.in
+
+; RUN: echo 'CHECK: start{{(.|[[:space:]])*}}end' > %t.chk
+
+; REDEFINE: %{pre} = MULTILINE-MARKER
+; RUN: %{run-vv}
+; RUN: %{run-v}
+; RUN: %{run}
+
+; MULTILINE-MARKER:<<<<<<
+; MULTILINE-MARKER-NEXT: 1: <start
+; MULTILINE-MARKER-V-NEXT:check:1 ^~~~~~
+; MULTILINE-MARKER-NEXT: 2:
+; MULTILINE-MARKER-V-NEXT:check:1 ~
+; MULTILINE-MARKER-NEXT: 3: 1
+; MULTILINE-MARKER-V-NEXT:check:1 ~~
+; MULTILINE-MARKER-NEXT: 4: 12
+; MULTILINE-MARKER-V-NEXT:check:1 ~~~
+; MULTILINE-MARKER-NEXT: 5: end>
+; MULTILINE-MARKER-V-NEXT:check:1 ~~~
+; MULTILINE-MARKER-NEXT:>>>>>>
+
+;--------------------------------------------------
+; Check empty and single-character match ranges.
+;--------------------------------------------------
+
+; RUN: echo '11' > %t.in
+
+; RUN: echo 'empty at bof: CHECK: {{0?}}' > %t.chk
+; RUN: echo 'single at bof: CHECK: {{1?}}' >> %t.chk
+; RUN: echo 'empty in middle: CHECK: {{0?}}' >> %t.chk
+; RUN: echo 'single in middle: CHECK: {{1?}}' >> %t.chk
+; RUN: echo 'single at eof: CHECK: {{[[:space:]]?}}' >> %t.chk
+; RUN: echo 'empty at eof: CHECK: {{0?}}' >> %t.chk
+
+; REDEFINE: %{pre} = EMPTY-SINGLE-MATCH
+; RUN: %{run-vv}
+; RUN: %{run-v}
+; RUN: %{run}
+
+; EMPTY-SINGLE-MATCH:<<<<<<
+; EMPTY-SINGLE-MATCH-NEXT: 1: 11
+; EMPTY-SINGLE-MATCH-V-NEXT:check:1 ^
+; EMPTY-SINGLE-MATCH-V-NEXT:check:2 ^
+; EMPTY-SINGLE-MATCH-V-NEXT:check:3 ^
+; EMPTY-SINGLE-MATCH-V-NEXT:check:4 ^
+; EMPTY-SINGLE-MATCH-V-NEXT:check:5 ^
+; EMPTY-SINGLE-MATCH-V-NEXT: 2:
+; EMPTY-SINGLE-MATCH-V-NEXT:check:6 ^
+; EMPTY-SINGLE-MATCH-NEXT:>>>>>>
+
+;--------------------------------------------------
+; Check search range for empty file and thus empty search range at start/end of
+; file.
+;--------------------------------------------------
+
+; RUN: echo 'CHECK-NOT: foo' > %t.chk
+; REDEFINE: %{opts} = -input-file=/dev/null -allow-empty
+
+; REDEFINE: %{pre} = NEG-SEARCH-EMPTY-FILE
+; RUN: %{run-vv}
+; RUN: %{run-v}
+; RUN: %{run}
+
+; NEG-SEARCH-EMPTY-FILE:<<<<<<
+; NEG-SEARCH-EMPTY-FILE-VV-NEXT: 1:
+; NEG-SEARCH-EMPTY-FILE-VV-NEXT:eof:1 ^
+; NEG-SEARCH-EMPTY-FILE-VV-NEXT:not:1 {} search range (exclusive bounds)
+; NEG-SEARCH-EMPTY-FILE-NEXT:>>>>>>
+
+; RUN: echo 'CHECK: foo' > %t.chk
+
+; REDEFINE: %{pre} = POS-SEARCH-EMPTY-FILE
+; RUN: not %{run-vv}
+; RUN: not %{run-v}
+; RUN: not %{run}
+
+; Verbose diagnostics are suppressed but not errors.
+; POS-SEARCH-EMPTY-FILE:{{.*}}error:{{.*}}
+
+; POS-SEARCH-EMPTY-FILE:<<<<<<
+; POS-SEARCH-EMPTY-FILE-NEXT: 1:
+; POS-SEARCH-EMPTY-FILE-NEXT:check:1'0 {} search range (exclusive bounds)
+; POS-SEARCH-EMPTY-FILE-NEXT:check:1'1 error: no match found
+; POS-SEARCH-EMPTY-FILE-NEXT:>>>>>>
+
+; REDEFINE: %{opts} =
+
+;--------------------------------------------------
+; Check non-empty search range at start/end of file.
+;--------------------------------------------------
+
+; RUN: echo 'one line' > %t.in
+
+; RUN: echo 'CHECK-NOT: foo' > %t.chk
+
+; REDEFINE: %{pre} = SEARCH-BOF-EOF-ONE-LINE
+; RUN: %{run-vv}
+; RUN: %{run-v}
+; RUN: %{run}
+
+; SEARCH-BOF-EOF-ONE-LINE:<<<<<<
+; SEARCH-BOF-EOF-ONE-LINE-NEXT: 1: one line
+; SEARCH-BOF-EOF-ONE-LINE-VV-NEXT:not:1 { } search range (exclusive bounds)
+; SEARCH-BOF-EOF-ONE-LINE-VV-NEXT: 2:
+; SEARCH-BOF-EOF-ONE-LINE-VV-NEXT:eof:1 ^
+; SEARCH-BOF-EOF-ONE-LINE-NEXT:>>>>>>
+
+; RUN: echo 'line 1' > %t.in
+; RUN: echo 'line 2' >> %t.in
+
+; RUN: echo 'CHECK: foo' > %t.chk
+
+; REDEFINE: %{pre} = SEARCH-BOF-EOF-MULTI-LINE
+; RUN: not %{run-vv}
+; RUN: not %{run-v}
+; RUN: not %{run}
+
+; Verbose diagnostics are suppressed but not errors.
+; SEARCH-BOF-EOF-MULTI-LINE:{{.*}}error:{{.*}}
+
+; SEARCH-BOF-EOF-MULTI-LINE:<<<<<<
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT: 1: line 1
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT:check:1'0 { search range start (exclusive)
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT:check:1'1 error: no match found
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT: 2: line 2
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT:check:1'2 } search range end (exclusive)
+; SEARCH-BOF-EOF-MULTI-LINE-NEXT:>>>>>>
+
+;--------------------------------------------------
+; Check search ranges at line boundaries but not file boundaries. Also check
+; empty search ranges (for non-empty file) and one-character search ranges.
+;--------------------------------------------------
+
+; RUN: echo 'start' > %t.in
+; RUN: echo 'empty' >> %t.in
+; RUN: echo 'next' >> %t.in
+; RUN: echo 'one char' >> %t.in
+; RUN: echo 'next' >> %t.in
+; RUN: echo 'one line' >> %t.in
+; RUN: echo 'next' >> %t.in
+; RUN: echo 'multi' >> %t.in
+; RUN: echo 'line' >> %t.in
+; RUN: echo 'next' >> %t.in
+; RUN: echo 'off by one' >> %t.in
+; RUN: echo ' end' >> %t.in
+
+; RUN: echo ' 1: CHECK: start{{[[:space:]]}}' > %t.chk
+; RUN: echo ' 2: CHECK-NOT: foo' >> %t.chk
+; RUN: echo ' 3: CHECK: empty' >> %t.chk
+; RUN: echo ' 4: CHECK-NOT: foo' >> %t.chk
+; RUN: echo ' 5: CHECK: {{[[:space:]]}}next{{[[:space:]]}}' >> %t.chk
+; RUN: echo ' 6: CHECK-NOT: foo' >> %t.chk
+; RUN: echo ' 7: CHECK: ne char' >> %t.chk
+; RUN: echo ' 8: CHECK-NOT: foo' >> %t.chk
+; RUN: echo ' 9: CHECK: next{{[[:space:]]}}' >> %t.chk
+; RUN: echo '10: CHECK-NOT: foo' >> %t.chk
+; RUN: echo '11: CHECK: next{{[[:space:]]}}' >> %t.chk
+; RUN: echo '12: CHECK-NOT: foo' >> %t.chk
+; RUN: echo '13: CHECK: next' >> %t.chk
+; RUN: echo '14: CHECK-NOT: foo' >> %t.chk
+; RUN: echo '15: CHECK: end' >> %t.chk
+
+; REDEFINE: %{pre} = SEARCH-LINE-BOUNDS
+; RUN: %{run-vv}
+; RUN: %{run-v}
+; RUN: %{run}
+
+; SEARCH-LINE-BOUNDS:<<<<<<
+; SEARCH-LINE-BOUNDS-NEXT: 1: start
+; SEARCH-LINE-BOUNDS-V-NEXT:check:1 ^~~~~~
+; SEARCH-LINE-BOUNDS-NEXT: 2: empty
+; SEARCH-LINE-BOUNDS-V-NEXT:check:3 ^~~~~
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:2 {} search range (exclusive bounds)
+; SEARCH-LINE-BOUNDS-V-NEXT:check:5 ^
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:4 {} search range (exclusive bounds)
+; SEARCH-LINE-BOUNDS-NEXT: 3: next
+; SEARCH-LINE-BOUNDS-V-NEXT:check:5 ~~~~~
+; SEARCH-LINE-BOUNDS-NEXT: 4: one char
+; SEARCH-LINE-BOUNDS-V-NEXT:check:7 ^~~~~~~
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:6 { } search range (exclusive bounds)
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:8 { } search range (exclusive bounds)
+; SEARCH-LINE-BOUNDS-NEXT: 5: next
+; SEARCH-LINE-BOUNDS-V-NEXT:check:9 ^~~~~
+; SEARCH-LINE-BOUNDS-NEXT: 6: one line
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:10 { } search range (exclusive bounds)
+; SEARCH-LINE-BOUNDS-NEXT: 7: next
+; SEARCH-LINE-BOUNDS-V-NEXT:check:11 ^~~~~
+; SEARCH-LINE-BOUNDS-NEXT: 8: multi
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:12'0 { search range start (exclusive)
+; SEARCH-LINE-BOUNDS-NEXT: 9: line
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:12'1 } search range end (exclusive)
+; SEARCH-LINE-BOUNDS-NEXT: 10: next
+; SEARCH-LINE-BOUNDS-V-NEXT:check:13 ^~~~
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:14'0 { search range start (exclusive)
+; SEARCH-LINE-BOUNDS-NEXT: 11: off by one
+; SEARCH-LINE-BOUNDS-NEXT: 12: end
+; SEARCH-LINE-BOUNDS-V-NEXT:check:15 ^~~
+; SEARCH-LINE-BOUNDS-VV-NEXT:not:14'1 } search range end (exclusive)
+; SEARCH-LINE-BOUNDS-NEXT:>>>>>>
diff --git a/llvm/test/FileCheck/dump-input/context.txt b/llvm/test/FileCheck/dump-input/context.txt
index 78092a909dbd8..8a567a5d567b2 100644
--- a/llvm/test/FileCheck/dump-input/context.txt
+++ b/llvm/test/FileCheck/dump-input/context.txt
@@ -52,111 +52,113 @@
; RUN: echo 'CHECK-LABEL: lab1' > %t.chk
; RUN: echo ' CHECK-NEXT: hello' >> %t.chk
; RUN: echo 'CHECK-LABEL: lab2' >> %t.chk
-; RUN: echo ' CHECK-NEXT: world' >> %t.chk
-
-; C0: <<<<<<
-; CS-NEXT: .
-; CS-NEXT: .
-; CS-NEXT: .
-; C5-NEXT: 1: foo8
-; C5-NEXT: 2: foo7
-; C5-NEXT: 3: foo6
-; C5-NEXT: 4: foo5
-; C4-NEXT: 5: foo4
-; C3-NEXT: 6: foo3
-; C2-NEXT: 7: foo2
-; C1-NEXT: 8: foo1
-; C0-NEXT: 9: lab1 hello
-; C0-NEXT: label:1'0 ^~~~
-; C0-NEXT: label:1'1 ^~~~
-; C0-NEXT: next:2 !~~~~ error: match on wrong line
-; C1-NEXT: 10: foo1
-; C2-NEXT: 11: foo2
-; C3-NEXT: 12: foo3
-; C4-NEXT: 13: foo4
-; C5-NEXT: 14: foo5
-; C6-NEXT: 15: foo6
-; C6-NEXT: 16: foo7
-; CM-NEXT: .
-; CM-NEXT: .
-; CM-NEXT: .
-; C6-NEXT: 17: foo7
-; C6-NEXT: 18: foo6
-; C5-NEXT: 19: foo5
-; C4-NEXT: 20: foo4
-; C3-NEXT: 21: foo3
-; C2-NEXT: 22: foo2
-; C1-NEXT: 23: foo1
-; C0-NEXT: 24: lab2 world
-; C0-NEXT: label:3 ^~~~
-; C0-NEXT: next:4 !~~~~ error: match on wrong line
-; C1-NEXT: 25: foo1
-; C2-NEXT: 26: foo2
-; C3-NEXT: 27: foo3
-; C4-NEXT: 28: foo4
-; C5-NEXT: 29: foo5
-; C6-NEXT: 30: foo6
-; C7-NEXT: 31: foo7
-; C7-NEXT: 32: foo8
-; C7-NEXT: 33: foo9
-; C7-NEXT: 34: foo0
-; CE-NEXT: .
-; CE-NEXT: .
-; CE-NEXT: .
-; C0-NEXT: >>>>>>
+
+; C0:<<<<<<
+; CS-NEXT: .
+; CS-NEXT: .
+; CS-NEXT: .
+; C5-NEXT: 1: foo8
+; C5-NEXT: 2: foo7
+; C5-NEXT: 3: foo6
+; C5-NEXT: 4: foo5
+; C4-NEXT: 5: foo4
+; C3-NEXT: 6: foo3
+; C2-NEXT: 7: foo2
+; C1-NEXT: 8: foo1
+; C0-NEXT: 9: lab1 hello
+; C0-NEXT:label:1'0 ^~~~
+; C0-NEXT:label:1'1 ^~~~
+; C0-NEXT:next:2'0 { search range start (exclusive)
+; C0-NEXT:next:2'1 !~~~~ error: match on wrong line
+; C1-NEXT: 10: foo1
+; C2-NEXT: 11: foo2
+; C3-NEXT: 12: foo3
+; C4-NEXT: 13: foo4
+; C5-NEXT: 14: foo5
+; C6-NEXT: 15: foo6
+; C6-NEXT: 16: foo7
+; CM-NEXT: .
+; CM-NEXT: .
+; CM-NEXT: .
+; C6-NEXT: 17: foo7
+; C6-NEXT: 18: foo6
+; C5-NEXT: 19: foo5
+; C4-NEXT: 20: foo4
+; C3-NEXT: 21: foo3
+; C2-NEXT: 22: foo2
+; C1-NEXT: 23: foo1
+; C0-NEXT: 24: lab2 world
+; C0-NEXT:label:3 ^~~~
+; C0-NEXT:next:2'2 } search range end (exclusive)
+; C1-NEXT: 25: foo1
+; C2-NEXT: 26: foo2
+; C3-NEXT: 27: foo3
+; C4-NEXT: 28: foo4
+; C5-NEXT: 29: foo5
+; C6-NEXT: 30: foo6
+; C7-NEXT: 31: foo7
+; C7-NEXT: 32: foo8
+; C7-NEXT: 33: foo9
+; C7-NEXT: 34: foo0
+; CE-NEXT: .
+; CE-NEXT: .
+; CE-NEXT: .
+; C0-NEXT:>>>>>>
; Now build an alternate set of checks where input lines that might be elided by
; ellipses have annotations.
; RUN: cp %t.in %t.wide.in
-; RUN: echo 'CHECK-LABEL: lab1' > %t.wide.chk
-; RUN: echo ' CHECK: hello' >> %t.wide.chk
-; RUN: echo ' CHECK: goodbye' >> %t.wide.chk
-; RUN: echo 'CHECK-LABEL: lab2' >> %t.wide.chk
-; RUN: echo ' CHECK-NEXT: world' >> %t.wide.chk
-
-; W5: <<<<<<
-; W5: 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: 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
+; RUN: echo 'CHECK-LABEL: lab1' > %t.wide.chk
+; RUN: echo ' CHECK-DAG: hello{{([foo0-9[:space:]])*}}' >> %t.wide.chk
+; RUN: echo ' CHECK-DAG: goodbye' >> %t.wide.chk
+; RUN: echo 'CHECK-LABEL: lab2' >> %t.wide.chk
+; RUN: echo ' CHECK-NEXT: world' >> %t.wide.chk
+
+; W5:<<<<<<
+; W5: 9: lab1 hello
+; W5-NEXT:label:1'0 ^~~~
+; W5-NEXT:label:1'1 ^~~~
+; W5-NEXT:dag:2 ^~~~~~
+; W5-NEXT:dag:3'0 { search range start (exclusive)
+; W5-NEXT:dag:3'1 error: no match found
+; W5-NEXT: 10: foo1
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT:dag:3'2 ? possible intended match
+; W5-NEXT: 11: foo2
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 12: foo3
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 13: foo4
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 14: foo5
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 15: foo6
+; W5-NEXT:dag:2 ~~~~~
+; W6-NEXT: 16: foo7
+; W6-NEXT:dag:2 ~~~~~
+; WM-NEXT: .
+; WM-NEXT: .
+; WM-NEXT: .
+; W6-NEXT: 17: foo7
+; W6-NEXT:dag:2 ~~~~~
+; W6-NEXT: 18: foo6
+; W6-NEXT:dag:2 ~~~~~
+; W5-NEXT: 19: foo5
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 20: foo4
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 21: foo3
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 22: foo2
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 23: foo1
+; W5-NEXT:dag:2 ~~~~~
+; W5-NEXT: 24: lab2 world
+; W5-NEXT:label:4 ^~~~
+; W5-NEXT:dag:3'3 } search range end (exclusive)
+; W5-NEXT:next:5'0 { search range start (exclusive)
+; W5-NEXT:next:5'1 !~~~~ error: match on wrong line
;--------------------------------------------------
; Check -dump-input-context=<bad value>.
@@ -172,7 +174,7 @@
; RUN: -dump-input-context=foobar \
; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL -DVAL=foobar
-BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-context option: '[[VAL]]' value invalid for uint argument!
+BADVAL:{{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-context option: '[[VAL]]' value invalid for uint argument!
;--------------------------------------------------
; Check -dump-input-context explicit values.
diff --git a/llvm/test/FileCheck/dump-input/enable.txt b/llvm/test/FileCheck/dump-input/enable.txt
index 89b56960885e3..58472eb4751f0 100644
--- a/llvm/test/FileCheck/dump-input/enable.txt
+++ b/llvm/test/FileCheck/dump-input/enable.txt
@@ -27,7 +27,7 @@
; RUN: %ProtectFileCheckOutput not FileCheck -dump-input=foobar 2>&1 \
; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL
-BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input option: Cannot find option named 'foobar'!
+BADVAL:{{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input option: Cannot find option named 'foobar'!
;--------------------------------------------------
; Check -dump-input=help.
@@ -228,28 +228,29 @@ BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input option: Cannot find op
; HELP-NOT: {{.}}
; Trace is sometimes suppressed.
-; TRACE: {{.*}}remark:{{.*}}
-; NOTRACE-NOT: remark:
+; TRACE:{{.*}}remark:{{.*}}
+; NOTRACE-NOT:remark:
; Error diagnostics are never suppressed.
-; ERR: {{.*}}error:{{.*}}
-
-; NODUMP-NOT: <<<<<<
-
-; DUMP-OK: Input was:
-; DUMP-OK-NEXT: <<<<<<
-; DUMP-OK-NEXT: 1: hello
-; DUMP-OK-NEXT: check:1 ^~~~~
-; DUMP-OK-NEXT: 2: world
-; DUMP-OK-NEXT: next:2 ^~~~~
-; DUMP-OK-NEXT: >>>>>>
-
-; DUMP-ERR: Input was:
-; DUMP-ERR-NEXT: <<<<<<
-; DUMP-ERR-NEXT: 1: hello
-; DUMP-ERR-V-NEXT: check:1 ^~~~~
-; DUMP-ERR-NEXT: next:2'0 X error: no match found
-; DUMP-ERR-NEXT: 2: whirled
-; DUMP-ERR-NEXT: next:2'0 ~~~~~~~~
-; DUMP-ERR-NEXT: next:2'1 ? possible intended match
-; DUMP-ERR-NEXT: >>>>>>
+; ERR:{{.*}}error:{{.*}}
+
+; NODUMP-NOT:<<<<<<
+
+; DUMP-OK:Input was:
+; DUMP-OK-NEXT:<<<<<<
+; DUMP-OK-NEXT: 1: hello
+; DUMP-OK-NEXT:check:1 ^~~~~
+; DUMP-OK-NEXT: 2: world
+; DUMP-OK-NEXT:next:2 ^~~~~
+; DUMP-OK-NEXT:>>>>>>
+
+; DUMP-ERR:Input was:
+; DUMP-ERR-NEXT:<<<<<<
+; DUMP-ERR-NEXT: 1: hello
+; DUMP-ERR-V-NEXT:check:1 ^~~~~
+; DUMP-ERR-NEXT:next:2'0 { search range start (exclusive)
+; DUMP-ERR-NEXT:next:2'1 error: no match found
+; DUMP-ERR-NEXT: 2: whirled
+; DUMP-ERR-NEXT:next:2'2 ? possible intended match
+; DUMP-ERR-NEXT:next:2'3 } search range end (exclusive)
+; DUMP-ERR-NEXT:>>>>>>
diff --git a/llvm/test/FileCheck/dump-input/filter.txt b/llvm/test/FileCheck/dump-input/filter.txt
index 6ea858d2126e6..7f7df8e2d1095 100644
--- a/llvm/test/FileCheck/dump-input/filter.txt
+++ b/llvm/test/FileCheck/dump-input/filter.txt
@@ -30,7 +30,7 @@
; RUN: echo foo8 >> %t.in
; RUN: echo foo9 >> %t.in
; line 23
-; RUN: echo word >> %t.in
+; RUN: echo world >> %t.in
; RUN: echo foo0 >> %t.in
; RUN: echo foo1 >> %t.in
; RUN: echo foo2 >> %t.in
@@ -42,207 +42,290 @@
; RUN: echo foo8 >> %t.in
; RUN: echo foo9 >> %t.in
; line 34
-; RUN: echo end >> %t.in
-
-; RUN: echo 'CHECK: start' > %t.chk
-; RUN: echo 'CHECK: hello' >> %t.chk
-; RUN: echo 'CHECK: world' >> %t.chk
-; RUN: echo 'CHECK: end' >> %t.chk
+; RUN: echo bye >> %t.in
+; RUN: echo foo0 >> %t.in
+; RUN: echo foo1 >> %t.in
+; RUN: echo foo2 >> %t.in
+; RUN: echo foo3 >> %t.in
+; RUN: echo foo4 >> %t.in
+; RUN: echo foo5 >> %t.in
+; RUN: echo foo6 >> %t.in
+; RUN: echo foo7 >> %t.in
+; RUN: echo foo8 >> %t.in
+; RUN: echo foo9 >> %t.in
+; line 45
+; RUN: echo sleep >> %t.in
+; RUN: echo foo0 >> %t.in
+; RUN: echo foo1 >> %t.in
+; RUN: echo foo2 >> %t.in
+; RUN: echo foo3 >> %t.in
+; RUN: echo foo4 >> %t.in
+; RUN: echo foo5 >> %t.in
+; RUN: echo foo6 >> %t.in
+; RUN: echo foo7 >> %t.in
+; RUN: echo foo8 >> %t.in
+; RUN: echo foo9 >> %t.in
+; RUN: echo end >> %t.in
+
+; RUN: echo 'CHECK: start' > %t.chk
+; RUN: echo 'CHECK: hello{{(.|[[:space:]])*}}world' >> %t.chk
+; RUN: echo 'CHECK-NOT: bye{{(.|[[:space:]])*}}sleep' >> %t.chk
+; RUN: echo 'CHECK: end' >> %t.chk
;--------------------------------------------------
; Directives for checking the dump.
;--------------------------------------------------
-; ALL: <<<<<<
-; ALL-NEXT: 1: start
-; ALL-NEXT: check:1 ^~~~~
-; ALL-NEXT: 2: foo0
-; ALL-NEXT: 3: foo1
-; ALL-NEXT: 4: foo2
-; ALL-NEXT: 5: foo3
-; ALL-NEXT: 6: foo4
-; ALL-NEXT: 7: foo5
-; ALL-NEXT: 8: foo6
-; ALL-NEXT: 9: foo7
-; ALL-NEXT: 10: foo8
-; ALL-NEXT: 11: foo9
-; ALL-NEXT: 12: hello
-; 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: >>>>>>
-
-; ANNOTATION-FULL: <<<<<<
-; ANNOTATION-FULL-NEXT: 1: start
-; ANNOTATION-FULL-NEXT: check:1 ^~~~~
-; ANNOTATION-FULL-NEXT: 2: foo0
-; ANNOTATION-FULL-NEXT: 3: foo1
-; ANNOTATION-FULL-NEXT: .
-; ANNOTATION-FULL-NEXT: .
-; ANNOTATION-FULL-NEXT: .
-; ANNOTATION-FULL-NEXT: 10: foo8
-; ANNOTATION-FULL-NEXT: 11: foo9
-; ANNOTATION-FULL-NEXT: 12: hello
-; 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: 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: 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: >>>>>>
-
-; ANNOTATION: <<<<<<
-; ANNOTATION-NEXT: 1: start
-; ANNOTATION-NEXT: check:1 ^~~~~
-; ANNOTATION-NEXT: 2: foo0
-; ANNOTATION-NEXT: 3: foo1
-; ANNOTATION-NEXT: .
-; ANNOTATION-NEXT: .
-; ANNOTATION-NEXT: .
-; ANNOTATION-NEXT: 10: foo8
-; ANNOTATION-NEXT: 11: foo9
-; ANNOTATION-NEXT: 12: hello
-; 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: .
-; ANNOTATION-NEXT: >>>>>>
-
-; ERROR: <<<<<<
-; ERROR-NEXT: .
-; ERROR-NEXT: .
-; ERROR-NEXT: .
-; ERROR-NEXT: 10: foo8
-; ERROR-NEXT: 11: foo9
-; ERROR-NEXT: 12: hello
-; 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: .
-; ERROR-NEXT: >>>>>>
+; ALL:<<<<<<
+; ALL-NEXT: 1: start
+; ALL-NEXT:check:1 ^~~~~
+; ALL-NEXT: 2: foo0
+; ALL-NEXT: 3: foo1
+; ALL-NEXT: 4: foo2
+; ALL-NEXT: 5: foo3
+; ALL-NEXT: 6: foo4
+; ALL-NEXT: 7: foo5
+; ALL-NEXT: 8: foo6
+; ALL-NEXT: 9: foo7
+; ALL-NEXT: 10: foo8
+; ALL-NEXT: 11: foo9
+; ALL-NEXT: 12: hello
+; ALL-NEXT:check:2 ^~~~~~
+; ALL-NEXT: 13: foo0
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 14: foo1
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 15: foo2
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 16: foo3
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 17: foo4
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 18: foo5
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 19: foo6
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 20: foo7
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 21: foo8
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 22: foo9
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT: 23: world
+; ALL-NEXT:check:2 ~~~~~
+; ALL-NEXT:not:3'0 { search range start (exclusive)
+; ALL-NEXT: 24: foo0
+; ALL-NEXT: 25: foo1
+; ALL-NEXT: 26: foo2
+; ALL-NEXT: 27: foo3
+; ALL-NEXT: 28: foo4
+; ALL-NEXT: 29: foo5
+; ALL-NEXT: 30: foo6
+; ALL-NEXT: 31: foo7
+; ALL-NEXT: 32: foo8
+; ALL-NEXT: 33: foo9
+; ALL-NEXT: 34: bye
+; ALL-NEXT:not:3'1 !~~~ error: no match expected
+; ALL-NEXT: 35: foo0
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 36: foo1
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 37: foo2
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 38: foo3
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 39: foo4
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 40: foo5
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 41: foo6
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 42: foo7
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 43: foo8
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 44: foo9
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 45: sleep
+; ALL-NEXT:not:3'1 ~~~~~
+; ALL-NEXT: 46: foo0
+; ALL-NEXT: 47: foo1
+; ALL-NEXT: 48: foo2
+; ALL-NEXT: 49: foo3
+; ALL-NEXT: 50: foo4
+; ALL-NEXT: 51: foo5
+; ALL-NEXT: 52: foo6
+; ALL-NEXT: 53: foo7
+; ALL-NEXT: 54: foo8
+; ALL-NEXT: 55: foo9
+; ALL-NEXT:not:3'2 } search range end (exclusive)
+; ALL-NEXT: 56: end
+; ALL-NEXT:check:4 ^~~
+; ALL-NEXT:>>>>>>
+
+; ANNOTATION-FULL:<<<<<<
+; ANNOTATION-FULL-NEXT: 1: start
+; ANNOTATION-FULL-NEXT:check:1 ^~~~~
+; ANNOTATION-FULL-NEXT: 2: foo0
+; ANNOTATION-FULL-NEXT: 3: foo1
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: 10: foo8
+; ANNOTATION-FULL-NEXT: 11: foo9
+; ANNOTATION-FULL-NEXT: 12: hello
+; ANNOTATION-FULL-NEXT:check:2 ^~~~~~
+; ANNOTATION-FULL-NEXT: 13: foo0
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 14: foo1
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 15: foo2
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 16: foo3
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 17: foo4
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 18: foo5
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 19: foo6
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 20: foo7
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 21: foo8
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 22: foo9
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT: 23: world
+; ANNOTATION-FULL-NEXT:check:2 ~~~~~
+; ANNOTATION-FULL-NEXT:not:3'0 { search range start (exclusive)
+; ANNOTATION-FULL-NEXT: 24: foo0
+; ANNOTATION-FULL-NEXT: 25: foo1
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: 32: foo8
+; ANNOTATION-FULL-NEXT: 33: foo9
+; ANNOTATION-FULL-NEXT: 34: bye
+; ANNOTATION-FULL-NEXT:not:3'1 !~~~ error: no match expected
+; ANNOTATION-FULL-NEXT: 35: foo0
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 36: foo1
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 37: foo2
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 38: foo3
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 39: foo4
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 40: foo5
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 41: foo6
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 42: foo7
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 43: foo8
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 44: foo9
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 45: sleep
+; ANNOTATION-FULL-NEXT:not:3'1 ~~~~~
+; ANNOTATION-FULL-NEXT: 46: foo0
+; ANNOTATION-FULL-NEXT: 47: foo1
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: .
+; ANNOTATION-FULL-NEXT: 53: foo7
+; ANNOTATION-FULL-NEXT: 54: foo8
+; ANNOTATION-FULL-NEXT: 55: foo9
+; ANNOTATION-FULL-NEXT:not:3'2 } search range end (exclusive)
+; ANNOTATION-FULL-NEXT: 56: end
+; ANNOTATION-FULL-NEXT:check:4 ^~~
+; ANNOTATION-FULL-NEXT:>>>>>>
+
+; ANNOTATION:<<<<<<
+; ANNOTATION-NEXT: 1: start
+; ANNOTATION-NEXT:check:1 ^~~~~
+; ANNOTATION-NEXT: 2: foo0
+; ANNOTATION-NEXT: 3: foo1
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: 10: foo8
+; ANNOTATION-NEXT: 11: foo9
+; ANNOTATION-NEXT: 12: hello
+; ANNOTATION-NEXT:check:2 ^~~~~~
+; ANNOTATION-NEXT: 13: foo0
+; ANNOTATION-NEXT:check:2 ~~~~~
+; ANNOTATION-NEXT: 14: foo1
+; ANNOTATION-NEXT:check:2 ~~~~~
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: 21: foo8
+; ANNOTATION-NEXT:check:2 ~~~~~
+; ANNOTATION-NEXT: 22: foo9
+; ANNOTATION-NEXT:check:2 ~~~~~
+; ANNOTATION-NEXT: 23: world
+; ANNOTATION-NEXT:check:2 ~~~~~
+; ANNOTATION-NEXT:not:3'0 { search range start (exclusive)
+; ANNOTATION-NEXT: 24: foo0
+; ANNOTATION-NEXT: 25: foo1
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: 32: foo8
+; ANNOTATION-NEXT: 33: foo9
+; ANNOTATION-NEXT: 34: bye
+; ANNOTATION-NEXT:not:3'1 !~~~ error: no match expected
+; ANNOTATION-NEXT: 35: foo0
+; ANNOTATION-NEXT:not:3'1 ~~~~~
+; ANNOTATION-NEXT: 36: foo1
+; ANNOTATION-NEXT:not:3'1 ~~~~~
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: .
+; ANNOTATION-NEXT: 53: foo7
+; ANNOTATION-NEXT: 54: foo8
+; ANNOTATION-NEXT: 55: foo9
+; ANNOTATION-NEXT:not:3'2 } search range end (exclusive)
+; ANNOTATION-NEXT: 56: end
+; ANNOTATION-NEXT:check:4 ^~~
+; ANNOTATION-NEXT:>>>>>>
+
+; ERROR:<<<<<<
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: 21: foo8
+; ERROR-NEXT:check:2 ~~~~~
+; ERROR-NEXT: 22: foo9
+; ERROR-NEXT:check:2 ~~~~~
+; ERROR-NEXT: 23: world
+; ERROR-NEXT:check:2 ~~~~~
+; ERROR-NEXT:not:3'0 { search range start (exclusive)
+; ERROR-NEXT: 24: foo0
+; ERROR-NEXT: 25: foo1
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: 32: foo8
+; ERROR-NEXT: 33: foo9
+; ERROR-NEXT: 34: bye
+; ERROR-NEXT:not:3'1 !~~~ error: no match expected
+; ERROR-NEXT: 35: foo0
+; ERROR-NEXT:not:3'1 ~~~~~
+; ERROR-NEXT: 36: foo1
+; ERROR-NEXT:not:3'1 ~~~~~
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: 53: foo7
+; ERROR-NEXT: 54: foo8
+; ERROR-NEXT: 55: foo9
+; ERROR-NEXT:not:3'2 } search range end (exclusive)
+; ERROR-NEXT: 56: end
+; ERROR-NEXT:check:4 ^~~
+; ERROR-NEXT:>>>>>>
;--------------------------------------------------
; Check -dump-input-filter=<bad value>.
@@ -253,7 +336,7 @@
; RUN: -dump-input-filter=foobar \
; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL
-BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-filter option: Cannot find option named 'foobar'!
+BADVAL:{{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-filter option: Cannot find option named 'foobar'!
;--------------------------------------------------
; Check -dump-input-filter explicit values.
@@ -380,47 +463,123 @@ BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input-filter option: Cannot
; RUN: < %t.in 2>&1 \
; RUN: | FileCheck %s -match-full-lines -check-prefixes=EMPTY
-; EMPTY: <<<<<<
-; EMPTY-NEXT: .
-; EMPTY-NEXT: .
-; EMPTY-NEXT: .
-; EMPTY-NEXT: >>>>>>
+; EMPTY:<<<<<<
+; EMPTY-NEXT: .
+; EMPTY-NEXT: .
+; EMPTY-NEXT: .
+; EMPTY-NEXT:>>>>>>
;--------------------------------------------------
; Check that other kinds of errors are included by -dump-input-filter=error.
;
-; "error: no match found" and "possible intended match" are checked above.
+; "error: no match expected" is checked above.
;--------------------------------------------------
;- - - - - - - - - - - - - - - - - - - - - - - - -
-; error: no match expected.
+; error: no match found.
+; possible intended match
;- - - - - - - - - - - - - - - - - - - - - - - - -
-; RUN: echo 'foo' > %t.not-err.in
-; RUN: echo 'CHECK-NOT: foo' > %t.not-err.chk
+; RUN: echo 'start' > %t.chk-err.in
+; RUN: echo 'foo0' >> %t.chk-err.in
+; RUN: echo 'foo1' >> %t.chk-err.in
+; RUN: echo 'foo2' >> %t.chk-err.in
+; RUN: echo 'foo3' >> %t.chk-err.in
+; RUN: echo 'hello' >> %t.chk-err.in
+; RUN: echo 'foo0' >> %t.chk-err.in
+; RUN: echo 'foo1' >> %t.chk-err.in
+; RUN: echo 'foo2' >> %t.chk-err.in
+; RUN: echo 'foo3' >> %t.chk-err.in
+; RUN: echo 'end' >> %t.chk-err.in
+
+; RUN: echo 'CHECK: jello' > %t.chk-err.chk
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -dump-input-context=0 -dump-input-filter=error \
-; RUN: %t.not-err.chk < %t.not-err.in 2>&1 \
-; RUN: | FileCheck %s -match-full-lines -check-prefixes=NOT-ERR
-
-; NOT-ERR: 1: foo
-; NOT-ERR-NEXT: not:1 !~~ error: no match expected
+; RUN: %t.chk-err.chk < %t.chk-err.in 2>&1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=CHK-ERR
+
+; CHK-ERR:<<<<<<
+; CHK-ERR-NEXT: 1: start
+; CHK-ERR-NEXT:check:1'0 { search range start (exclusive)
+; CHK-ERR-NEXT:check:1'1 error: no match found
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: 6: hello
+; CHK-ERR-NEXT:check:1'2 ? possible intended match
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: .
+; CHK-ERR-NEXT: 11: end
+; CHK-ERR-NEXT:check:1'3 } search range end (exclusive)
+; CHK-ERR-NEXT:>>>>>>
;- - - - - - - - - - - - - - - - - - - - - - - - -
; error: match on wrong line.
;- - - - - - - - - - - - - - - - - - - - - - - - -
-; RUN: echo 'foo' > %t.next-err.in
-; RUN: echo 'foo' >> %t.next-err.in
-; RUN: echo 'bar' >> %t.next-err.in
-; RUN: echo 'CHECK: foo' > %t.next-err.chk
-; RUN: echo 'CHECK-NEXT: bar' >> %t.next-err.chk
+; RUN: echo 'start' > %t.next-err.in
+; RUN: echo 'foo0' >> %t.next-err.in
+; RUN: echo 'foo1' >> %t.next-err.in
+; RUN: echo 'foo2' >> %t.next-err.in
+; RUN: echo 'foo3' >> %t.next-err.in
+; RUN: echo 'hello' >> %t.next-err.in
+; RUN: echo 'foo0' >> %t.next-err.in
+; RUN: echo 'foo1' >> %t.next-err.in
+; RUN: echo 'foo2' >> %t.next-err.in
+; RUN: echo 'foo3' >> %t.next-err.in
+; RUN: echo 'end' >> %t.next-err.in
+
+; RUN: echo 'CHECK: start' > %t.next-err.chk
+; RUN: echo 'CHECK-NEXT: hello' >> %t.next-err.chk
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck -dump-input-context=0 -dump-input-filter=error \
; RUN: %t.next-err.chk < %t.next-err.in 2>&1 \
; RUN: | FileCheck %s -match-full-lines -check-prefixes=NEXT-ERR
-; NEXT-ERR: 3: bar
-; NEXT-ERR-NEXT: next:2 !~~ error: match on wrong line
+; NEXT-ERR:<<<<<<
+; NEXT-ERR-NEXT: 1: start
+; NEXT-ERR-NEXT:next:2'0 { search range start (exclusive)
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: 6: hello
+; NEXT-ERR-NEXT:next:2'1 !~~~~ error: match on wrong line
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: .
+; NEXT-ERR-NEXT: 11: end
+; NEXT-ERR-NEXT:next:2'2 } search range end (exclusive)
+; NEXT-ERR-NEXT:>>>>>>
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; error: match failed for invalid pattern.
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+
+; RUN: echo 'start' > %t.invalid.in
+; RUN: echo 'foo0' >> %t.invalid.in
+; RUN: echo 'foo1' >> %t.invalid.in
+; RUN: echo 'foo2' >> %t.invalid.in
+; RUN: echo 'foo3' >> %t.invalid.in
+; RUN: echo 'end' >> %t.invalid.in
+
+; RUN: echo 'CHECK: [[#0x0 - 0x1]]' > %t.invalid.chk
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=0 -dump-input-filter=error \
+; RUN: %t.invalid.chk < %t.invalid.in 2>&1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=INVALID -strict-whitespace
+
+; INVALID:<<<<<<
+; INVALID-NEXT: 1: start
+; INVALID-NEXT:check:1'0 { search range start (exclusive)
+; INVALID-NEXT:check:1'1 error: match failed for invalid pattern
+; INVALID-NEXT:check:1'2 unable to substitute variable or numeric expression: overflow error
+; INVALID-NEXT: .
+; INVALID-NEXT: .
+; INVALID-NEXT: .
+; INVALID-NEXT: 6: end
+; INVALID-NEXT:check:1'3 } search range end (exclusive)
+; INVALID-NEXT:>>>>>>
diff --git a/llvm/test/FileCheck/dump-input/search-range-annotations/check-label-follows.txt b/llvm/test/FileCheck/dump-input/search-range-annotations/check-label-follows.txt
new file mode 100644
index 0000000000000..b003a9ce4aabd
--- /dev/null
+++ b/llvm/test/FileCheck/dump-input/search-range-annotations/check-label-follows.txt
@@ -0,0 +1,74 @@
+; Because -dump-input-filter=error includes a failed directive's search range
+; end, input line 15 below is revealed, and -dump-input-context=2 reveals the
+; surrounding lines. Those lines are where the actual problem is: the "host
+; triples:" header is off by one. Sometimes the key to understanding a
+; directive's failure is where the following CHECK-LABEL matched because it
+; limits the search range.
+
+RUN: rm -rf %t
+RUN: split-file %s %t
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t/check.txt < %t/input.txt \
+RUN: -dump-input-filter=error 2>&1 | \
+RUN: FileCheck %s -match-full-lines -check-prefix=DMP
+
+ DMP:<<<<<<
+DMP-NEXT: 1: offload triples:
+DMP-NEXT:label:1'0 ^~~~~~~~~~~~~~~~
+DMP-NEXT:label:1'1 ^~~~~~~~~~~~~~~~
+DMP-NEXT:check:2'0 { search range start (exclusive)
+DMP-NEXT:check:2'1 error: no match found
+DMP-NEXT: 2: - x86_64-linux-gnu
+DMP-NEXT:check:2'2 ? possible intended match
+DMP-NEXT: 3: - x86_64-unknown-linux-gnu
+DMP-NEXT: 4: - x86_64-pc-linux-gnu
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: 13: - x86_64-linux-android
+DMP-NEXT: 14: - amdgcn-amd-amdhsa
+DMP-NEXT: 15: host triples:
+DMP-NEXT:label:3 ^~~~~~~~~~~~~
+DMP-NEXT:check:2'3 } search range end (exclusive)
+DMP-NEXT: 16: - nvptx64-nvidia-cuda
+DMP-NEXT: 17: - x86_64-linux-gnu
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT:>>>>>>
+
+;--- check.txt
+CHECK-LABEL: offload triples:
+ CHECK: nvptx64-nvidia-cuda
+CHECK-LABEL: host triples:
+
+;--- input.txt
+offload triples:
+- x86_64-linux-gnu
+- x86_64-unknown-linux-gnu
+- x86_64-pc-linux-gnu
+- x86_64-redhat-linux6E
+- x86_64-redhat-linux
+- x86_64-suse-linux
+- x86_64-manbo-linux-gnu
+- x86_64-linux-gnu
+- x86_64-slackware-linux
+- x86_64-unknown-linux
+- x86_64-amazon-linux
+- x86_64-linux-android
+- amdgcn-amd-amdhsa
+host triples:
+- nvptx64-nvidia-cuda
+- x86_64-linux-gnu
+- x86_64-unknown-linux-gnu
+- x86_64-pc-linux-gnu
+- x86_64-redhat-linux6E
+- x86_64-redhat-linux
+- x86_64-suse-linux
+- x86_64-manbo-linux-gnu
+- x86_64-linux-gnu
+- x86_64-slackware-linux
+- x86_64-unknown-linux
+- x86_64-amazon-linux
+- x86_64-linux-android
diff --git a/llvm/test/FileCheck/dump-input/search-range-annotations/check-next-same.txt b/llvm/test/FileCheck/dump-input/search-range-annotations/check-next-same.txt
new file mode 100644
index 0000000000000..0767d4b734590
--- /dev/null
+++ b/llvm/test/FileCheck/dump-input/search-range-annotations/check-next-same.txt
@@ -0,0 +1,61 @@
+; Because -dump-input-filter=error includes a failed directive's search range
+; start, input line 1 below is revealed, and -dump-input-context=2 reveals lines
+; 2-3. Those lines are where the actual problem is because that is where the
+; CHECK-NEXT/CHECK-SAME directive was expected to match but did not. The line
+; where CHECK-NEXT/CHECK-SAME actually matched, line 13, is typically less
+; useful information.
+
+RUN: rm -rf %t
+RUN: split-file %s %t
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t/next.txt < %t/input.txt \
+RUN: -dump-input-filter=error 2>&1 | \
+RUN: FileCheck %s -match-full-lines -check-prefixes=DMP,DMP_NEXT
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t/same.txt < %t/input.txt \
+RUN: -dump-input-filter=error 2>&1 | \
+RUN: FileCheck %s -match-full-lines -check-prefixes=DMP,DMP_SAME
+
+ DMP:<<<<<<
+ DMP-NEXT: 1: start
+ DMP-NEXT:check:1 ^~~~~
+DMP_NEXT-NEXT:next:2'0 { search range start (exclusive)
+DMP_SAME-NEXT:same:2'0 { search range start (exclusive)
+ DMP-NEXT: 2: foo0
+ DMP-NEXT: 3: foo1
+ DMP-NEXT: .
+ DMP-NEXT: .
+ DMP-NEXT: .
+ DMP-NEXT: 11: foo9
+ DMP-NEXT: 12: foo10
+ DMP-NEXT: 13: end
+DMP_NEXT-NEXT:next:2'1 !~~ error: match on wrong line
+DMP_SAME-NEXT:same:2'1 !~~ error: match on wrong line
+DMP_NEXT-NEXT:next:2'2 } search range end (exclusive)
+DMP_SAME-NEXT:same:2'2 } search range end (exclusive)
+ DMP-NEXT:>>>>>>
+
+;--- next.txt
+CHECK: start
+CHECK-NEXT: end
+
+;--- same.txt
+CHECK: start
+CHECK-SAME: end
+
+;--- input.txt
+start
+foo0
+foo1
+foo2
+foo3
+foo4
+foo5
+foo6
+foo7
+foo8
+foo9
+foo10
+end
diff --git a/llvm/test/FileCheck/dump-input/search-range-annotations/check-not.txt b/llvm/test/FileCheck/dump-input/search-range-annotations/check-not.txt
new file mode 100644
index 0000000000000..7cae1b85bb47a
--- /dev/null
+++ b/llvm/test/FileCheck/dump-input/search-range-annotations/check-not.txt
@@ -0,0 +1,112 @@
+; Because -dump-input-filter=error includes a failed directive's search range,
+; input lines 1 and 62 below are revealed, and -dump-input-context=2 reveals the
+; surrounding lines. Even with only line 1, it looks like the host list
+; includes a GPU triple. Line 62 and its context reveals we have an
+; accidentally nested list. Often the key to understanding a CHECK-NOT failure
+; is where the directives before and after it matched because those establish
+; the search range.
+
+RUN: rm -rf %t
+RUN: split-file %s %t
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t/check.txt < %t/input.txt \
+RUN: -dump-input-filter=error 2>&1 | \
+RUN: FileCheck %s -match-full-lines -check-prefix=DMP
+
+ DMP:<<<<<<
+DMP-NEXT: 1: <triples kind="host">
+DMP-NEXT:check:1 ^~~~~~~~~~~~~~~~~~~~~
+DMP-NEXT:not:2'0 { search range start (exclusive)
+DMP-NEXT: 2: x86_64-linux-gnu
+DMP-NEXT: 3: x86_64-unknown-linux-gnu
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: 42: x86_64-amazon-linux
+DMP-NEXT: 43: x86_64-linux-android
+DMP-NEXT: 44: nvptx64-nvidia-cuda
+DMP-NEXT:not:2'1 !~~~~~~~~~~~~~~~~~~ error: no match expected
+DMP-NEXT: 45: i686-linux-gnu
+DMP-NEXT: 46: i686-pc-linux-gnu
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: .
+DMP-NEXT: 59: i486-gnu
+DMP-NEXT: 60: i586-gnu
+DMP-NEXT: 61: i686-gnu
+DMP-NEXT:not:2'2 } search range end (exclusive)
+DMP-NEXT: 62: </triples>
+DMP-NEXT:check:3 ^~~~~~~~~~
+DMP-NEXT: 63: </triples>
+DMP-NEXT:>>>>>>
+
+;--- check.txt
+CHECK: <triples kind="host">
+CHECK-NOT: nvptx64-nvidia-cuda
+CHECK: </triples>
+
+;--- input.txt
+<triples kind="host">
+ x86_64-linux-gnu
+ x86_64-unknown-linux-gnu
+ x86_64-pc-linux-gnu
+ x86_64-redhat-linux6E
+ x86_64-redhat-linux
+ x86_64-suse-linux
+ x86_64-manbo-linux-gnu
+ x86_64-linux-gnu
+ x86_64-slackware-linux
+ x86_64-unknown-linux
+ x86_64-amazon-linux
+ x86_64-linux-android
+ i686-linux-gnu
+ i686-pc-linux-gnu
+ i486-linux-gnu
+ i386-linux-gnu
+ i386-redhat-linux6E
+ i686-redhat-linux
+ i586-redhat-linux
+ i386-redhat-linux
+ i586-suse-linux
+ i486-slackware-linux
+ i686-montavista-linux
+ i586-linux-gnu
+ i686-linux-android
+ i386-gnu
+ i486-gnu
+ i586-gnu
+ i686-gnu
+<triples kind="offload">
+ x86_64-linux-gnu
+ x86_64-unknown-linux-gnu
+ x86_64-pc-linux-gnu
+ x86_64-redhat-linux6E
+ x86_64-redhat-linux
+ x86_64-suse-linux
+ x86_64-manbo-linux-gnu
+ x86_64-linux-gnu
+ x86_64-slackware-linux
+ x86_64-unknown-linux
+ x86_64-amazon-linux
+ x86_64-linux-android
+ nvptx64-nvidia-cuda
+ i686-linux-gnu
+ i686-pc-linux-gnu
+ i486-linux-gnu
+ i386-linux-gnu
+ i386-redhat-linux6E
+ i686-redhat-linux
+ i586-redhat-linux
+ i386-redhat-linux
+ i586-suse-linux
+ i486-slackware-linux
+ i686-montavista-linux
+ i586-linux-gnu
+ i686-linux-android
+ i386-gnu
+ i486-gnu
+ i586-gnu
+ i686-gnu
+</triples>
+</triples>
diff --git a/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt
index c8e621eb31796..001f74c0c5a3d 100644
--- a/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt
+++ b/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt
@@ -28,9 +28,10 @@ ERR-VV-EMPTY:
DUMP:<<<<<<
DUMP-NEXT: 1: -1
- DUMP-NEXT:not:1'0 X~~ error: match failed for invalid pattern
- DUMP-NEXT:not:1'1 unable to substitute variable or numeric expression: overflow error
- DUMP-NEXT:not:1'2 undefined variable: UNDEFVAR
+ DUMP-NEXT:not:1'0 { } search range (exclusive bounds)
+ DUMP-NEXT:not:1'1 error: match failed for invalid pattern
+ DUMP-NEXT:not:1'2 unable to substitute variable or numeric expression: overflow error
+ DUMP-NEXT:not:1'3 undefined variable: UNDEFVAR
DUMP-VV-NEXT: 2:
DUMP-VV-NEXT:eof:1 ^
DUMP-NEXT:>>>>>>
diff --git a/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt
index 6aada8b0c7587..c633a98b76c9a 100644
--- a/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt
+++ b/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt
@@ -17,9 +17,10 @@ RUN: echo > %t.in '-1'
DUMP:<<<<<<
DUMP-NEXT: 1: -1
-DUMP-NEXT:check:1'0 X~~ error: match failed for invalid pattern
-DUMP-NEXT:check:1'1 unable to substitute variable or numeric expression: overflow error
-DUMP-NEXT:check:1'2 undefined variable: UNDEFVAR
+DUMP-NEXT:check:1'0 { } search range (exclusive bounds)
+DUMP-NEXT:check:1'1 error: match failed for invalid pattern
+DUMP-NEXT:check:1'2 unable to substitute variable or numeric expression: overflow error
+DUMP-NEXT:check:1'3 undefined variable: UNDEFVAR
DUMP-NEXT:>>>>>>
;--------------------------------------------------
diff --git a/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt
index b76ff5ed66aa6..7c7a3e44b77d2 100644
--- a/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt
+++ b/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt
@@ -23,9 +23,10 @@ ERR-VV-EMPTY:
DUMP:<<<<<<
DUMP-NEXT: 1: 123 abc -1
- DUMP-NEXT:not:1'0 X~~~~~~~~~~ error: match failed for invalid pattern
- DUMP-NEXT:not:1'1 unable to substitute variable or numeric expression: overflow error
- DUMP-NEXT:not:1'2 with "122+1" equal to "123"
+ DUMP-NEXT:not:1'0 { } search range (exclusive bounds)
+ DUMP-NEXT:not:1'1 error: match failed for invalid pattern
+ DUMP-NEXT:not:1'2 unable to substitute variable or numeric expression: overflow error
+ DUMP-NEXT:not:1'3 with "122+1" equal to "123"
DUMP-VV-NEXT: 2:
DUMP-VV-NEXT:eof:1 ^
DUMP-NEXT:>>>>>>
diff --git a/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt
index 3388a53273918..bc7dd506d0ede 100644
--- a/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt
+++ b/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt
@@ -19,10 +19,11 @@ RUN: echo > %t.in '123 abc -1'
DUMP:<<<<<<
DUMP-NEXT: 1: 123 abc -1
-DUMP-NEXT:check:1'0 X~~~~~~~~~~ error: match failed for invalid pattern
-DUMP-NEXT:check:1'1 unable to substitute variable or numeric expression: overflow error
-DUMP-NEXT:check:1'2 with "122+1" equal to "123"
-DUMP-NEXT:check:1'3 ? possible intended match
+DUMP-NEXT:check:1'0 { } search range (exclusive bounds)
+DUMP-NEXT:check:1'1 error: match failed for invalid pattern
+DUMP-NEXT:check:1'2 unable to substitute variable or numeric expression: overflow error
+DUMP-NEXT:check:1'3 with "122+1" equal to "123"
+DUMP-NEXT:check:1'4 ? possible intended match
DUMP-NEXT:>>>>>>
;--------------------------------------------------
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 8c760db50a375..808df9a41029f 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -217,9 +217,8 @@ static MarkerStyle getMarker(const FileCheckDiag &Diag) {
// don't have markers. For example, a marker for the MatchNoteDiag
// 'with "VAR" equal to "5"' would seem to indicate where "VAR" matches, but
// we don't actually have that location. Instead, we just place the note
- // after the start of the associated MatchResultDiag. This decision is
- // overriden below for the case of MatchNoneDiag because the search range is
- // used instead.
+ // after the start of the associated MatchResultDiag. Search ranges are
+ // indicated separately.
MarkerStyle Res;
bool IsError = Diag.isError() || Diag.getMatchResultDiag().isError();
if (Diag.getMatchRange()) {
@@ -231,7 +230,7 @@ static MarkerStyle getMarker(const FileCheckDiag &Diag) {
Res.Color = IsError ? raw_ostream::RED : raw_ostream::GREEN;
Res.FiltersAsError = IsError;
- // Add Note. Override the default Lead and Color for some diagnostic kinds.
+ // Add Note. Override the default Head and Color for some diagnostic kinds.
switch (Diag.getKind()) {
case FileCheckDiag::MatchFoundDiag:
switch (cast<MatchFoundDiag>(Diag).getStatus()) {
@@ -251,8 +250,6 @@ static MarkerStyle getMarker(const FileCheckDiag &Diag) {
}
break;
case FileCheckDiag::MatchNoneDiag:
- Res.Head = 'X';
- Res.Mid = Res.Tail = '~';
switch (cast<MatchNoneDiag>(Diag).getStatus()) {
case MatchNoneDiag::Success:
break;
@@ -334,8 +331,11 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) {
<< " - CHECK-DAG overlapping match (discarded, reported if "
<< "-vv)\n"
<< " - ";
- WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
- OS << " marks search range when no match is found, such as:\n"
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "{ }";
+ OS << " encloses search range (exclusive bounds) when no match is found "
+ << "or\n"
+ << " there is an error, such as:\n"
+ << " - the errors mentioned above\n"
<< " - CHECK-NEXT not found (error)\n"
<< " - CHECK-NOT not found (success, reported if -vv)\n"
<< " - CHECK-DAG not found after discarded matches (error)\n"
@@ -381,7 +381,8 @@ struct InputAnnotation {
unsigned InputLine;
/// The column range (one-origin indexing, inclusive boundaries) in which to
/// mark the input line. If \c InputLastCol is \c UINT_MAX, the rest of the
- /// input line should be marked.
+ /// input line should be marked, and another \c InputAnnotation will continue
+ /// it on the next line.
unsigned InputFirstCol, InputLastCol;
/// The marker to use.
MarkerStyle Marker;
@@ -513,14 +514,55 @@ struct MarkerRange {
/// \p Range specifies the \a logical input range to be depicted by annotation
/// markers \a drawn at the resulting \c MarkerRange.
///
- /// If \p Range is an empty range, then the resulting \c MarkerRange is
- /// expanded to a single character. This avoids a missing marker for an empty
- /// range, but it means the markers for a single-character range are
- /// indistinguishable from markers for an empty range.
- MarkerRange(const SourceMgr &SM, SMRange Range) {
- // Range has an inclusive start as MarkerRange requires.
+ /// \a how that drawing depicts that logical input range is determined by
+ /// \p ShowExclusive. The drawing specifies either:
+ /// - The \a inclusive start and end bounds of the logical input range if
+ /// \p !ShowExclusive. In this case:
+ /// - If the logical input range is empty, then the resulting \c MarkerRange
+ /// is expanded to a single character. This avoids a missing marker, but
+ /// it means the markers for a single-character range are
+ /// indistinguishable from markers for an empty range.
+ /// - The first and last location of the \c MarkerRange are always real
+ /// locations in the input (never, for example, column 0).
+ /// - The \a exclusive start and end bounds of the logical input range if
+ /// \p ShowExclusive. In this case:
+ /// - The \c MarkerRange length is then always at least two because
+ /// exclusive boundaries never occupy the same location.
+ /// - If a \p Range boundary is an input line boundary, the corresponding
+ /// \c MarkerRange column might be in the line's margin (e.g., column 0)
+ /// to avoid placing a marker on an adjacent line. That decision can make
+ /// input annotations more concise (more line-liners) and easier to read.
+ /// It also avoids non-existent adjacent lines (e.g., line 0) that are not
+ /// depicted in the input dump.
+ MarkerRange(const SourceMgr &SM, SMRange Range, bool ShowExclusive = false) {
+ // Given an SMRange representing the range of text "range of text", the
+ // following example compares how it and the resulting MarkerRange encode
+ // the same start (s) and end (e) bounds:
+ //
+ // ....range of text....
+ // s e SMRange
+ // s e MarkerRange with ShowExclusive=false
+ // s e MarkerRange with ShowExclusive=true
+ if (ShowExclusive) {
+ // Range has inclusive start, but ShowExclusive requires exclusive start.
+ First = SM.getLineAndColumn(Range.Start);
+ --First.Col;
+ // Range has an exclusive end as ShowExclusive requires. If it is at a
+ // line boundary, it is at the start of the next line, so normally move it
+ // to the end of the previous line. For an empty range, do not do that as
+ // we do not want an end marker on the line before the start marker.
+ if (Range.Start == Range.End) {
+ Last = SM.getLineAndColumn(Range.End);
+ } else {
+ SMLoc EndLoc = SMLoc::getFromPointer(Range.End.getPointer() - 1);
+ Last = SM.getLineAndColumn(EndLoc);
+ ++Last.Col;
+ }
+ return;
+ }
+ // Range has an inclusive start as !ShowExclusive requires.
First = SM.getLineAndColumn(Range.Start);
- // Range has an exclusive end, but MarkerRange requires an inclusive end.
+ // Range has an exclusive end, but !ShowExclusive requires an inclusive end.
if (Range.Start == Range.End) {
// Convert the empty range to a one-character range.
Last = First;
@@ -541,6 +583,124 @@ struct MarkerRange {
/// Return a range marking only the first character.
MarkerRange truncate() const { return {First, First}; }
};
+
+/// Emits search range annotations for each \c MatchResultDiag as it is
+/// encountered.
+///
+/// In some cases, it emits a single, one-line annotation. Otherwise, it emits
+/// separate annotations for the start and end of the search range. The logic
+/// for making this determination is encapsulated in static member functions.
+class SearchRangeAnnotator {
+private:
+ const SourceMgr &SM;
+ /// Where to append search range annotations.
+ std::vector<InputAnnotation> &Annotations;
+ /// A globally unique index for this annotation.
+ unsigned &LabelIndexGlobal;
+ /// The most recent \c MatchResultDiag, or \c nullptr if all search range
+ /// annotations have been added already for the most recent
+ /// \c MatchResultDiag.
+ const MatchResultDiag *MRD;
+ /// The labeler for \c MRD. Stored by value as the original labeler might be
+ /// destroyed by the time we call \c endDiags here.
+ InputAnnotationLabeler Labeler;
+ /// Would a \c SearchRangeAnnotator make any search range annotations for
+ /// \p MRD?
+ static bool makesAnnotationsFor(const MatchResultDiag &MRD) {
+ return !MRD.getMatchRange() || MRD.isError();
+ }
+ /// Assuming \c makesAnnotationsFor(MRD), would a \c SearchRangeAnnotator make
+ /// a one-line search range annotation for \p MRD? Either way, the search
+ /// range computed for \p MRD is stored in \p SearchRange.
+ static bool makesOneLinerFor(const SourceMgr &SM, const MatchResultDiag &MRD,
+ MarkerRange &SearchRange) {
+ assert(makesAnnotationsFor(MRD) &&
+ "expected makesAnnotationsFor to be checked first");
+ SearchRange = {SM, MRD.getSearchRange(), /*ShowExclusive=*/true};
+ return SearchRange.isSingleLine();
+ }
+ /// Make the next annotation for the current \c MatchResultDiag.
+ void makeAnnotation(bool Start) {
+ InputAnnotation &A = Annotations.emplace_back();
+ A.LabelIndexGlobal = LabelIndexGlobal++;
+ Labeler.generateLabel(A.Label);
+ A.IsFirstLine = true;
+ A.FoundAndExpectedMatch = false;
+ MarkerRange SearchRange;
+ if (makesOneLinerFor(SM, *MRD, SearchRange)) {
+ assert(Start && "expected no search range end annotation for one-liner");
+ A.InputLine = SearchRange.getFirstLoc().Line;
+ A.InputFirstCol = SearchRange.getFirstLoc().Col;
+ A.InputLastCol = SearchRange.getLastLoc().Col;
+ MatchCustomNoteDiag NoteDiag("search range (exclusive bounds)");
+ NoteDiag.setMatchResultDiag(MRD);
+ A.Marker = getMarker(NoteDiag);
+ A.Marker.Head = '{';
+ A.Marker.Mid = ' ';
+ A.Marker.Tail = '}';
+ MRD = nullptr;
+ return;
+ }
+ // We have separate annotations for start and end.
+ MarkerRange::Loc Loc =
+ Start ? SearchRange.getFirstLoc() : SearchRange.getLastLoc();
+ A.InputLine = Loc.Line;
+ A.InputFirstCol = A.InputLastCol = Loc.Col;
+ MatchCustomNoteDiag NoteDiag(std::string("search range ") +
+ (Start ? "start" : "end") + " (exclusive)");
+ NoteDiag.setMatchResultDiag(MRD);
+ A.Marker = getMarker(NoteDiag);
+ A.Marker.Head = Start ? '{' : '}';
+ }
+
+public:
+ /// How many search range annotations would a \c SearchRangeAnnotator generate
+ /// for \c MRD?
+ static unsigned countAnnotationsFor(const SourceMgr &SM,
+ const MatchResultDiag &MRD) {
+ if (!makesAnnotationsFor(MRD))
+ return 0;
+ MarkerRange SearchRange;
+ return makesOneLinerFor(SM, MRD, SearchRange) ? 1 : 2;
+ }
+ /// Are the search range annotations generated by a \c SearchRangeAnnotator
+ /// sufficient for \p MRD? Otherwise, \p MRD needs to be rendered as a
+ /// separate annotation.
+ static bool sufficesFor(const MatchResultDiag &MRD) {
+ return makesAnnotationsFor(MRD) && getMarker(MRD).Note.empty();
+ }
+ /// \p Annotations is where this annotator should append search range
+ /// annotations. \p LabelIndexGlobal is the globally unique index of the next
+ /// annotation label to be generated. This annotator will increment it when
+ /// generating a new label for a search range annotation.
+ SearchRangeAnnotator(const SourceMgr &SM,
+ std::vector<InputAnnotation> &Annotations,
+ unsigned &LabelIndexGlobal)
+ : SM(SM), Annotations(Annotations), LabelIndexGlobal(LabelIndexGlobal),
+ MRD(nullptr) {}
+ /// Emit any search range start annotation or one-line search range annotation
+ /// for \p MRDNew using its labeler \p LabelerNew. This annotator will emit
+ /// any search range end annotation at the next call to \c newMatchResultDiag
+ /// or \c endDiags.
+ void newMatchResultDiag(const MatchResultDiag &MRDNew,
+ InputAnnotationLabeler LabelerNew) {
+ if (MRD) {
+ makeAnnotation(/*Start=*/false);
+ MRD = nullptr;
+ }
+ if (makesAnnotationsFor(MRDNew)) {
+ MRD = &MRDNew;
+ Labeler = LabelerNew;
+ makeAnnotation(/*Start=*/true);
+ }
+ }
+ /// Emit any search range end annotation for the final \c MatchResultDiag
+ /// passed to \c newMatchResultDiag.
+ void endDiags() {
+ if (MRD)
+ makeAnnotation(/*Start=*/false);
+ }
+};
} // namespace
static void
@@ -560,12 +720,22 @@ buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
// series of zero or more MatchNoteDiag's. Each such MatchResultDiag and its
// MatchNoteDiag series can require multiple labels.
std::map<SMLoc, unsigned, CompareSMLoc> LabelCountPerPattern;
- for (const FileCheckDiag &Diag : Diags)
- ++LabelCountPerPattern[Diag.getMatchResultDiag().getCheckLoc()];
+ for (const FileCheckDiag &Diag : Diags) {
+ unsigned &C = LabelCountPerPattern[Diag.getMatchResultDiag().getCheckLoc()];
+ if (const MatchResultDiag *MRD = dyn_cast<MatchResultDiag>(&Diag)) {
+ C += SearchRangeAnnotator::countAnnotationsFor(SM, *MRD);
+ if (!SearchRangeAnnotator::sufficesFor(*MRD))
+ ++C;
+ } else {
+ ++C;
+ }
+ }
// How many labels have we generated so far per check pattern?
std::map<SMLoc, unsigned, CompareSMLoc> LabelIndexPerPattern;
// How many total labels have we generated so far?
unsigned LabelIndexGlobal = 0;
+ SearchRangeAnnotator TheSearchRangeAnnotator(SM, Annotations,
+ LabelIndexGlobal);
// What's the widest label we've generated so far?
LabelWidthGlobal = 0;
// The labeler for the current MatchResultDiag and its MatchNoteDiag series.
@@ -578,6 +748,9 @@ buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
LabelCountPerPattern[MRD->getCheckLoc()] > 1
? &LabelIndexPerPattern[MRD->getCheckLoc()]
: nullptr);
+ TheSearchRangeAnnotator.newMatchResultDiag(*MRD, CurLabeler);
+ if (SearchRangeAnnotator::sufficesFor(*MRD))
+ continue;
}
// Build label that is unique for this input annotation before it is
@@ -596,18 +769,13 @@ buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
A.FoundAndExpectedMatch = true;
}
- // If Diag has a match range, position the marker there. If it is a
- // MatchNoneDiag, position the marker at its search range. Otherwise,
+ // If Diag has a match range, position the marker there. Otherwise,
// position the marker at the start of the most recent MatchResultDiag, with
// which it is associated.
MarkerRange InputRange;
if (Diag.getMatchRange()) {
InputRange = MarkerRange(SM, *Diag.getMatchRange());
- } else if (const MatchNoneDiag *MND = dyn_cast<MatchNoneDiag>(&Diag)) {
- InputRange = MarkerRange(SM, MND->getSearchRange());
} else {
- assert(isa<MatchNoteDiag>(Diag) &&
- "expected only MatchNoteDiag to have no input range");
const MatchResultDiag &MRD = Diag.getMatchResultDiag();
InputRange = MRD.getMatchRange() ? MarkerRange(SM, *MRD.getMatchRange())
: MarkerRange(SM, MRD.getSearchRange());
@@ -647,6 +815,7 @@ buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
}
}
}
+ TheSearchRangeAnnotator.endDiags();
}
static unsigned FindInputLineInFilter(
@@ -864,17 +1033,20 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
AnnotationItr->InputLine == Line) {
WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
/*BG=*/false, TheColorMode);
- // The two spaces below are where the ": " appears on input lines.
- COS << left_justify(AnnotationItr->Label, LabelWidthGlobal) << " ";
+ // The space below aligns with the ":" on the input line.
+ COS << left_justify(AnnotationItr->Label, LabelWidthGlobal) << " ";
unsigned Col;
- for (Col = 1; Col < AnnotationItr->InputFirstCol; ++Col)
+ // A search range annotation at the beginning of the line starts at column
+ // 0 because it is an exclusive boundary.
+ for (Col = 0; Col < AnnotationItr->InputFirstCol; ++Col)
COS << ' ';
COS << AnnotationItr->Marker.Head;
// If InputLastCol==UINT_MAX, stop at InputLineWidth.
for (++Col; Col < AnnotationItr->InputLastCol && Col <= InputLineWidth;
++Col)
COS << AnnotationItr->Marker.Mid;
- if (Col <= AnnotationItr->InputLastCol && Col <= InputLineWidth) {
+ if (Col <= AnnotationItr->InputLastCol &&
+ AnnotationItr->InputLastCol != UINT_MAX) {
COS << AnnotationItr->Marker.Tail;
++Col;
}
@@ -884,7 +1056,7 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
// put the note right after the marker, subsequent annotations for the
// same input line might appear to mark this note instead of the input
// line.
- for (; Col <= InputLineWidth; ++Col)
+ for (; Col <= InputLineWidth + 1; ++Col)
COS << ' ';
COS << ' ' << Note;
}
>From 21d1e2117b40b7d78d399e5999b2a5d10e80f8eb Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Mon, 18 May 2026 11:41:30 -0400
Subject: [PATCH 2/2] Replace MarkerRange::truncate with new constructor
`truncate` was ok before this patch, but now it is unclear what
happens if the original `MarkerRange` had `ShowExclusive==true`. In
that case, it seems like maybe we should end up with a range marking
the characters before and after the one character, but instead we
always end up with a single marker at just the one character.
Currently, `truncate` is never called for that case, but it is better
to keep the API clear for the future. Constructing an entirely new
`MarkerRange` makes it clearer to me that it knows nothing about the
old `MarkerRange`'s `ShowExclusive`. If needed later, we can add a
`ShowExclusive` parameter to the new constructor.
---
llvm/utils/FileCheck/FileCheck.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 808df9a41029f..4d8a036a31903 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -574,14 +574,15 @@ struct MarkerRange {
Last = SM.getLineAndColumn(EndLoc);
}
}
+ /// \p Loc specifies a single input character to be marked by a single
+ /// annotation marker character.
+ MarkerRange(Loc OneChar) : First(OneChar), Last(OneChar) {}
/// Is the marker range contained on a single line?
bool isSingleLine() const { return First.Line == Last.Line; }
/// Get the location of the first marked character.
Loc getFirstLoc() const { return First; }
/// Get the location of the last marked character.
Loc getLastLoc() const { return Last; }
- /// Return a range marking only the first character.
- MarkerRange truncate() const { return {First, First}; }
};
/// Emits search range annotations for each \c MatchResultDiag as it is
@@ -779,7 +780,7 @@ buildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
const MatchResultDiag &MRD = Diag.getMatchResultDiag();
InputRange = MRD.getMatchRange() ? MarkerRange(SM, *MRD.getMatchRange())
: MarkerRange(SM, MRD.getSearchRange());
- InputRange = InputRange.truncate();
+ InputRange = MarkerRange(InputRange.getFirstLoc());
assert(A.Marker.Head == ' ' && "expected no marker for no match range");
}
More information about the llvm-branch-commits
mailing list