Index: llvm/test/FileCheck/dump-input-annotations.txt =================================================================== --- llvm/test/FileCheck/dump-input-annotations.txt +++ 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 ^~~~~ Index: llvm/test/FileCheck/dump-input-context.txt =================================================================== --- /dev/null +++ llvm/test/FileCheck/dump-input-context.txt @@ -0,0 +1,293 @@ +;-------------------------------------------------- +; 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=4. One becomes useless each time +; -dump-input-context is incremented beyond that because then that ellipsis +; becomes equal to or larger than the input lines it elides. +;-------------------------------------------------- + +; 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 foo0 >> %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: . +; 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: >>>>>> + +; 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: 10: foo1 +; W5-NEXT: check:3 X~~~ error: no match found +; W5-NEXT: 11: foo2 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 12: foo3 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 13: foo4 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 14: foo5 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 15: foo6 +; W5-NEXT: check:3 ~~~~ +; W6-NEXT: 16: foo7 +; W6-NEXT: check:3 ~~~~ +; WM-NEXT: . +; WM-NEXT: . +; WM-NEXT: . +; W6-NEXT: 17: foo7 +; W6-NEXT: check:3 ~~~~ +; W6-NEXT: 18: foo6 +; W6-NEXT: check:3 ~~~~ +; W5-NEXT: 19: foo5 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 20: foo4 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 21: foo3 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 22: foo2 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 23: foo1 +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: 24: lab2 world +; W5-NEXT: label:4 ^~~~ +; W5-NEXT: check:3 ~~~~ +; W5-NEXT: next:5 !~~~~ error: match on wrong line + +;-------------------------------------------------- +; Check -dump-input-context=. +;-------------------------------------------------- + +; 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 + +; 4 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=4 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,CS,CM,CE + +; 5 is the boundary case at which the start ellipsis is useless. +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ +; RUN: -dump-input-context=5 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,C5,CM,CE + +; 6 is the boundary case at which the middle ellipsis is useless. +; 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 -check-prefixes=C0,C1,C2,C3,C4,C5,C6,CE + +; 7 is the boundary case at which the end ellipsis is useless. +; 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 -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7 + +; 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 -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7 + +;-------------------------------------------------- +; 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 -check-prefixes=C0,C1,C2,C3,C4,C5,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 + +;-------------------------------------------------- +; Check how annotations on input lines that might be elided by ellipses affect +; whether they are actually elided. +;-------------------------------------------------- + +; At -dump-input-context=5, the ellipsis is useful but only when annotations on +; elided input lines are considered. +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=fail -vv %t.wide.chk < %t.wide.in 2>&1 \ +; RUN: -dump-input-context=5 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=W5,WM + +; At -dump-input-context=6, the ellipsis is not useful even when annotations on +; elided input lines are considered. +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=fail -vv %t.wide.chk < %t.wide.in 2>&1 \ +; RUN: -dump-input-context=6 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=W5,W6 Index: llvm/test/FileCheck/dump-input-enable.txt =================================================================== --- llvm/test/FileCheck/dump-input-enable.txt +++ llvm/test/FileCheck/dump-input-enable.txt @@ -236,7 +236,7 @@ ; 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 @@ ; 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 ^~~~~ Index: llvm/test/FileCheck/dump-input-filter.txt =================================================================== --- /dev/null +++ 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 Index: llvm/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/utils/FileCheck/FileCheck.cpp +++ llvm/utils/FileCheck/FileCheck.cpp @@ -128,6 +128,14 @@ clEnumValN(DumpInputFail, "fail", "Dump input on failure"), clEnumValN(DumpInputNever, "never", "Never dump input"))); +static cl::list DumpInputContexts( + "dump-input-context", cl::value_desc("N"), + cl::desc("In the dump requested by -dump-input=fail, print input\n" + "lines before and input lines after the starting line of\n" + "any error diagnostic. When there are multiple occurrences of\n" + "this option, the largest specified has precedence. The\n" + "default is 5.\n")); + typedef cl::list::const_iterator prefix_iterator; @@ -150,10 +158,16 @@ 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 @@ 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 @@ << "\n" << "Related command-line options:\n" << " - -dump-input= enables or disables the input dump\n" + << " - -dump-input-context= 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 @@ 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 @@ 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 @@ // 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 @@ 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,43 @@ } } +static unsigned FindInputLineInFilter( + bool FilterOnError, unsigned CurInputLine, + const std::vector::iterator &AnnotationBeg, + const std::vector::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 DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines, + unsigned LabelWidth) { + if (ElidedLines.empty()) + return; + unsigned EllipsisLines = 3; + if (EllipsisLines < StringRef(ElidedLines).count('\n')) { + for (unsigned i = 0; i < EllipsisLines; ++i) { + WithColor(OS, raw_ostream::BLACK, /*Bold=*/true) + << right_justify(".", LabelWidth); + OS << '\n'; + } + } else + OS << ElidedLines; + ElidedLines.clear(); +} + static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, + bool DumpInputFilterOnError, + unsigned DumpInputContext, StringRef InputFileText, std::vector &Annotations, unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; + OS << "Input was:\n<<<<<<\n"; // Sort annotations. std::sort(Annotations.begin(), Annotations.end(), @@ -460,21 +521,53 @@ 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 + std::string ElidedLines; + raw_string_ostream ElidedLinesOS(ElidedLines); + ColorMode TheColorMode = + WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable; + if (TheColorMode == ColorMode::Enable) + ElidedLinesOS.enable_colors(true); 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. However, in case the resulting ellipsis would occupy + // more lines than the input lines and annotations it elides, buffer the + // elided lines and annotations so we can print them instead. + raw_ostream *LineOS = &OS; + if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) && + (NextLineInFilter == UINT_MAX || + Line + DumpInputContext < NextLineInFilter)) + LineOS = &ElidedLinesOS; + else { + LineOS = &OS; + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); + } + // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) + WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false, + TheColorMode) << format_decimal(Line, LabelWidth) << ": "; // For the case where -v and colors are enabled, find the annotations for // good matches for expected patterns in order to highlight everything // else in the line. There are no such annotations if -v is disabled. std::vector FoundAndExpectedMatches; - if (Req.Verbose && WithColor(OS).colorsEnabled()) { + if (Req.Verbose && TheColorMode == ColorMode::Enable) { for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; ++I) { if (I->FoundAndExpectedMatch) @@ -486,7 +579,8 @@ // expected patterns. bool Newline = false; { - WithColor COS(OS); + WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false, + /*BG=*/false, TheColorMode); bool InMatch = false; if (Req.Verbose) COS.changeColor(raw_ostream::CYAN, true, true); @@ -510,13 +604,14 @@ ++InputFilePtr; } } - OS << '\n'; + *LineOS << '\n'; unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; // Print any annotations. while (AnnotationItr != AnnotationEnd && AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); + 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, LabelWidth) << " "; unsigned Col; @@ -541,6 +636,7 @@ ++AnnotationItr; } } + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); OS << ">>>>>>\n"; } @@ -553,10 +649,21 @@ 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 +796,8 @@ 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;