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