diff --git a/llvm/test/FileCheck/dump-input-annotations.txt b/llvm/test/FileCheck/dump-input-annotations.txt --- 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 --- /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=. +;-------------------------------------------------- + +; 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 diff --git a/llvm/test/FileCheck/dump-input-enable.txt b/llvm/test/FileCheck/dump-input-enable.txt --- a/llvm/test/FileCheck/dump-input-enable.txt +++ b/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 ^~~~~ diff --git a/llvm/test/FileCheck/dump-input-filter.txt b/llvm/test/FileCheck/dump-input-filter.txt new file mode 100644 --- /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 --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/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,27 @@ } } +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 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,12 +505,47 @@ 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 @@ 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 @@ 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;