[llvm] bce8fce - [FileCheck] Implement -dump-input-context
Joel E. Denny via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 10 08:04:27 PDT 2020
Author: Joel E. Denny
Date: 2020-07-10T11:02:10-04:00
New Revision: bce8fced41b96260a42dfbb254240a49769fafa9
URL: https://github.com/llvm/llvm-project/commit/bce8fced41b96260a42dfbb254240a49769fafa9
DIFF: https://github.com/llvm/llvm-project/commit/bce8fced41b96260a42dfbb254240a49769fafa9.diff
LOG: [FileCheck] Implement -dump-input-context
This patch is motivated by discussions at each of:
* <https://reviews.llvm.org/D81422>
* <http://lists.llvm.org/pipermail/llvm-dev/2020-June/142369.html>
When input is dumped as specified by `-dump-input=fail`, this patch
filters the dump to show only input lines that are the starting lines
of error diagnostics plus the number of contextual lines specified
`-dump-input-context` (defaults to 5).
When `-dump-input=always`, there might be not be any errors, so all
input lines are printed, as without this patch.
Here's some sample output with `-dump-input-context=3 -vv`:
```
<<<<<<
.
.
.
13: foo
14: foo
15: hello world
check:1 ^~~~~~~~~~~
16: foo
check:2'0 X~~ error: no match found
17: foo
check:2'0 ~~~
18: foo
check:2'0 ~~~
19: foo
check:2'0 ~~~
.
.
.
27: foo
check:2'0 ~~~
28: foo
check:2'0 ~~~
29: foo
check:2'0 ~~~
30: goodbye word
check:2'0 ~~~~~~~~~~~~
check:2'1 ? possible intended match
31: foo
check:2'0 ~~~
32: foo
check:2'0 ~~~
33: foo
check:2'0 ~~~
.
.
.
>>>>>>
```
Reviewed By: mehdi_amini, arsenm, jhenderson, rsmith, SjoerdMeijer, Meinersbur, lattner
Differential Revision: https://reviews.llvm.org/D82203
Added:
llvm/test/FileCheck/dump-input-context.txt
llvm/test/FileCheck/dump-input-filter.txt
Modified:
llvm/test/FileCheck/dump-input-annotations.txt
llvm/test/FileCheck/dump-input-enable.txt
llvm/utils/FileCheck/FileCheck.cpp
Removed:
################################################################################
diff --git a/llvm/test/FileCheck/dump-input-annotations.txt b/llvm/test/FileCheck/dump-input-annotations.txt
index f4f0d3ca6022..785ee23d03dd 100644
--- a/llvm/test/FileCheck/dump-input-annotations.txt
+++ b/llvm/test/FileCheck/dump-input-annotations.txt
@@ -24,7 +24,7 @@
; ALIGN:{{.*}}error:{{.*}}
; ALIGN:{{.*}}possible intended match here{{.*}}
-; ALIGN:Full input was:
+; ALIGN:Input was:
; ALIGN-NEXT:<<<<<<
; ALIGN-NEXT: 1: hello world
; ALIGN-NEXT:check:1 ^~~~~
diff --git a/llvm/test/FileCheck/dump-input-context.txt b/llvm/test/FileCheck/dump-input-context.txt
new file mode 100644
index 000000000000..6badaf88778d
--- /dev/null
+++ b/llvm/test/FileCheck/dump-input-context.txt
@@ -0,0 +1,227 @@
+;--------------------------------------------------
+; Input file, check file, and directives for checking the size of the context.
+;
+; These are designed to be used with -dump-input=fail -vv.
+;
+; In the resulting input dump, there are three potential ellipses:
+;
+; - S: At the start of the input.
+; - M: Between two input lines included by the filter.
+; - E: At the end of the input.
+;
+; They are all present at -dump-input-context=6. One disappears each time
+; -dump-input-context is incremented beyond that because there are no lines
+; left to elide.
+;--------------------------------------------------
+
+; RUN: echo foo8 > %t.in
+; RUN: echo foo7 >> %t.in
+; RUN: echo foo6 >> %t.in
+; RUN: echo foo5 >> %t.in
+; RUN: echo foo4 >> %t.in
+; RUN: echo foo3 >> %t.in
+; RUN: echo foo2 >> %t.in
+; RUN: echo foo1 >> %t.in
+; RUN: echo lab1 hello >> %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 foo7 >> %t.in
+; RUN: echo foo6 >> %t.in
+; RUN: echo foo5 >> %t.in
+; RUN: echo foo4 >> %t.in
+; RUN: echo foo3 >> %t.in
+; RUN: echo foo2 >> %t.in
+; RUN: echo foo1 >> %t.in
+; RUN: echo lab2 world >> %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 '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: .
+; C8-NEXT: 1: foo8
+; C7-NEXT: 2: foo7
+; C6-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
+; C7-NEXT: 16: foo7
+; CM-NEXT: .
+; CM-NEXT: .
+; CM-NEXT: .
+; C7-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
+; C8-NEXT: 32: foo8
+; C9-NEXT: 33: foo9
+; CE-NEXT: .
+; CE-NEXT: .
+; CE-NEXT: .
+; C0-NEXT: >>>>>>
+
+;--------------------------------------------------
+; Check -dump-input-context=<bad value>.
+;--------------------------------------------------
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=-1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL -DVAL=-1
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; 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!
+
+;--------------------------------------------------
+; Check -dump-input-context explicit values.
+;--------------------------------------------------
+
+; 0 is an important boundary case.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=0 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,CS,CM,CE
+
+; 1 is an important boundary case.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
+
+; 6 is the boundary case at which all ellipses are present in our test.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=6 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,CS,CM,CE
+
+; 7 is the boundary case at which the middle ellipsis disappears.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=7 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,CS,CE
+
+; 8 is the boundary case at which the start ellipsis disappears.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=8 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,CE
+
+; 9 is the boundary case at which the end ellipsis disappears.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=9 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,C9
+
+; Make sure all is fine when -dump-input-context is far larger than the input.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=200 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,C9
+
+;--------------------------------------------------
+; Check that -dump-input-context default is 5.
+;--------------------------------------------------
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: | FileCheck %s -match-full-lines \
+; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,CS,CM,CE
+
+;--------------------------------------------------
+; Check multiple -dump-input-context options.
+;
+; This might occur when a test author specifies -dump-input-context on a
+; specific FileCheck call while a test runner specifies -dump-input-context in
+; FILECHECK_OPTS, but check the behavior generally.
+;
+; The largest value wins because it provides the most information.
+;--------------------------------------------------
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; Check duplicate.
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=1 -dump-input-context=1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; Check precedence.
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=0 -dump-input-context=1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=1 -dump-input-context=0 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; Check that FILECHECK_OPTS isn't handled
diff erently.
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+
+; RUN: %ProtectFileCheckOutput FILECHECK_OPTS=-dump-input-context=0 \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
+
+; RUN: %ProtectFileCheckOutput FILECHECK_OPTS=-dump-input-context=1 \
+; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input-context=0 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,CS,CM,CE
diff --git a/llvm/test/FileCheck/dump-input-enable.txt b/llvm/test/FileCheck/dump-input-enable.txt
index b0aacfb2fed2..932701b0019b 100644
--- a/llvm/test/FileCheck/dump-input-enable.txt
+++ b/llvm/test/FileCheck/dump-input-enable.txt
@@ -236,7 +236,7 @@ BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input option: Cannot find op
; NODUMP-NOT: <<<<<<
-; DUMP-OK: Full input was:
+; DUMP-OK: Input was:
; DUMP-OK-NEXT: <<<<<<
; DUMP-OK-NEXT: 1: hello
; DUMP-OK-NEXT: check:1 ^~~~~
@@ -244,7 +244,7 @@ BADVAL: {{F|f}}ile{{C|c}}heck{{.*}}: for the --dump-input option: Cannot find op
; DUMP-OK-NEXT: next:2 ^~~~~
; DUMP-OK-NEXT: >>>>>>
-; DUMP-ERR: Full input was:
+; DUMP-ERR: Input was:
; DUMP-ERR-NEXT: <<<<<<
; DUMP-ERR-NEXT: 1: hello
; DUMP-ERR-V-NEXT: check:1 ^~~~~
diff --git a/llvm/test/FileCheck/dump-input-filter.txt b/llvm/test/FileCheck/dump-input-filter.txt
new file mode 100644
index 000000000000..f1b8cf543d05
--- /dev/null
+++ b/llvm/test/FileCheck/dump-input-filter.txt
@@ -0,0 +1,208 @@
+; To keep this test maintainable, avoid depending on -dump-input-context's
+; default value, which is checked in dump-input-context.txt instead.
+
+;--------------------------------------------------
+; Create the input file and the check file.
+;--------------------------------------------------
+
+; line 1
+; RUN: echo start > %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 12
+; RUN: echo hello >> %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 23
+; RUN: echo word >> %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 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
+
+;--------------------------------------------------
+; 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: 13: foo0
+; ALL-NEXT: check:3'0 X~~~ error: no match found
+; 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: >>>>>>
+
+; ERROR: <<<<<<
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: .
+; ERROR-NEXT: 11: foo9
+; ERROR-NEXT: 12: hello
+; ERROR-NEXT: check:2 ^~~~~
+; ERROR-NEXT: 13: foo0
+; ERROR-NEXT: check:3'0 X~~~ error: no match found
+; ERROR-NEXT: 14: foo1
+; ERROR-NEXT: check:3'0 ~~~~
+; ERROR-NEXT: 15: foo2
+; 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: >>>>>>
+
+;--------------------------------------------------
+; Check how -dump-input affects filter.
+;--------------------------------------------------
+
+; no -dump-input => include errors.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=2 -vv %t.chk < %t.in 2>&1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=ERROR
+
+; -dump-input=fail => include errors.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=2 -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input=fail \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=ERROR
+
+; -dump-input=always => include all.
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=2 -vv %t.chk < %t.in 2>&1 \
+; RUN: -dump-input=always \
+; RUN: | FileCheck %s -match-full-lines -check-prefixes=ALL
+
+;--------------------------------------------------
+; Check that other kinds of errors are included by -dump-input=fail.
+;
+; "error: no match found" and "possible intended match" are checked above.
+;--------------------------------------------------
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; error: no match expected.
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+
+; RUN: echo 'foo' > %t.not-err.in
+; RUN: echo 'CHECK-NOT: foo' > %t.not-err.chk
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=0 -dump-input=fail \
+; 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
+
+;- - - - - - - - - - - - - - - - - - - - - - - - -
+; 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: %ProtectFileCheckOutput \
+; RUN: not FileCheck -dump-input-context=0 -dump-input=fail \
+; 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
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 659491c89636..0952d4bd24d0 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -128,6 +128,14 @@ static cl::list<DumpInputValue> DumpInputs(
clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
clEnumValN(DumpInputNever, "never", "Never dump input")));
+static cl::list<unsigned> DumpInputContexts(
+ "dump-input-context", cl::value_desc("N"),
+ cl::desc("In the dump requested by -dump-input=fail, print <N> input\n"
+ "lines before and <N> input lines after the starting line of\n"
+ "any error diagnostic. When there are multiple occurrences of\n"
+ "this option, the largest specified <N> has precedence. The\n"
+ "default is 5.\n"));
+
typedef cl::list<std::string>::const_iterator prefix_iterator;
@@ -150,10 +158,16 @@ struct MarkerStyle {
raw_ostream::Colors Color;
/// A note to follow the marker, or empty string if none.
std::string Note;
+ /// Does this marker indicate inclusion by the input filter implied by
+ /// -dump-input=fail?
+ bool FiltersAsError;
MarkerStyle() {}
MarkerStyle(char Lead, raw_ostream::Colors Color,
- const std::string &Note = "")
- : Lead(Lead), Color(Color), Note(Note) {}
+ const std::string &Note = "", bool FiltersAsError = false)
+ : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) {
+ assert((!FiltersAsError || !Note.empty()) &&
+ "expected error diagnostic to have note");
+ }
};
static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
@@ -161,18 +175,22 @@ static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
case FileCheckDiag::MatchFoundAndExpected:
return MarkerStyle('^', raw_ostream::GREEN);
case FileCheckDiag::MatchFoundButExcluded:
- return MarkerStyle('!', raw_ostream::RED, "error: no match expected");
+ return MarkerStyle('!', raw_ostream::RED, "error: no match expected",
+ /*FiltersAsError=*/true);
case FileCheckDiag::MatchFoundButWrongLine:
- return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line");
+ return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line",
+ /*FiltersAsError=*/true);
case FileCheckDiag::MatchFoundButDiscarded:
return MarkerStyle('!', raw_ostream::CYAN,
"discard: overlaps earlier match");
case FileCheckDiag::MatchNoneAndExcluded:
return MarkerStyle('X', raw_ostream::GREEN);
case FileCheckDiag::MatchNoneButExpected:
- return MarkerStyle('X', raw_ostream::RED, "error: no match found");
+ return MarkerStyle('X', raw_ostream::RED, "error: no match found",
+ /*FiltersAsError=*/true);
case FileCheckDiag::MatchFuzzy:
- return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match");
+ return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
+ /*FiltersAsError=*/true);
}
llvm_unreachable_internal("unexpected match type");
}
@@ -183,6 +201,7 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) {
<< "\n"
<< "Related command-line options:\n"
<< " - -dump-input=<value> enables or disables the input dump\n"
+ << " - -dump-input-context=<N> adjusts the context of errors\n"
<< " - -v and -vv add more annotations\n"
<< " - -color forces colors to be enabled both in the dump and below\n"
<< " - -help documents the above options in more detail\n"
@@ -228,6 +247,12 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) {
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
OS << " marks fuzzy match when no match is found\n";
+ // Elided lines.
+ OS << " - ";
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
+ OS << " indicates elided input lines and annotations, as specified by\n"
+ << " -dump-input=fail and -dump-input-context\n";
+
// Colors.
OS << " - colors ";
WithColor(OS, raw_ostream::GREEN, true) << "success";
@@ -248,10 +273,12 @@ struct InputAnnotation {
unsigned DiagIndex;
/// The label for this annotation.
std::string Label;
+ /// Is this the initial fragment of a diagnostic that has been broken across
+ /// multiple lines?
+ bool IsFirstLine;
/// What input line (one-origin indexing) this annotation marks. This might
- /// be
diff erent from the starting line of the original diagnostic if this is
- /// a non-initial fragment of a diagnostic that has been broken across
- /// multiple lines.
+ /// be
diff erent from the starting line of the original diagnostic if
+ /// !IsFirstLine.
unsigned InputLine;
/// The column range (one-origin indexing, open end) in which to mark the
/// input line. If InputEndCol is UINT_MAX, treat it as the last column
@@ -347,6 +374,7 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
// Compute the mark location, and break annotation into multiple
// annotations if it spans multiple lines.
+ A.IsFirstLine = true;
A.InputLine = DiagItr->InputStartLine;
A.InputStartCol = DiagItr->InputStartCol;
if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
@@ -370,6 +398,7 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
InputAnnotation B;
B.DiagIndex = A.DiagIndex;
B.Label = A.Label;
+ B.IsFirstLine = false;
B.InputLine = L;
B.Marker = A.Marker;
B.Marker.Lead = '~';
@@ -386,11 +415,27 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
}
}
+static unsigned FindInputLineInFilter(
+ bool FilterOnError, unsigned CurInputLine,
+ const std::vector<InputAnnotation>::iterator &AnnotationBeg,
+ const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
+ if (!FilterOnError)
+ return CurInputLine;
+ for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
+ ++AnnotationItr) {
+ if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
+ return AnnotationItr->InputLine;
+ }
+ return UINT_MAX;
+}
+
static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
+ bool DumpInputFilterOnError,
+ unsigned DumpInputContext,
StringRef InputFileText,
std::vector<InputAnnotation> &Annotations,
unsigned LabelWidth) {
- OS << "Full input was:\n<<<<<<\n";
+ OS << "Input was:\n<<<<<<\n";
// Sort annotations.
std::sort(Annotations.begin(), Annotations.end(),
@@ -460,12 +505,47 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
// Print annotated input lines.
+ unsigned PrevLineInFilter = 0; // 0 means none so far
+ unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
+ bool PrevLineElided = false;
auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
for (unsigned Line = 1;
InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
++Line) {
const unsigned char *InputFileLine = InputFilePtr;
+ // Compute the previous and next line included by the filter.
+ if (NextLineInFilter < Line)
+ NextLineInFilter = FindInputLineInFilter(DumpInputFilterOnError, Line,
+ AnnotationItr, AnnotationEnd);
+ assert(NextLineInFilter && "expected NextLineInFilter to be computed");
+ if (NextLineInFilter == Line)
+ PrevLineInFilter = Line;
+
+ // Elide this input line and its annotations if it's not within the
+ // context specified by -dump-input-context of an input line included by
+ // the dump filter.
+ if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
+ (NextLineInFilter == UINT_MAX ||
+ Line + DumpInputContext < NextLineInFilter)) {
+ while (InputFilePtr != InputFileEnd && *InputFilePtr != '\n')
+ ++InputFilePtr;
+ if (InputFilePtr != InputFileEnd)
+ ++InputFilePtr;
+ while (AnnotationItr != AnnotationEnd && AnnotationItr->InputLine == Line)
+ ++AnnotationItr;
+ if (!PrevLineElided) {
+ for (unsigned i = 0; i < 3; ++i) {
+ WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
+ << right_justify(".", LabelWidth);
+ OS << '\n';
+ }
+ PrevLineElided = true;
+ }
+ continue;
+ }
+ PrevLineElided = false;
+
// Print right-aligned line number.
WithColor(OS, raw_ostream::BLACK, true)
<< format_decimal(Line, LabelWidth) << ": ";
@@ -553,10 +633,21 @@ int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
"FILECHECK_OPTS");
+
+ // Select -dump-input* values. The -help documentation specifies the default
+ // value and which value to choose if an option is specified multiple times.
+ // In the latter case, the general rule of thumb is to choose the value that
+ // provides the most information.
DumpInputValue DumpInput =
DumpInputs.empty()
? DumpInputFail
: *std::max_element(DumpInputs.begin(), DumpInputs.end());
+ bool DumpInputFilterOnError = DumpInput == DumpInputFail;
+ unsigned DumpInputContext = DumpInputContexts.empty()
+ ? 5
+ : *std::max_element(DumpInputContexts.begin(),
+ DumpInputContexts.end());
+
if (DumpInput == DumpInputHelp) {
DumpInputAnnotationHelp(outs());
return 0;
@@ -689,7 +780,8 @@ int main(int argc, char **argv) {
unsigned LabelWidth;
BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
Annotations, LabelWidth);
- DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth);
+ DumpAnnotatedInput(errs(), Req, DumpInputFilterOnError, DumpInputContext,
+ InputFileText, Annotations, LabelWidth);
}
return ExitCode;
More information about the llvm-commits
mailing list