aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/FileCheck/FileCheck.cpp
diff options
context:
space:
mode:
authorJoel E. Denny <jdenny.ornl@gmail.com>2020-07-10 07:50:00 -0400
committerJoel E. Denny <jdenny.ornl@gmail.com>2020-07-10 11:02:10 -0400
commitbce8fced41b96260a42dfbb254240a49769fafa9 (patch)
treea4d6f7d5c9272df53755e8e1f75718326567e6c2 /llvm/utils/FileCheck/FileCheck.cpp
parent23cd70d71c10dc0b31ac37a733349f9de2e9b84c (diff)
downloadllvm-bce8fced41b96260a42dfbb254240a49769fafa9.zip
llvm-bce8fced41b96260a42dfbb254240a49769fafa9.tar.gz
llvm-bce8fced41b96260a42dfbb254240a49769fafa9.tar.bz2
[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
Diffstat (limited to 'llvm/utils/FileCheck/FileCheck.cpp')
-rw-r--r--llvm/utils/FileCheck/FileCheck.cpp114
1 files changed, 103 insertions, 11 deletions
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 659491c..0952d4b 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 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;