diff --git a/llvm/test/FileCheck/dump-input-context.txt b/llvm/test/FileCheck/dump-input-context.txt --- a/llvm/test/FileCheck/dump-input-context.txt +++ b/llvm/test/FileCheck/dump-input-context.txt @@ -9,9 +9,9 @@ ; - 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. +; 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 @@ -47,6 +47,7 @@ ; 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 @@ -57,9 +58,9 @@ ; CS-NEXT: . ; CS-NEXT: . ; CS-NEXT: . -; C8-NEXT: 1: foo8 -; C7-NEXT: 2: foo7 -; C6-NEXT: 3: foo6 +; C5-NEXT: 1: foo8 +; C5-NEXT: 2: foo7 +; C5-NEXT: 3: foo6 ; C5-NEXT: 4: foo5 ; C4-NEXT: 5: foo4 ; C3-NEXT: 6: foo3 @@ -75,11 +76,11 @@ ; C4-NEXT: 13: foo4 ; C5-NEXT: 14: foo5 ; C6-NEXT: 15: foo6 -; C7-NEXT: 16: foo7 +; C6-NEXT: 16: foo7 ; CM-NEXT: . ; CM-NEXT: . ; CM-NEXT: . -; C7-NEXT: 17: foo7 +; C6-NEXT: 17: foo7 ; C6-NEXT: 18: foo6 ; C5-NEXT: 19: foo5 ; C4-NEXT: 20: foo4 @@ -96,13 +97,65 @@ ; C5-NEXT: 29: foo5 ; C6-NEXT: 30: foo6 ; C7-NEXT: 31: foo7 -; C8-NEXT: 32: foo8 -; C9-NEXT: 33: foo9 +; 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=. ;-------------------------------------------------- @@ -135,40 +188,35 @@ ; 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. +; 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=6 \ -; RUN: | FileCheck %s -match-full-lines \ -; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,CS,CM,CE +; RUN: -dump-input-context=4 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,CS,CM,CE -; 7 is the boundary case at which the middle ellipsis disappears. +; 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=7 \ -; RUN: | FileCheck %s -match-full-lines \ -; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,CS,CE +; RUN: -dump-input-context=5 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,C5,CM,CE -; 8 is the boundary case at which the start ellipsis disappears. +; 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=8 \ -; RUN: | FileCheck %s -match-full-lines \ -; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,CE +; RUN: -dump-input-context=6 \ +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,C5,C6,CE -; 9 is the boundary case at which the end ellipsis disappears. +; 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=9 \ -; RUN: | FileCheck %s -match-full-lines \ -; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,C9 +; 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 \ -; RUN: -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7,C8,C9 +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,C5,C6,C7 ;-------------------------------------------------- ; Check that -dump-input-context default is 5. @@ -176,8 +224,7 @@ ; 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 +; RUN: | FileCheck %s -match-full-lines -check-prefixes=C0,C1,C2,C3,C4,C5,CM,CE ;-------------------------------------------------- ; Check multiple -dump-input-context options. @@ -225,3 +272,22 @@ ; 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 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 @@ -429,6 +429,25 @@ return UINT_MAX; } +/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would +/// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either +/// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing. +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, @@ -507,7 +526,12 @@ // 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; + 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; @@ -524,37 +548,29 @@ // 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. + // 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)) { - 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; + Line + DumpInputContext < NextLineInFilter)) + LineOS = &ElidedLinesOS; + else { + LineOS = &OS; + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); } - PrevLineElided = false; // 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) @@ -566,7 +582,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); @@ -590,13 +607,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; @@ -621,6 +639,7 @@ ++AnnotationItr; } } + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); OS << ">>>>>>\n"; }