1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

[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
This commit is contained in:
Joel E. Denny 2020-07-10 07:50:00 -04:00
parent f387d6739f
commit 82eeb7f9a7
5 changed files with 541 additions and 14 deletions

View File

@ -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 ^~~~~

View File

@ -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 differently.
;- - - - - - - - - - - - - - - - - - - - - - - - -
; 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

View File

@ -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 ^~~~~

View File

@ -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

View File

@ -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 different 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 different 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;