[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