[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