[llvm] r349418 - [FileCheck] Annotate input dump (1/7)

David Blaikie via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 28 22:17:40 PST 2018


Hey there - a bunch of these commit messages came through as N/7, all
otherwise containing an identical description. Could you reply to the
commit emails with specifics about what was changed in each commit - it'd
be helpful for future archaeology (ideally the specific patch descriptions
would be in the specific commits, but having the info on the mailing list
at least will help some folks).

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/20181229/f34d45f0/attachment.html>


More information about the llvm-commits mailing list