<div dir="ltr">Hey there - a bunch of these commit messages came through as N/7, all otherwise containing an identical description. Could you reply to the commit emails with specifics about what was changed in each commit - it'd be helpful for future archaeology (ideally the specific patch descriptions would be in the specific commits, but having the info on the mailing list at least will help some folks).</div><br><div class="gmail_quote"><div dir="ltr">On Mon, Dec 17, 2018 at 4:04 PM Joel E. Denny via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: jdenny<br>
Date: Mon Dec 17 16:01:39 2018<br>
New Revision: 349418<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=349418&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=349418&view=rev</a><br>
Log:<br>
[FileCheck] Annotate input dump (1/7)<br>
<br>
Extend FileCheck to dump its input annotated with FileCheck's<br>
diagnostics: errors, good matches if -v, and additional information if<br>
-vv. The goal is to make it easier to visualize FileCheck's matching<br>
behavior when debugging.<br>
<br>
Each patch in this series implements input annotations for a<br>
particular category of FileCheck diagnostics. While the first few<br>
patches alone are somewhat useful, the annotations become much more<br>
useful as later patches implement annotations for -v and -vv<br>
diagnostics, which show the matching behavior leading up to the error.<br>
<br>
This first patch implements boilerplate plus input annotations for<br>
error diagnostics reporting that no matches were found for a<br>
directive. These annotations mark the search ranges of the failed<br>
directives. Instead of using the usual `^~~`, which is used by later<br>
patches for good matches, these annotations use `X~~` so that this<br>
category of errors is visually distinct.<br>
<br>
For example:<br>
<br>
```<br>
$ FileCheck -dump-input=help<br>
The following description was requested by -dump-input=help to<br>
explain the input annotations printed by -dump-input=always and<br>
-dump-input=fail:<br>
<br>
- L: labels line number L of the input file<br>
- T:L labels the match result for a pattern of type T from line L of<br>
the check file<br>
- X~~ marks search range when no match is found<br>
- colors error<br>
<br>
If you are not seeing color above or in input dumps, try: -color<br>
<br>
$ FileCheck -v -dump-input=always check1 < input1 |& sed -n '/^Input file/,$p'<br>
Input file: <stdin><br>
Check file: check1<br>
<br>
-dump-input=help describes the format of the following dump.<br>
<br>
Full input was:<br>
<<<<<<<br>
1: ; abc def<br>
2: ; ghI jkl<br>
next:3 X~~~~~~~~ error: no match found<br>
>>>>>><br>
<br>
$ cat check1<br>
CHECK: abc<br>
CHECK-SAME: def<br>
CHECK-NEXT: ghi<br>
CHECK-SAME: jkl<br>
<br>
$ cat input1<br>
; abc def<br>
; ghI jkl<br>
```<br>
<br>
Some additional details related to the boilerplate:<br>
<br>
* Enabling: The annotated input dump is enabled by `-dump-input`,<br>
which can also be set via the `FILECHECK_OPTS` environment variable.<br>
Accepted values are `help`, `always`, `fail`, or `never`. As shown<br>
above, `help` describes the format of the dump. `always` is helpful<br>
when you want to investigate a successful FileCheck run, perhaps for<br>
an unexpected pass. `-dump-input-on-failure` and<br>
`FILECHECK_DUMP_INPUT_ON_FAILURE` remain as a deprecated alias for<br>
`-dump-input=fail`.<br>
<br>
* Diagnostics: The usual diagnostics are not suppressed in this mode<br>
and are printed first. For brevity in the example above, I've<br>
omitted them using a sed command. Sometimes they're perfectly<br>
sufficient, and then they make debugging quicker than if you were<br>
forced to hunt through a dump of long input looking for the error.<br>
If you think they'll get in the way sometimes, keep in mind that<br>
it's pretty easy to grep for the start of the input dump, which is<br>
`<<<`.<br>
<br>
* Colored Annotations: The annotated input is colored if colors are<br>
enabled (enabling colors can be forced using -color). For example,<br>
errors are red. However, as in the above example, colors are not<br>
vital to reading the annotations.<br>
<br>
I don't know how to test color in the output, so any hints here would<br>
be appreciated.<br>
<br>
Reviewed By: george.karpenkov, zturner, probinson<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D52999" rel="noreferrer" target="_blank">https://reviews.llvm.org/D52999</a><br>
<br>
Added:<br>
llvm/trunk/test/FileCheck/dump-input-annotations.txt<br>
llvm/trunk/test/FileCheck/dump-input-enable.txt<br>
llvm/trunk/test/FileCheck/no-check-file.txt<br>
Removed:<br>
llvm/trunk/test/FileCheck/verbose_mode.txt<br>
Modified:<br>
llvm/trunk/docs/CommandGuide/FileCheck.rst<br>
llvm/trunk/include/llvm/Support/FileCheck.h<br>
llvm/trunk/lib/Support/FileCheck.cpp<br>
llvm/trunk/utils/FileCheck/FileCheck.cpp<br>
<br>
Modified: llvm/trunk/docs/CommandGuide/FileCheck.rst<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=349418&r1=349417&r2=349418&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=349418&r1=349417&r2=349418&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)<br>
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Mon Dec 17 16:01:39 2018<br>
@@ -80,9 +80,16 @@ and from the command line.<br>
-verify``. With this option FileCheck will verify that input does not contain<br>
warnings not covered by any ``CHECK:`` patterns.<br>
<br>
+.. option:: --dump-input <mode><br>
+<br>
+ Dump input to stderr, adding annotations representing currently enabled<br>
+ diagnostics. Do this either 'always', on 'fail', or 'never'. Specify 'help'<br>
+ to explain the dump format and quit.<br>
+<br>
.. option:: --dump-input-on-failure<br>
<br>
- When the check fails, dump all of the original input.<br>
+ When the check fails, dump all of the original input. This option is<br>
+ deprecated in favor of `--dump-input=fail`.<br>
<br>
.. option:: --enable-var-scope<br>
<br>
<br>
Modified: llvm/trunk/include/llvm/Support/FileCheck.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=349418&r1=349417&r2=349418&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=349418&r1=349417&r2=349418&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)<br>
+++ llvm/trunk/include/llvm/Support/FileCheck.h Mon Dec 17 16:01:39 2018<br>
@@ -82,6 +82,8 @@ public:<br>
};<br>
}<br>
<br>
+struct FileCheckDiag;<br>
+<br>
class FileCheckPattern {<br>
SMLoc PatternLoc;<br>
<br>
@@ -146,6 +148,28 @@ private:<br>
};<br>
<br>
//===----------------------------------------------------------------------===//<br>
+/// Summary of a FileCheck diagnostic.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+struct FileCheckDiag {<br>
+ /// What is the FileCheck directive for this diagnostic?<br>
+ Check::FileCheckType CheckTy;<br>
+ /// Where is the FileCheck directive for this diagnostic?<br>
+ unsigned CheckLine, CheckCol;<br>
+ /// What kind of match result does this diagnostic describe?<br>
+ enum MatchType {<br>
+ // TODO: More members will appear with later patches in this series.<br>
+ /// Indicates no match for an expected pattern.<br>
+ MatchNoneButExpected,<br>
+ MatchTypeCount,<br>
+ } MatchTy;<br>
+ /// The search range.<br>
+ unsigned InputStartLine, InputStartCol, InputEndLine, InputEndCol;<br>
+ FileCheckDiag(const SourceMgr &SM, const Check::FileCheckType &CheckTy,<br>
+ SMLoc CheckLoc, MatchType MatchTy, SMRange InputRange);<br>
+};<br>
+<br>
+//===----------------------------------------------------------------------===//<br>
// Check Strings.<br>
//===----------------------------------------------------------------------===//<br>
<br>
@@ -169,7 +193,7 @@ struct FileCheckString {<br>
<br>
size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,<br>
size_t &MatchLen, StringMap<StringRef> &VariableTable,<br>
- FileCheckRequest &Req) const;<br>
+ FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const;<br>
<br>
bool CheckNext(const SourceMgr &SM, StringRef Buffer) const;<br>
bool CheckSame(const SourceMgr &SM, StringRef Buffer) const;<br>
@@ -180,7 +204,8 @@ struct FileCheckString {<br>
size_t CheckDag(const SourceMgr &SM, StringRef Buffer,<br>
std::vector<const FileCheckPattern *> &NotStrings,<br>
StringMap<StringRef> &VariableTable,<br>
- const FileCheckRequest &Req) const;<br>
+ const FileCheckRequest &Req,<br>
+ std::vector<FileCheckDiag> *Diags) const;<br>
};<br>
<br>
/// FileCheck class takes the request and exposes various methods that<br>
@@ -217,7 +242,8 @@ public:<br>
///<br>
/// Returns false if the input fails to satisfy the checks.<br>
bool CheckInput(SourceMgr &SM, StringRef Buffer,<br>
- ArrayRef<FileCheckString> CheckStrings);<br>
+ ArrayRef<FileCheckString> CheckStrings,<br>
+ std::vector<FileCheckDiag> *Diags = nullptr);<br>
};<br>
} // namespace llvm<br>
#endif<br>
<br>
Modified: llvm/trunk/lib/Support/FileCheck.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=349418&r1=349417&r2=349418&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=349418&r1=349417&r2=349418&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Support/FileCheck.cpp (original)<br>
+++ llvm/trunk/lib/Support/FileCheck.cpp Mon Dec 17 16:01:39 2018<br>
@@ -412,6 +412,21 @@ void FileCheckPattern::PrintVariableUses<br>
}<br>
}<br>
<br>
+static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,<br>
+ const SourceMgr &SM, SMLoc Loc,<br>
+ Check::FileCheckType CheckTy,<br>
+ StringRef Buffer, size_t Pos, size_t Len,<br>
+ std::vector<FileCheckDiag> *Diags) {<br>
+ SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);<br>
+ SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);<br>
+ SMRange Range(Start, End);<br>
+ // TODO: The second condition will disappear when we extend this to handle<br>
+ // more match types.<br>
+ if (Diags && MatchTy != FileCheckDiag::MatchTypeCount)<br>
+ Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);<br>
+ return Range;<br>
+}<br>
+<br>
void FileCheckPattern::PrintFuzzyMatch(<br>
const SourceMgr &SM, StringRef Buffer,<br>
const StringMap<StringRef> &VariableTable) const {<br>
@@ -531,6 +546,22 @@ llvm::FileCheck::CanonicalizeFile(Memory<br>
return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);<br>
}<br>
<br>
+FileCheckDiag::FileCheckDiag(const SourceMgr &SM,<br>
+ const Check::FileCheckType &CheckTy,<br>
+ SMLoc CheckLoc, MatchType MatchTy,<br>
+ SMRange InputRange)<br>
+ : CheckTy(CheckTy), MatchTy(MatchTy) {<br>
+ auto Start = SM.getLineAndColumn(InputRange.Start);<br>
+ auto End = SM.getLineAndColumn(InputRange.End);<br>
+ InputStartLine = Start.first;<br>
+ InputStartCol = Start.second;<br>
+ InputEndLine = End.first;<br>
+ InputEndCol = End.second;<br>
+ Start = SM.getLineAndColumn(CheckLoc);<br>
+ CheckLine = Start.first;<br>
+ CheckCol = Start.second;<br>
+}<br>
+<br>
static bool IsPartOfWord(char c) {<br>
return (isalnum(c) || c == '-' || c == '_');<br>
}<br>
@@ -897,7 +928,8 @@ static void PrintNoMatch(bool ExpectedMa<br>
StringRef Prefix, SMLoc Loc,<br>
const FileCheckPattern &Pat, int MatchedCount,<br>
StringRef Buffer, StringMap<StringRef> &VariableTable,<br>
- bool VerboseVerbose) {<br>
+ bool VerboseVerbose,<br>
+ std::vector<FileCheckDiag> *Diags) {<br>
if (!ExpectedMatch && !VerboseVerbose)<br>
return;<br>
<br>
@@ -915,9 +947,11 @@ static void PrintNoMatch(bool ExpectedMa<br>
// Print the "scanning from here" line. If the current position is at the<br>
// end of a line, advance to the start of the next line.<br>
Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));<br>
-<br>
- SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,<br>
- "scanning from here");<br>
+ SMRange SearchRange = ProcessMatchResult(<br>
+ ExpectedMatch ? FileCheckDiag::MatchNoneButExpected<br>
+ : FileCheckDiag::MatchTypeCount,<br>
+ SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags);<br>
+ SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");<br>
<br>
// Allow the pattern to print additional information if desired.<br>
Pat.PrintVariableUses(SM, Buffer, VariableTable);<br>
@@ -928,9 +962,10 @@ static void PrintNoMatch(bool ExpectedMa<br>
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,<br>
const FileCheckString &CheckStr, int MatchedCount,<br>
StringRef Buffer, StringMap<StringRef> &VariableTable,<br>
- bool VerboseVerbose) {<br>
+ bool VerboseVerbose,<br>
+ std::vector<FileCheckDiag> *Diags) {<br>
PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,<br>
- MatchedCount, Buffer, VariableTable, VerboseVerbose);<br>
+ MatchedCount, Buffer, VariableTable, VerboseVerbose, Diags);<br>
}<br>
<br>
/// Count the number of newlines in the specified range.<br>
@@ -958,9 +993,10 @@ static unsigned CountNumNewlinesBetween(<br>
<br>
/// Match check string and its "not strings" and/or "dag strings".<br>
size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,<br>
- bool IsLabelScanMode, size_t &MatchLen,<br>
- StringMap<StringRef> &VariableTable,<br>
- FileCheckRequest &Req) const {<br>
+ bool IsLabelScanMode, size_t &MatchLen,<br>
+ StringMap<StringRef> &VariableTable,<br>
+ FileCheckRequest &Req,<br>
+ std::vector<FileCheckDiag> *Diags) const {<br>
size_t LastPos = 0;<br>
std::vector<const FileCheckPattern *> NotStrings;<br>
<br>
@@ -970,7 +1006,7 @@ size_t FileCheckString::Check(const Sour<br>
// over the block again (including the last CHECK-LABEL) in normal mode.<br>
if (!IsLabelScanMode) {<br>
// Match "dag strings" (with mixed "not strings" if any).<br>
- LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req);<br>
+ LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req, Diags);<br>
if (LastPos == StringRef::npos)<br>
return StringRef::npos;<br>
}<br>
@@ -992,7 +1028,7 @@ size_t FileCheckString::Check(const Sour<br>
// report<br>
if (MatchPos == StringRef::npos) {<br>
PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable,<br>
- Req.VerboseVerbose);<br>
+ Req.VerboseVerbose, Diags);<br>
return StringRef::npos;<br>
}<br>
PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos,<br>
@@ -1116,7 +1152,7 @@ bool FileCheckString::CheckNot(const Sou<br>
<br>
if (Pos == StringRef::npos) {<br>
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,<br>
- VariableTable, Req.VerboseVerbose);<br>
+ VariableTable, Req.VerboseVerbose, nullptr);<br>
continue;<br>
}<br>
<br>
@@ -1130,10 +1166,12 @@ bool FileCheckString::CheckNot(const Sou<br>
}<br>
<br>
/// Match "dag strings" and their mixed "not strings".<br>
-size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,<br>
- std::vector<const FileCheckPattern *> &NotStrings,<br>
- StringMap<StringRef> &VariableTable,<br>
- const FileCheckRequest &Req) const {<br>
+size_t<br>
+FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,<br>
+ std::vector<const FileCheckPattern *> &NotStrings,<br>
+ StringMap<StringRef> &VariableTable,<br>
+ const FileCheckRequest &Req,<br>
+ std::vector<FileCheckDiag> *Diags) const {<br>
if (DagNotStrings.empty())<br>
return 0;<br>
<br>
@@ -1177,7 +1215,7 @@ size_t FileCheckString::CheckDag(const S<br>
// that group of CHECK-DAGs fails immediately.<br>
if (MatchPosBuf == StringRef::npos) {<br>
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,<br>
- VariableTable, Req.VerboseVerbose);<br>
+ VariableTable, Req.VerboseVerbose, Diags);<br>
return StringRef::npos;<br>
}<br>
// Re-calc it as the offset relative to the start of the original string.<br>
@@ -1318,7 +1356,8 @@ static void ClearLocalVars(StringMap<Str<br>
///<br>
/// Returns false if the input fails to satisfy the checks.<br>
bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,<br>
- ArrayRef<FileCheckString> CheckStrings) {<br>
+ ArrayRef<FileCheckString> CheckStrings,<br>
+ std::vector<FileCheckDiag> *Diags) {<br>
bool ChecksFailed = false;<br>
<br>
/// VariableTable - This holds all the current filecheck variables.<br>
@@ -1341,9 +1380,8 @@ bool llvm::FileCheck::CheckInput(SourceM<br>
<br>
// Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG<br>
size_t MatchLabelLen = 0;<br>
- size_t MatchLabelPos =<br>
- CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, VariableTable,<br>
- Req);<br>
+ size_t MatchLabelPos = CheckLabelStr.Check(<br>
+ SM, Buffer, true, MatchLabelLen, VariableTable, Req, Diags);<br>
if (MatchLabelPos == StringRef::npos)<br>
// Immediately bail of CHECK-LABEL fails, nothing else we can do.<br>
return false;<br>
@@ -1362,8 +1400,8 @@ bool llvm::FileCheck::CheckInput(SourceM<br>
// Check each string within the scanned region, including a second check<br>
// of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)<br>
size_t MatchLen = 0;<br>
- size_t MatchPos =<br>
- CheckStr.Check(SM, CheckRegion, false, MatchLen, VariableTable, Req);<br>
+ size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen,<br>
+ VariableTable, Req, Diags);<br>
<br>
if (MatchPos == StringRef::npos) {<br>
ChecksFailed = true;<br>
<br>
Added: llvm/trunk/test/FileCheck/dump-input-annotations.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/dump-input-annotations.txt?rev=349418&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/dump-input-annotations.txt?rev=349418&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/FileCheck/dump-input-annotations.txt (added)<br>
+++ llvm/trunk/test/FileCheck/dump-input-annotations.txt Mon Dec 17 16:01:39 2018<br>
@@ -0,0 +1,241 @@<br>
+;--------------------------------------------------<br>
+; Use -strict-whitespace to check marker alignment here.<br>
+; (Also check multiline marker where start/end columns vary across lines.)<br>
+;<br>
+; In the remaining checks, don't use -strict-whitespace and thus check just the<br>
+; presence, order, and lengths of markers. That way, if we ever change padding<br>
+; within line labels, we don't have to adjust so many tests.<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: echo 'hello world' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'goodbye' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'world' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK: hello' > %t.chk<br>
+; RUN: echo 'CHECK: universe' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -strict-whitespace -match-full-lines -check-prefix=ALIGN %s<br>
+<br>
+; ALIGN:Full input was:<br>
+; ALIGN-NEXT:<<<<<<<br>
+; ALIGN-NEXT: 1: hello world<br>
+; ALIGN-NEXT:check:2 X~~~~<br>
+; ALIGN-NEXT: 2: goodbye<br>
+; ALIGN-NEXT:check:2 ~~~~~~~<br>
+; ALIGN-NEXT: 3: world<br>
+; ALIGN-NEXT:check:2 ~~~~~ error: no match found<br>
+; ALIGN-NEXT:>>>>>><br>
+; ALIGN-NOT:{{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK (also: multi-line search range)<br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+<br>
+; RUN: echo 'hello' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'again' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'whirled' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK: hello' > %t.chk<br>
+; RUN: echo 'CHECK: world' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefix=CHK<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=CHK,CHK-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=CHK,CHK-V<br>
+<br>
+; CHK: <<<<<<<br>
+; CHK-NEXT: 1: hello<br>
+; CHK-NEXT: 2: again<br>
+; CHK-NEXT: check:2 X~~~~<br>
+; CHK-NEXT: 3: whirled<br>
+; CHK-NEXT: check:2 ~~~~~~~ error: no match found<br>
+; CHK-NEXT: >>>>>><br>
+; CHK-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-COUNT-<num><br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+<br>
+; RUN: echo 'pete' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'repete' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'repeat' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK-COUNT-3: pete' > %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefix=CNT<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=CNT,CNT-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=CNT,CNT-V<br>
+<br>
+; CNT: <<<<<<<br>
+; CNT-NEXT: 1: pete<br>
+; CNT-NEXT: 2: repete<br>
+; CNT-NEXT: 3: repeat<br>
+; CNT-NEXT: count:1 X~~~~~ error: no match found<br>
+; CNT-NEXT: >>>>>><br>
+; CNT-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-NEXT (also: EOF search-range)<br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+<br>
+; RUN: echo 'hello' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'again' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK: hello' > %t.chk<br>
+; RUN: echo 'CHECK-NEXT: again' >> %t.chk<br>
+; RUN: echo 'CHECK-NEXT: world' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefix=NXT<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NXT,NXT-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NXT,NXT-V,NXT-VV<br>
+<br>
+; NXT: <<<<<<<br>
+; NXT-NEXT: 1: hello<br>
+; NXT-NEXT: 2: again<br>
+; NXT-NEXT: 3:<br>
+; NXT-NEXT: next:3 X error: no match found<br>
+; NXT-NEXT: >>>>>><br>
+; NXT-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-SAME (also: single-char search range)<br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+<br>
+; RUN: echo 'hello world!' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK: hello' > %t.chk<br>
+; RUN: echo 'CHECK-SAME: world' >> %t.chk<br>
+; RUN: echo 'CHECK-SAME: again' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefix=SAM<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=SAM,SAM-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=SAM,SAM-V,SAM-VV<br>
+<br>
+; SAM: <<<<<<<br>
+; SAM-NEXT: 1: hello world!<br>
+; SAM-NEXT: same:3 X error: no match found<br>
+; SAM-NEXT: >>>>>><br>
+; SAM-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-EMPTY (also: search range ends at label)<br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+;<br>
+; CHECK-EMPTY always seems to match an empty line at EOF (illegally when it's<br>
+; not the next line) unless either (1) the last line is non-empty and has no<br>
+; newline or (2) there's a CHECK-LABEL to end the search range before EOF. We<br>
+; choose scenario 2 to check the case of no match.<br>
+<br>
+; RUN: echo 'hello' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo '' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'world' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'label' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK: hello' > %t.chk<br>
+; RUN: echo 'CHECK-EMPTY:' >> %t.chk<br>
+; RUN: echo 'CHECK-EMPTY:' >> %t.chk<br>
+; RUN: echo 'CHECK-LABEL: label' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefix=EMP<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=EMP,EMP-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=EMP,EMP-V,EMP-VV<br>
+<br>
+; EMP: <<<<<<<br>
+; EMP-NEXT: 1: hello<br>
+; EMP-NEXT: 2:<br>
+; EMP-NEXT: 3: world<br>
+; EMP-NEXT: empty:3 X~~~~<br>
+; EMP-NEXT: 4: label<br>
+; EMP-NEXT: empty:3 ~~~~~ error: no match found<br>
+; EMP-NEXT: >>>>>><br>
+; EMP-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-DAG<br>
+;--------------------------------------------------<br>
+<br>
+; Good match, discarded match plus good match, and no match.<br>
+<br>
+; RUN: echo 'abc' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'def' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'abc' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK-DAG: def' > %t.chk<br>
+; RUN: echo 'CHECK-DAG: abc' >> %t.chk<br>
+; RUN: echo 'CHECK-DAG: abc' >> %t.chk<br>
+; RUN: echo 'CHECK-DAG: def' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG<br>
+<br>
+; DAG: <<<<<<<br>
+; DAG-NEXT: 1: abc<br>
+; DAG-NEXT: 2: def<br>
+; DAG-NEXT: 3: abc<br>
+; DAG-NEXT: dag:4 X~~ error: no match found<br>
+; DAG-NEXT: >>>>>><br>
+; DAG-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; CHECK-LABEL<br>
+;--------------------------------------------------<br>
+<br>
+; Good match and no match.<br>
+<br>
+; RUN: echo 'lab0' > %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'foo' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'lab1' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+; RUN: echo 'bar' >> %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a><br>
+<br>
+; RUN: echo 'CHECK-LABEL: lab0' > %t.chk<br>
+; RUN: echo 'CHECK: foo' >> %t.chk<br>
+; RUN: echo 'CHECK-LABEL: lab2' >> %t.chk<br>
+<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=LAB<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -v 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=LAB,LAB-V<br>
+; RUN: not FileCheck -dump-input=always -input-file %<a href="http://t.in" rel="noreferrer" target="_blank">t.in</a> %t.chk -vv 2>&1 \<br>
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=LAB,LAB-V,LAB-VV<br>
+<br>
+; LAB: <<<<<<<br>
+; LAB-NEXT: 1: lab0<br>
+; LAB-NEXT: 2: foo<br>
+; LAB-NEXT: label:3 X~~<br>
+; LAB-NEXT: 3: lab1<br>
+; LAB-NEXT: label:3 ~~~~<br>
+; LAB-NEXT: 4: bar<br>
+; LAB-NEXT: label:3 ~~~ error: no match found<br>
+; LAB-NEXT: >>>>>><br>
+; LAB-NOT: {{.}}<br>
+<br>
+<br>
<br>
Added: llvm/trunk/test/FileCheck/dump-input-enable.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/dump-input-enable.txt?rev=349418&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/dump-input-enable.txt?rev=349418&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/FileCheck/dump-input-enable.txt (added)<br>
+++ llvm/trunk/test/FileCheck/dump-input-enable.txt Mon Dec 17 16:01:39 2018<br>
@@ -0,0 +1,126 @@<br>
+; RUN: echo ciao > %t.good<br>
+; RUN: echo world >> %t.good<br>
+<br>
+; RUN: echo hello > %t.err<br>
+; RUN: echo world >> %t.err<br>
+<br>
+; RUN: echo 'CHECK: ciao' > %t.check<br>
+; RUN: echo 'CHECK-NEXT: world' >> %t.check<br>
+<br>
+;--------------------------------------------------<br>
+; unknown value<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: not FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=foobar 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL<br>
+<br>
+; No positional arg.<br>
+; RUN: not FileCheck -dump-input=foobar 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL<br>
+<br>
+BADVAL: FileCheck: for the -dump-input option: Cannot find option named 'foobar'!<br>
+<br>
+;--------------------------------------------------<br>
+; help<br>
+;--------------------------------------------------<br>
+<br>
+; Appended to normal command line.<br>
+; RUN: FileCheck -input-file %t.err -color %t.check -dump-input=help \<br>
+; RUN: | FileCheck %s -check-prefix=HELP<br>
+<br>
+; No positional arg.<br>
+; RUN: FileCheck -dump-input=help | FileCheck %s -check-prefix=HELP<br>
+<br>
+HELP-NOT: {{.}}<br>
+HELP: The following description was requested by -dump-input=help<br>
+HELP: try{{.*}}-color<br>
+HELP-NOT: {{.}}<br>
+<br>
+;--------------------------------------------------<br>
+; never<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=never 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP -allow-empty<br>
+<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=never 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP<br>
+<br>
+;--------------------------------------------------<br>
+; default: never<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP -allow-empty<br>
+<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP<br>
+<br>
+;--------------------------------------------------<br>
+; fail<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=fail 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP -allow-empty<br>
+<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=fail 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-ERR<br>
+<br>
+;--------------------------------------------------<br>
+; -dump-input-on-failure<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input-on-failure 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP -allow-empty<br>
+<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input-on-failure 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-ERR<br>
+<br>
+; RUN: env FILECHECK_DUMP_INPUT_ON_FAILURE=1 \<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-NODUMP -allow-empty<br>
+<br>
+; RUN: env FILECHECK_DUMP_INPUT_ON_FAILURE=1 \<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-ERR<br>
+<br>
+;--------------------------------------------------<br>
+; always<br>
+;--------------------------------------------------<br>
+<br>
+; RUN: FileCheck -input-file %t.good %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=always -v 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-GOOD<br>
+<br>
+; RUN: not FileCheck -input-file %t.err %t.check -check-prefix=CHECK \<br>
+; RUN: -match-full-lines -dump-input=always 2>&1 \<br>
+; RUN: | FileCheck %s -match-full-lines -check-prefix=CHECK-ERR<br>
+<br>
+; END.<br>
+<br>
+; CHECK-GOOD: Full input was:<br>
+; CHECK-GOOD-NEXT: <<<<<<<br>
+; CHECK-GOOD-NEXT: 1: ciao<br>
+; CHECK-GOOD-NEXT: 2: world<br>
+; CHECK-GOOD-NEXT: >>>>>><br>
+<br>
+; CHECK-ERR: Full input was:<br>
+; CHECK-ERR-NEXT: <<<<<<<br>
+; CHECK-ERR-NEXT: 1: hello<br>
+; CHECK-ERR-NEXT: check:1 X~~~~<br>
+; CHECK-ERR-NEXT: 2: world<br>
+; CHECK-ERR-NEXT: check:1 ~~~~~ error: no match found<br>
+; CHECK-ERR-NEXT: >>>>>><br>
+<br>
+; CHECK-NODUMP-NOT: <<<<<<<br>
<br>
Added: llvm/trunk/test/FileCheck/no-check-file.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/no-check-file.txt?rev=349418&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/no-check-file.txt?rev=349418&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/FileCheck/no-check-file.txt (added)<br>
+++ llvm/trunk/test/FileCheck/no-check-file.txt Mon Dec 17 16:01:39 2018<br>
@@ -0,0 +1,3 @@<br>
+; RUN: not FileCheck 2>&1 | FileCheck %s<br>
+<br>
+CHECK: <check-file> not specified<br>
<br>
Removed: llvm/trunk/test/FileCheck/verbose_mode.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/verbose_mode.txt?rev=349417&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/verbose_mode.txt?rev=349417&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/FileCheck/verbose_mode.txt (original)<br>
+++ llvm/trunk/test/FileCheck/verbose_mode.txt (removed)<br>
@@ -1,17 +0,0 @@<br>
-; RUN: not FileCheck -input-file %s %s --check-prefix=CHECK1 --match-full-lines --dump-input-on-failure 2>&1 | FileCheck %s --check-prefix=CHECKERROR --match-full-lines<br>
-; RUN: env FILECHECK_DUMP_INPUT_ON_FAILURE=1 not FileCheck -input-file %s %s --check-prefix=CHECK1 --match-full-lines 2>&1 | FileCheck %s --check-prefix=CHECKERROR --match-full-lines<br>
-; RUN: env FILECHECK_DUMP_INPUT_ON_FAILURE=1 not FileCheck -input-file %s %s --check-prefix=CHECK1 --match-full-lines --dump-input-on-failure=0 2>&1 | FileCheck %s --check-prefix=CHECKERRORNOVERBOSE --match-full-lines<br>
-<br>
-hello<br>
-world<br>
-<br>
-; CHECK1: ciao<br>
-; CHECK1-NEXT: world<br>
-<br>
-; CHECKERROR: Full input was:<br>
-; CHECKERROR-NEXT: <<<<<<<br>
-; CHECKERROR: hello<br>
-; CHECKERROR: world<br>
-; CHECKERROR: >>>>>><br>
-<br>
-; CHECKERRORNOVERBOSE-NOT: <<<<<<<br>
<br>
Modified: llvm/trunk/utils/FileCheck/FileCheck.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/FileCheck/FileCheck.cpp?rev=349418&r1=349417&r2=349418&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/FileCheck/FileCheck.cpp?rev=349418&r1=349417&r2=349418&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/utils/FileCheck/FileCheck.cpp (original)<br>
+++ llvm/trunk/utils/FileCheck/FileCheck.cpp Mon Dec 17 16:01:39 2018<br>
@@ -19,12 +19,13 @@<br>
#include "llvm/Support/CommandLine.h"<br>
#include "llvm/Support/InitLLVM.h"<br>
#include "llvm/Support/Process.h"<br>
+#include "llvm/Support/WithColor.h"<br>
#include "llvm/Support/raw_ostream.h"<br>
#include "llvm/Support/FileCheck.h"<br>
using namespace llvm;<br>
<br>
static cl::opt<std::string><br>
- CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Required);<br>
+ CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);<br>
<br>
static cl::opt<std::string><br>
InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),<br>
@@ -91,7 +92,27 @@ static cl::opt<bool> DumpInputOnFailure(<br>
"dump-input-on-failure", cl::init(std::getenv(DumpInputEnv)),<br>
cl::desc("Dump original input to stderr before failing.\n"<br>
"The value can be also controlled using\n"<br>
- "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n"));<br>
+ "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n"<br>
+ "This option is deprecated in favor of -dump-input=fail.\n"));<br>
+<br>
+enum DumpInputValue {<br>
+ DumpInputDefault,<br>
+ DumpInputHelp,<br>
+ DumpInputNever,<br>
+ DumpInputFail,<br>
+ DumpInputAlways<br>
+};<br>
+<br>
+static cl::opt<DumpInputValue> DumpInput(<br>
+ "dump-input", cl::init(DumpInputDefault),<br>
+ cl::desc("Dump input to stderr, adding annotations representing\n"<br>
+ " currently enabled diagnostics\n"),<br>
+ cl::value_desc("mode"),<br>
+ cl::values(clEnumValN(DumpInputHelp, "help",<br>
+ "Explain dump format and quit"),<br>
+ clEnumValN(DumpInputNever, "never", "Never dump input"),<br>
+ clEnumValN(DumpInputFail, "fail", "Dump input on failure"),<br>
+ clEnumValN(DumpInputAlways, "always", "Always dump input")));<br>
<br>
typedef cl::list<std::string>::const_iterator prefix_iterator;<br>
<br>
@@ -108,6 +129,269 @@ static void DumpCommandLine(int argc, ch<br>
errs() << "\n";<br>
}<br>
<br>
+struct MarkerStyle {<br>
+ /// The starting char (before tildes) for marking the line.<br>
+ char Lead;<br>
+ /// What color to use for this annotation.<br>
+ raw_ostream::Colors Color;<br>
+ /// A note to follow the marker, or empty string if none.<br>
+ std::string Note;<br>
+ MarkerStyle() {}<br>
+ MarkerStyle(char Lead, raw_ostream::Colors Color, const std::string &Note)<br>
+ : Lead(Lead), Color(Color), Note(Note) {}<br>
+};<br>
+<br>
+static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {<br>
+ switch (MatchTy) {<br>
+ case FileCheckDiag::MatchNoneButExpected:<br>
+ return MarkerStyle('X', raw_ostream::RED, "error: no match found");<br>
+ case FileCheckDiag::MatchTypeCount:<br>
+ llvm_unreachable_internal("unexpected match type");<br>
+ }<br>
+ llvm_unreachable_internal("unexpected match type");<br>
+}<br>
+<br>
+static void DumpInputAnnotationHelp(raw_ostream &OS) {<br>
+ OS << "The following description was requested by -dump-input=help to\n"<br>
+ << "explain the input annotations printed by -dump-input=always and\n"<br>
+ << "-dump-input=fail:\n\n";<br>
+<br>
+ // Labels for input lines.<br>
+ OS << " - ";<br>
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";<br>
+ OS << " labels line number L of the input file\n";<br>
+<br>
+ // Labels for annotation lines.<br>
+ OS << " - ";<br>
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";<br>
+ OS << " labels the match result for a pattern of type T from "<br>
+ << "line L of\n"<br>
+ << " the check file\n";<br>
+<br>
+ // Markers on annotation lines.<br>
+ OS << " - ";<br>
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";<br>
+ OS << " marks search range when no match is found\n";<br>
+<br>
+ // Colors.<br>
+ OS << " - colors ";<br>
+ WithColor(OS, raw_ostream::RED, true) << "error";<br>
+ OS << "\n\n"<br>
+ << "If you are not seeing color above or in input dumps, try: -color\n";<br>
+}<br>
+<br>
+/// An annotation for a single input line.<br>
+struct InputAnnotation {<br>
+ /// The check file line (one-origin indexing) where the directive that<br>
+ /// produced this annotation is located.<br>
+ unsigned CheckLine;<br>
+ /// The label for this annotation.<br>
+ std::string Label;<br>
+ /// What input line (one-origin indexing) this annotation marks. This might<br>
+ /// be different from the starting line of the original diagnostic if this is<br>
+ /// a non-initial fragment of a diagnostic that has been broken across<br>
+ /// multiple lines.<br>
+ unsigned InputLine;<br>
+ /// The column range (one-origin indexing, open end) in which to to mark the<br>
+ /// input line. If InputEndCol is UINT_MAX, treat it as the last column<br>
+ /// before the newline.<br>
+ unsigned InputStartCol, InputEndCol;<br>
+ /// The marker to use.<br>
+ MarkerStyle Marker;<br>
+};<br>
+<br>
+/// Get an abbreviation for the check type.<br>
+std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {<br>
+ switch (Ty) {<br>
+ case Check::CheckPlain:<br>
+ if (Ty.getCount() > 1)<br>
+ return "count";<br>
+ return "check";<br>
+ case Check::CheckNext:<br>
+ return "next";<br>
+ case Check::CheckSame:<br>
+ return "same";<br>
+ case Check::CheckNot:<br>
+ return "not";<br>
+ case Check::CheckDAG:<br>
+ return "dag";<br>
+ case Check::CheckLabel:<br>
+ return "label";<br>
+ case Check::CheckEmpty:<br>
+ return "empty";<br>
+ case Check::CheckEOF:<br>
+ return "eof";<br>
+ case Check::CheckBadNot:<br>
+ return "bad-not";<br>
+ case Check::CheckBadCount:<br>
+ return "bad-count";<br>
+ case Check::CheckNone:<br>
+ llvm_unreachable("invalid FileCheckType");<br>
+ }<br>
+ llvm_unreachable("unknown FileCheckType");<br>
+}<br>
+<br>
+static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,<br>
+ std::vector<InputAnnotation> &Annotations,<br>
+ unsigned &LabelWidth) {<br>
+ // What's the widest label?<br>
+ LabelWidth = 0;<br>
+ for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;<br>
+ ++DiagItr) {<br>
+ InputAnnotation A;<br>
+<br>
+ // Build label, which uniquely identifies this check result.<br>
+ A.CheckLine = DiagItr->CheckLine;<br>
+ llvm::raw_string_ostream Label(A.Label);<br>
+ Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"<br>
+ << DiagItr->CheckLine;<br>
+ Label.flush();<br>
+ LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());<br>
+<br>
+ MarkerStyle Marker = GetMarker(DiagItr->MatchTy);<br>
+ A.Marker = Marker;<br>
+<br>
+ // Compute the mark location, and break annotation into multiple<br>
+ // annotations if it spans multiple lines.<br>
+ A.InputLine = DiagItr->InputStartLine;<br>
+ A.InputStartCol = DiagItr->InputStartCol;<br>
+ if (DiagItr->InputStartLine == DiagItr->InputEndLine) {<br>
+ // Sometimes ranges are empty in order to indicate a specific point, but<br>
+ // that would mean nothing would be marked, so adjust the range to<br>
+ // include the following character.<br>
+ A.InputEndCol =<br>
+ std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);<br>
+ Annotations.push_back(A);<br>
+ } else {<br>
+ assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&<br>
+ "expected input range not to be inverted");<br>
+ A.InputEndCol = UINT_MAX;<br>
+ A.Marker.Note = "";<br>
+ Annotations.push_back(A);<br>
+ for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;<br>
+ L <= E; ++L) {<br>
+ // If a range ends before the first column on a line, then it has no<br>
+ // characters on that line, so there's nothing to render.<br>
+ if (DiagItr->InputEndCol == 1 && L == E) {<br>
+ Annotations.back().Marker.Note = Marker.Note;<br>
+ break;<br>
+ }<br>
+ InputAnnotation B;<br>
+ B.CheckLine = A.CheckLine;<br>
+ B.Label = A.Label;<br>
+ B.InputLine = L;<br>
+ B.Marker = Marker;<br>
+ B.Marker.Lead = '~';<br>
+ B.InputStartCol = 1;<br>
+ if (L != E) {<br>
+ B.InputEndCol = UINT_MAX;<br>
+ B.Marker.Note = "";<br>
+ } else<br>
+ B.InputEndCol = DiagItr->InputEndCol;<br>
+ Annotations.push_back(B);<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+static void DumpAnnotatedInput(<br>
+ raw_ostream &OS, StringRef InputFileText,<br>
+ std::vector<InputAnnotation> &Annotations, unsigned LabelWidth) {<br>
+ OS << "Full input was:\n<<<<<<\n";<br>
+<br>
+ // Sort annotations.<br>
+ //<br>
+ // First, sort in the order of input lines to make it easier to find relevant<br>
+ // annotations while iterating input lines in the implementation below.<br>
+ // FileCheck diagnostics are not always reported and recorded in the order of<br>
+ // input lines due to, for example, CHECK-DAG and CHECK-NOT.<br>
+ //<br>
+ // Second, for annotations for the same input line, sort in the order of the<br>
+ // FileCheck directive's line in the check file (where there's at most one<br>
+ // directive per line). The rationale of this choice is that, for any input<br>
+ // line, this sort establishes a total order of annotations that, with<br>
+ // respect to match results, is consistent across multiple lines, thus<br>
+ // making match results easier to track from one line to the next when they<br>
+ // span multiple lines.<br>
+ std::sort(Annotations.begin(), Annotations.end(),<br>
+ [](const InputAnnotation &A, const InputAnnotation &B) {<br>
+ if (A.InputLine != B.InputLine)<br>
+ return A.InputLine < B.InputLine;<br>
+ return A.CheckLine < B.CheckLine;<br>
+ });<br>
+<br>
+ // Compute the width of the label column.<br>
+ const unsigned char *InputFilePtr = InputFileText.bytes_begin(),<br>
+ *InputFileEnd = InputFileText.bytes_end();<br>
+ unsigned LineCount = InputFileText.count('\n');<br>
+ if (InputFileEnd[-1] != '\n')<br>
+ ++LineCount;<br>
+ unsigned LineNoWidth = log10(LineCount) + 1;<br>
+ // +3 below adds spaces (1) to the left of the (right-aligned) line numbers<br>
+ // on input lines and (2) to the right of the (left-aligned) labels on<br>
+ // annotation lines so that input lines and annotation lines are more<br>
+ // visually distinct. For example, the spaces on the annotation lines ensure<br>
+ // that input line numbers and check directive line numbers never align<br>
+ // horizontally. Those line numbers might not even be for the same file.<br>
+ // One space would be enough to achieve that, but more makes it even easier<br>
+ // to see.<br>
+ LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;<br>
+<br>
+ // Print annotated input lines.<br>
+ auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();<br>
+ for (unsigned Line = 1;<br>
+ InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;<br>
+ ++Line) {<br>
+ const unsigned char *InputFileLine = InputFilePtr;<br>
+<br>
+ // Print right-aligned line number.<br>
+ WithColor(OS, raw_ostream::BLACK, true)<br>
+ << format_decimal(Line, LabelWidth) << ": ";<br>
+<br>
+ // Print numbered line.<br>
+ bool Newline = false;<br>
+ while (InputFilePtr != InputFileEnd && !Newline) {<br>
+ if (*InputFilePtr == '\n')<br>
+ Newline = true;<br>
+ else<br>
+ OS << *InputFilePtr;<br>
+ ++InputFilePtr;<br>
+ }<br>
+ OS << '\n';<br>
+ unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline;<br>
+<br>
+ // Print any annotations.<br>
+ while (AnnotationItr != AnnotationEnd &&<br>
+ AnnotationItr->InputLine == Line) {<br>
+ WithColor COS(OS, AnnotationItr->Marker.Color, true);<br>
+ // The two spaces below are where the ": " appears on input lines.<br>
+ COS << left_justify(AnnotationItr->Label, LabelWidth) << " ";<br>
+ unsigned Col;<br>
+ for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)<br>
+ COS << ' ';<br>
+ COS << AnnotationItr->Marker.Lead;<br>
+ // If InputEndCol=UINT_MAX, stop at InputLineWidth.<br>
+ for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;<br>
+ ++Col)<br>
+ COS << '~';<br>
+ const std::string &Note = AnnotationItr->Marker.Note;<br>
+ if (!Note.empty()) {<br>
+ // Put the note at the end of the input line. If we were to instead<br>
+ // put the note right after the marker, subsequent annotations for the<br>
+ // same input line might appear to mark this note instead of the input<br>
+ // line.<br>
+ for (; Col <= InputLineWidth; ++Col)<br>
+ COS << ' ';<br>
+ COS << ' ' << Note;<br>
+ }<br>
+ COS << '\n';<br>
+ ++AnnotationItr;<br>
+ }<br>
+ }<br>
+<br>
+ OS << ">>>>>>\n";<br>
+}<br>
+<br>
int main(int argc, char **argv) {<br>
// Enable use of ANSI color codes because FileCheck is using them to<br>
// highlight text.<br>
@@ -116,6 +400,14 @@ int main(int argc, char **argv) {<br>
InitLLVM X(argc, argv);<br>
cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,<br>
"FILECHECK_OPTS");<br>
+ if (DumpInput == DumpInputHelp) {<br>
+ DumpInputAnnotationHelp(outs());<br>
+ return 0;<br>
+ }<br>
+ if (CheckFilename.empty()) {<br>
+ errs() << "<check-file> not specified\n";<br>
+ return 2;<br>
+ }<br>
<br>
FileCheckRequest Req;<br>
for (auto Prefix : CheckPrefixes)<br>
@@ -157,7 +449,6 @@ int main(int argc, char **argv) {<br>
return 2;<br>
}<br>
<br>
-<br>
SourceMgr SM;<br>
<br>
// Read the expected strings from the check file.<br>
@@ -204,10 +495,29 @@ int main(int argc, char **argv) {<br>
InputFileText, InputFile.getBufferIdentifier()),<br>
SMLoc());<br>
<br>
- int ExitCode =<br>
- FC.CheckInput(SM, InputFileText, CheckStrings) ? EXIT_SUCCESS : 1;<br>
- if (ExitCode == 1 && DumpInputOnFailure)<br>
- errs() << "Full input was:\n<<<<<<\n" << InputFileText << "\n>>>>>>\n";<br>
+ if (DumpInput == DumpInputDefault)<br>
+ DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever;<br>
+<br>
+ std::vector<FileCheckDiag> Diags;<br>
+ int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings,<br>
+ DumpInput == DumpInputNever ? nullptr : &Diags)<br>
+ ? EXIT_SUCCESS<br>
+ : 1;<br>
+ if (DumpInput == DumpInputAlways ||<br>
+ (ExitCode == 1 && DumpInput == DumpInputFail)) {<br>
+ errs() << "\n"<br>
+ << "Input file: "<br>
+ << (InputFilename == "-" ? "<stdin>" : InputFilename.getValue())<br>
+ << "\n"<br>
+ << "Check file: " << CheckFilename << "\n"<br>
+ << "\n"<br>
+ << "-dump-input=help describes the format of the following dump.\n"<br>
+ << "\n";<br>
+ std::vector<InputAnnotation> Annotations;<br>
+ unsigned LabelWidth;<br>
+ BuildInputAnnotations(Diags, Annotations, LabelWidth);<br>
+ DumpAnnotatedInput(errs(), InputFileText, Annotations, LabelWidth);<br>
+ }<br>
<br>
return ExitCode;<br>
}<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div>