Index: llvm/docs/CommandGuide/FileCheck.rst
===================================================================
--- llvm/docs/CommandGuide/FileCheck.rst
+++ llvm/docs/CommandGuide/FileCheck.rst
@@ -77,9 +77,14 @@
   -verify``. With this option FileCheck will verify that input does not contain
   warnings not covered by any ``CHECK:`` patterns.
 
+.. option:: --dump-input <mode>
+
+  Dump annotated original input either 'always', on 'fail', or 'never'.
+
 .. 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`.
 
 .. option:: --enable-var-scope
 
Index: llvm/include/llvm/Support/FileCheck.h
===================================================================
--- llvm/include/llvm/Support/FileCheck.h
+++ llvm/include/llvm/Support/FileCheck.h
@@ -18,6 +18,7 @@
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SourceMgr.h"
+#include <list>
 #include <vector>
 #include <map>
 
@@ -62,6 +63,8 @@
 };
 }
 
+struct FileCheckDiag;
+
 class FileCheckPattern {
   SMLoc PatternLoc;
 
@@ -105,13 +108,15 @@
                          const StringMap<StringRef> &VariableTable,
                          SMRange MatchRange = None) const;
   void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
-                       const StringMap<StringRef> &VariableTable) const;
+                       const StringMap<StringRef> &VariableTable,
+                       std::list<FileCheckDiag> *Diags) const;
 
   bool hasVariable() const {
     return !(VariableUses.empty() && VariableDefs.empty());
   }
 
   Check::FileCheckType getCheckTy() const { return CheckTy; }
+  unsigned getLineNumber() const { return LineNumber; }
 
 private:
   bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
@@ -123,6 +128,43 @@
   size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
 };
 
+//===----------------------------------------------------------------------===//
+/// 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?
+  ///
+  /// There might be more than one of these for the same directive.  For
+  /// example, there might be several discards before either a final or fail,
+  /// and there might be a fuzzy match after a fail.
+  ///
+  /// We iterate these types, so they must have contiguous values in
+  /// [0, MatchTypeCount).  Moreover, keep match types together if they use the
+  /// same mark in annotated input dumps or else printing of the annotation key
+  /// will malfunction.
+  enum MatchType {
+    MatchFinalAndExpected, //< the final match for an expected pattern
+    MatchTypeFirst = MatchFinalAndExpected,
+    MatchFinalButExcluded, //< the final match for an excluded pattern
+    MatchFinalButIllegal,  //< the final but illegal match for an expected pattern
+    MatchDiscard,          //< a discarded match for an expected pattern
+    MatchNoneAndExcluded,  //< no match for an excluded pattern
+    MatchNoneButExpected,  //< no match for an expected pattern
+    MatchFuzzy,            //< a fuzzy match (because no perfect match)
+    MatchTypeCount,
+  } MatchTy;
+  /// The match range if MatchTy is not MatchNoneAndExcluded or
+  /// MatchNoneButExpected, or the search range otherwise.
+  unsigned InputStartLine, InputStartCol, InputEndLine, InputEndCol;
+  FileCheckDiag(const SourceMgr &SM, const Check::FileCheckType &CheckTy,
+                SMLoc CheckLoc, MatchType MatchTy, SMRange InputRange);
+};
+
 //===----------------------------------------------------------------------===//
 // Check Strings.
 //===----------------------------------------------------------------------===//
@@ -147,18 +189,20 @@
 
   size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
                size_t &MatchLen, StringMap<StringRef> &VariableTable,
-               FileCheckRequest &Req) const;
+               FileCheckRequest &Req, std::list<FileCheckDiag> *Diags) const;
 
   bool CheckNext(const SourceMgr &SM, StringRef Buffer) const;
   bool CheckSame(const SourceMgr &SM, StringRef Buffer) const;
   bool CheckNot(const SourceMgr &SM, StringRef Buffer,
                 const std::vector<const FileCheckPattern *> &NotStrings,
                 StringMap<StringRef> &VariableTable,
-                const FileCheckRequest &Req) const;
+                const FileCheckRequest &Req,
+                std::list<FileCheckDiag> *Diags) const;
   size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
                   std::vector<const FileCheckPattern *> &NotStrings,
                   StringMap<StringRef> &VariableTable,
-                  const FileCheckRequest &Req) const;
+                  const FileCheckRequest &Req,
+                  std::list<FileCheckDiag> *Diags) const;
 };
 
 /// FileCheck class takes the request and exposes various methods that
@@ -195,7 +239,8 @@
   ///
   /// Returns false if the input fails to satisfy the checks.
   bool CheckInput(SourceMgr &SM, StringRef Buffer,
-                  ArrayRef<FileCheckString> CheckStrings);
+                  ArrayRef<FileCheckString> CheckStrings,
+                  std::list<FileCheckDiag> *Diags = nullptr);
 };
 } // namespace llvm
 #endif
Index: llvm/include/llvm/Support/WithColor.h
===================================================================
--- llvm/include/llvm/Support/WithColor.h
+++ llvm/include/llvm/Support/WithColor.h
@@ -36,10 +36,11 @@
 /// color.
 class WithColor {
   raw_ostream &OS;
-  /// Determine whether colors should be displayed.
-  bool colorsEnabled(raw_ostream &OS);
 
 public:
+  /// Determine whether colors should be displayed.
+  static bool colorsEnabled(raw_ostream &OS);
+
   /// To be used like this: WithColor(OS, HighlightColor::String) << "text";
   WithColor(raw_ostream &OS, HighlightColor S);
   ~WithColor();
Index: llvm/lib/Support/FileCheck.cpp
===================================================================
--- llvm/lib/Support/FileCheck.cpp
+++ llvm/lib/Support/FileCheck.cpp
@@ -410,7 +410,8 @@
 
 void FileCheckPattern::PrintFuzzyMatch(
     const SourceMgr &SM, StringRef Buffer,
-    const StringMap<StringRef> &VariableTable) const {
+    const StringMap<StringRef> &VariableTable,
+    std::list<FileCheckDiag> *Diags) const {
   // Attempt to find the closest/best fuzzy match.  Usually an error happens
   // because some string in the output didn't exactly match. In these cases, we
   // would like to show the user a best guess at what "should have" matched, to
@@ -444,8 +445,13 @@
   // reasonable and not equal to what we showed in the "scanning from here"
   // line.
   if (Best && Best != StringRef::npos && BestQuality < 50) {
-    SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + Best),
-                    SourceMgr::DK_Note, "possible intended match here");
+    SMLoc MatchStart = SMLoc::getFromPointer(Buffer.data() + Best);
+    SMRange MatchRange(MatchStart, MatchStart);
+    SM.PrintMessage(MatchStart, SourceMgr::DK_Note,
+                    "possible intended match here");
+    if (Diags)
+      Diags->emplace_back(SM, getCheckTy(), getLoc(),
+                          FileCheckDiag::MatchFuzzy, MatchRange);
 
     // FIXME: If we wanted to be really friendly we would show why the match
     // failed, as it can be hard to spot simple one character differences.
@@ -527,6 +533,22 @@
   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 == '_');
 }
@@ -843,42 +865,65 @@
   return false;
 }
 
+static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
+                                  const SourceMgr &SM, SMLoc Loc,
+                                  Check::FileCheckType CheckTy,
+                                  StringRef Buffer, size_t Pos, size_t Len,
+                                  std::list<FileCheckDiag> *Diags,
+                                  bool AdjustPrevDiag = false) {
+  SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);
+  SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);
+  SMRange Range(Start, End);
+  if (Diags) {
+    if (AdjustPrevDiag)
+      Diags->rbegin()->MatchTy = MatchTy;
+    else
+      Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);
+  }
+  return Range;
+}
+
 static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
                        StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
                        StringRef Buffer, StringMap<StringRef> &VariableTable,
                        size_t MatchPos, size_t MatchLen,
-                       const FileCheckRequest &Req) {
+                       const FileCheckRequest &Req,
+                       std::list<FileCheckDiag> *Diags) {
   if (ExpectedMatch) {
     if (!Req.Verbose)
       return;
     if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
       return;
   }
-  SMLoc MatchStart = SMLoc::getFromPointer(Buffer.data() + MatchPos);
-  SMLoc MatchEnd = SMLoc::getFromPointer(Buffer.data() + MatchPos + MatchLen);
-  SMRange MatchRange(MatchStart, MatchEnd);
+  SMRange MatchRange = ProcessMatchResult(
+      ExpectedMatch ? FileCheckDiag::MatchFinalAndExpected
+                    : FileCheckDiag::MatchFinalButExcluded,
+      SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags);
   SM.PrintMessage(
       Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error,
       CheckTypeName(Prefix, Pat.getCheckTy()) + ": " +
           (ExpectedMatch ? "expected" : "excluded") +
           " string found in input");
-  SM.PrintMessage(MatchStart, SourceMgr::DK_Note, "found here", {MatchRange});
+  SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
+                  {MatchRange});
   Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange);
 }
 
 static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
                        const FileCheckString &CheckStr, StringRef Buffer,
                        StringMap<StringRef> &VariableTable, size_t MatchPos,
-                       size_t MatchLen, FileCheckRequest &Req) {
+                       size_t MatchLen, FileCheckRequest &Req,
+                       std::list<FileCheckDiag> *Diags) {
   PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
-             Buffer, VariableTable, MatchPos, MatchLen, Req);
+             Buffer, VariableTable, MatchPos, MatchLen, Req, Diags);
 }
 
 static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
-                         StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
-                         StringRef Buffer,
+                         StringRef Prefix, SMLoc Loc,
+                         const FileCheckPattern &Pat, StringRef Buffer,
                          StringMap<StringRef> &VariableTable,
-                         bool VerboseVerbose) {
+                         bool VerboseVerbose,
+                         std::list<FileCheckDiag> *Diags) {
   if (!ExpectedMatch && !VerboseVerbose)
     return;
 
@@ -892,22 +937,26 @@
   // 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::MatchNoneAndExcluded,
+      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);
+
   if (ExpectedMatch)
-    Pat.PrintFuzzyMatch(SM, Buffer, VariableTable);
+    Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags);
 }
 
 static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
                          const FileCheckString &CheckStr, StringRef Buffer,
                          StringMap<StringRef> &VariableTable,
-                         bool VerboseVerbose) {
+                         bool VerboseVerbose,
+                         std::list<FileCheckDiag> *Diags) {
   PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
-               Buffer, VariableTable, VerboseVerbose);
+               Buffer, VariableTable, VerboseVerbose, Diags);
 }
 
 /// Count the number of newlines in the specified range.
@@ -935,9 +984,10 @@
 
 /// 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::list<FileCheckDiag> *Diags) const {
   size_t LastPos = 0;
   std::vector<const FileCheckPattern *> NotStrings;
 
@@ -947,7 +997,7 @@
   // 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;
   }
@@ -956,10 +1006,12 @@
   StringRef MatchBuffer = Buffer.substr(LastPos);
   size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
   if (MatchPos == StringRef::npos) {
-    PrintNoMatch(true, SM, *this, MatchBuffer, VariableTable, Req.VerboseVerbose);
+    PrintNoMatch(true, SM, *this, MatchBuffer, VariableTable,
+                 Req.VerboseVerbose, Diags);
     return StringRef::npos;
   }
-  PrintMatch(true, SM, *this, MatchBuffer, VariableTable, MatchPos, MatchLen, Req);
+  PrintMatch(true, SM, *this, MatchBuffer, VariableTable, MatchPos, MatchLen,
+             Req, Diags);
 
   // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
   // or CHECK-NOT
@@ -968,17 +1020,25 @@
 
     // If this check is a "CHECK-NEXT", verify that the previous match was on
     // the previous line (i.e. that there is one newline between them).
-    if (CheckNext(SM, SkippedRegion))
+    if (CheckNext(SM, SkippedRegion)) {
+      ProcessMatchResult(FileCheckDiag::MatchFinalButIllegal, SM, Loc,
+                         Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
+                         Diags, Req.Verbose);
       return StringRef::npos;
+    }
 
     // If this check is a "CHECK-SAME", verify that the previous match was on
     // the same line (i.e. that there is no newline between them).
-    if (CheckSame(SM, SkippedRegion))
+    if (CheckSame(SM, SkippedRegion)) {
+      ProcessMatchResult(FileCheckDiag::MatchFinalButIllegal, SM, Loc,
+                         Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
+                         Diags, Req.Verbose);
       return StringRef::npos;
+    }
 
     // If this match had "not strings", verify that they don't exist in the
     // skipped region.
-    if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req))
+    if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags))
       return StringRef::npos;
   }
 
@@ -1061,10 +1121,11 @@
 }
 
 /// Verify there's no "not strings" in the given buffer.
-bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
-                           const std::vector<const FileCheckPattern *> &NotStrings,
-                           StringMap<StringRef> &VariableTable,
-                           const FileCheckRequest &Req) const {
+bool FileCheckString::CheckNot(
+    const SourceMgr &SM, StringRef Buffer,
+    const std::vector<const FileCheckPattern *> &NotStrings,
+    StringMap<StringRef> &VariableTable, const FileCheckRequest &Req,
+    std::list<FileCheckDiag> *Diags) const {
   for (const FileCheckPattern *Pat : NotStrings) {
     assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
 
@@ -1073,12 +1134,12 @@
 
     if (Pos == StringRef::npos) {
       PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer,
-                   VariableTable, Req.VerboseVerbose);
+                   VariableTable, Req.VerboseVerbose, Diags);
       continue;
     }
 
     PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer, VariableTable,
-               Pos, MatchLen, Req);
+               Pos, MatchLen, Req, Diags);
 
     return true;
   }
@@ -1087,10 +1148,12 @@
 }
 
 /// 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::list<FileCheckDiag> *Diags) const {
   if (DagNotStrings.empty())
     return 0;
 
@@ -1134,14 +1197,14 @@
       // that group of CHECK-DAGs fails immediately.
       if (MatchPosBuf == StringRef::npos) {
         PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 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.
       MatchPos += MatchPosBuf;
       if (Req.VerboseVerbose)
         PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
-                   MatchPos, MatchLen, Req);
+                   MatchPos, MatchLen, Req, Diags);
       MatchRange M{MatchPos, MatchPos + MatchLen};
       if (Req.AllowDeprecatedDagOverlap) {
         // We don't need to track all matches in this mode, so we just maintain
@@ -1178,12 +1241,14 @@
         SM.PrintMessage(OldStart, SourceMgr::DK_Note,
                         "match discarded, overlaps earlier DAG match here",
                         {OldRange});
+        if (Diags)
+          Diags->rbegin()->MatchTy = FileCheckDiag::MatchDiscard;
       }
       MatchPos = MI->End;
     }
     if (!Req.VerboseVerbose)
       PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
-                 MatchPos, MatchLen, Req);
+                 MatchPos, MatchLen, Req, Diags);
 
     // Handle the end of a CHECK-DAG group.
     if (std::next(PatItr) == PatEnd ||
@@ -1194,7 +1259,7 @@
         // region.
         StringRef SkippedRegion =
             Buffer.slice(StartPos, MatchRanges.begin()->Pos);
-        if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req))
+        if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags))
           return StringRef::npos;
         // Clear "not strings".
         NotStrings.clear();
@@ -1275,7 +1340,8 @@
 ///
 /// 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::list<FileCheckDiag> *Diags) {
   bool ChecksFailed = false;
 
   /// VariableTable - This holds all the current filecheck variables.
@@ -1298,9 +1364,8 @@
 
       // 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;
@@ -1319,8 +1384,8 @@
       // 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;
Index: llvm/test/FileCheck/dump-input-annotations.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input-annotations.txt
@@ -0,0 +1,353 @@
+;--------------------------------------------------
+; 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:1     ^~~~~
+; ALIGN-NEXT:check:2           X~~~~
+; ALIGN-NEXT:         2: goodbye
+; ALIGN-NEXT:check:2     ~~~~~~~
+; ALIGN-NEXT:         3: world
+; ALIGN-NEXT:check:2     ~~~~~
+; ALIGN-NEXT:>>>>>>
+; ALIGN-NOT:{{.}}
+
+;--------------------------------------------------
+; CHECK (also: multi-line search range, fuzzy match)
+;--------------------------------------------------
+
+; 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-V-NEXT: check:1       ^~~~~
+; CHK-NEXT:              2: again
+; CHK-NEXT:   check:2'0     X~~~~
+; CHK-NEXT:              3: whirled
+; CHK-NEXT:   check:2'0     ~~~~~~~
+; CHK-NEXT:   check:2'1     ?
+; CHK-NEXT:   >>>>>>
+; CHK-NOT:    {{.}}
+
+;--------------------------------------------------
+; CHECK-NEXT (also: EOF search-range, illegal match)
+;--------------------------------------------------
+
+; 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-V-NEXT: check:1     ^~~~~
+; NXT-NEXT:            2: again
+; NXT-V-NEXT: next:2      ^~~~~
+; NXT-NEXT:            3:
+; NXT-NEXT:   next:3      X
+; NXT-NEXT:   >>>>>>
+; NXT-NOT:    {{.}}
+
+; Illegal match.
+
+; RUN: echo 'yonder' >> %t.in
+; RUN: echo 'world' >> %t.in
+
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefix=NXT2
+
+; NXT2:      <<<<<<
+; NXT2-NEXT:         1: hello
+; NXT2-NEXT:         2: again
+; NXT2-NEXT:         3: yonder
+; NXT2-NEXT:         4: world
+; NXT2-NEXT: next:3     !~~~~
+; NXT2-NEXT: >>>>>>
+; NXT2-NOT:  {{.}}
+
+;--------------------------------------------------
+; CHECK-SAME (also: multiple annotations per line, single-char search range,
+; illegal match)
+;--------------------------------------------------
+
+; 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-V-NEXT: check:1     ^~~~~
+; SAM-V-NEXT: same:2            ^~~~~
+; SAM-NEXT:   same:3                 X
+; SAM-NEXT:   >>>>>>
+; SAM-NOT:    {{.}}
+
+; Illegal match.
+
+; RUN: echo 'again' >> %t.in
+
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=SAM2
+
+; SAM2:      <<<<<<
+; SAM2-NEXT:          1: hello world!
+; SAM2-NEXT: check:1     ^~~~~
+; SAM2-NEXT: same:2            ^~~~~
+; SAM2-NEXT:          2: again
+; SAM2-NEXT: same:3      !~~~~
+; SAM2-NEXT: >>>>>>
+; SAM2-NOT:  {{.}}
+
+;--------------------------------------------------
+; CHECK-EMPTY (also: search range ends at label, single-char match, illegal
+; match)
+;--------------------------------------------------
+
+; 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-V-NEXT: check:1     ^~~~~
+; EMP-NEXT:            2:
+; EMP-V-NEXT: empty:2     ^
+; EMP-NEXT:            3: world
+; EMP-NEXT:   empty:3     X~~~~
+; EMP-NEXT:            4: label
+; EMP-NEXT:   empty:3     ~~~~~
+; EMP-V-NEXT: label:4     ^~~~~
+; EMP-NEXT:   >>>>>>
+; EMP-NOT:    {{.}}
+
+; Illegal match.
+
+; RUN: echo 'hello' > %t.in
+; RUN: echo 'world' >> %t.in
+
+; RUN: echo 'CHECK: hello' > %t.chk
+; RUN: echo 'CHECK-EMPTY:' >> %t.chk
+
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefix=EMP2
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=EMP2,EMP2-V
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -vv 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=EMP2,EMP2-V,EMP2-VV
+
+; EMP2:        <<<<<<
+; EMP2-NEXT:            1: hello
+; EMP2-V-NEXT: check:1     ^~~~~
+; EMP2-NEXT:            2: world
+; EMP2-NEXT:            3:
+; EMP2-NEXT:   empty:2     !
+; EMP2-NEXT:   >>>>>>
+; EMP2-NOT:    {{.}}
+
+;--------------------------------------------------
+; CHECK-NOT (also: EOF pattern, and multiline range that ends before EOL)
+;--------------------------------------------------
+
+; No match (success) and illegal match.
+
+; RUN: echo 'hello' > %t.in
+; RUN: echo 'world' >> %t.in
+; RUN: echo 'again' >> %t.in
+
+; RUN: echo 'CHECK-NOT: goodbye' > %t.chk
+; RUN: echo 'CHECK-NOT: 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=NOT
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NOT,NOT-V
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -vv 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NOT,NOT-V,NOT-VV
+
+; NOT:         <<<<<<
+; NOT-NEXT:           1: hello
+; NOT-VV-NEXT: not:1     X~~~~
+; NOT-NEXT:           2: world
+; NOT-VV-NEXT: not:1     ~~~~~
+; NOT-NEXT:    not:2     !~~~~
+; NOT-NEXT:           3: again
+; NOT-VV-NEXT: not:1     ~~~~~
+; NOT-VV-NEXT:        4:
+; NOT-VV-NEXT: eof:2     ^
+; NOT-NEXT:    >>>>>>
+; NOT-NOT:     {{.}}
+
+; Again, but with a CHECK instead of EOF as search range end.
+
+; RUN: echo 'CHECK: ain' >> %t.chk
+
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefix=NOT2
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NOT2,NOT2-V
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -vv 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=NOT2,NOT2-V,NOT2-VV
+
+; NOT2:         <<<<<<
+; NOT2-NEXT:             1: hello
+; NOT2-VV-NEXT: not:1       X~~~~
+; NOT2-NEXT:             2: world
+; NOT2-VV-NEXT: not:1       ~~~~~
+; NOT2-NEXT:    not:2       !~~~~
+; NOT2-NEXT:             3: again
+; NOT2-VV-NEXT: not:1       ~~
+; NOT2-V-NEXT:  check:3       ^~~
+; NOT2-NEXT:    >>>>>>
+; NOT2-NOT:     {{.}}
+
+;--------------------------------------------------
+; CHECK-DAG (also: matches in different order than directives, discarded match)
+;--------------------------------------------------
+
+; 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,DAG-Q
+; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \
+; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG,DAG-V,DAG-VQ
+; 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-V,DAG-VV
+
+; DAG:         <<<<<<
+; DAG-NEXT:             1: abc
+; DAG-V-NEXT:  dag:2       ^~~
+; DAG-VV-NEXT: dag:3'0     !~~
+; DAG-NEXT:             2: def
+; DAG-V-NEXT:  dag:1       ^~~
+; DAG-VV-NEXT: dag:4'0     !~~
+; DAG-NEXT:             3: abc
+; DAG-VQ-NEXT: dag:3       ^~~
+; DAG-VV-NEXT: dag:3'1     ^~~
+; DAG-Q-NEXT:  dag:4       X~~
+; DAG-VQ-NEXT: dag:4       X~~
+; DAG-VV-NEXT: dag:4'1     X~~
+; DAG-NEXT:    >>>>>>
+; DAG-NOT:     {{.}}
+
+;--------------------------------------------------
+; CHECK-LABEL
+;
+; FIXME: Labels sometimes produce redundant diagnostics for good matches.
+; That bug is independent of but affects -dump-input.
+;--------------------------------------------------
+
+; 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-V-NEXT:  label:1'0     ^~~~
+; LAB-V-NEXT:  label:1'1     ^~~~
+; LAB-NEXT:               2: foo
+; LAB-NEXT:    label:3'0     X~~
+; LAB-NEXT:               3: lab1
+; LAB-NEXT:    label:3'0     ~~~~
+; LAB-NEXT:    label:3'1     ?
+; LAB-NEXT:               4: bar
+; LAB-NEXT:    label:3'0     ~~~
+; LAB-NEXT:    >>>>>>
+; LAB-NOT:     {{.}}
+
+
Index: llvm/test/FileCheck/dump-input-enable.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input-enable.txt
@@ -0,0 +1,118 @@
+; 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
+
+; RUN: FILECHECK_DUMP_INPUT=foobar \
+; RUN: not FileCheck -input-file %t.good %t.check -check-prefix=CHECK \
+; RUN:               -match-full-lines 2>&1 \
+; RUN: | FileCheck %s -match-full-lines -check-prefix=BADVAL
+
+BADVAL: Unrecognized value for dump-input option: foobar
+
+;--------------------------------------------------
+; 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
+
+; RUN: FILECHECK_DUMP_INPUT=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: FILECHECK_DUMP_INPUT=never \
+; 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
+
+; RUN: FILECHECK_DUMP_INPUT=fail \
+; 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: FILECHECK_DUMP_INPUT=fail \
+; 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
+
+; RUN: FILECHECK_DUMP_INPUT=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: FILECHECK_DUMP_INPUT=always \
+; 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
+
+;--------------------------------------------------
+; env and command line conflict
+;--------------------------------------------------
+
+; RUN: FILECHECK_DUMP_INPUT=always \
+; 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
+
+; END.
+
+; CHECK-GOOD: Full input was:
+; CHECK-GOOD-NEXT: <<<<<<
+; CHECK-GOOD-NEXT:          1: ciao
+; CHECK-GOOD-NEXT: check:1     ^~~~
+; CHECK-GOOD-NEXT:          2: world
+; CHECK-GOOD-NEXT: next:2      ^~~~~
+; 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     ~~~~~
+; CHECK-ERR-NEXT: >>>>>>
+
+; CHECK-NODUMP-NOT: <<<<<<
Index: llvm/test/FileCheck/match-full-lines.txt
===================================================================
--- llvm/test/FileCheck/match-full-lines.txt
+++ llvm/test/FileCheck/match-full-lines.txt
@@ -1,6 +1,9 @@
-// RUN: not FileCheck -match-full-lines -input-file %s %s  2>&1 \
+// In case the environment has FILECHECK_DUMP_INPUT, use -dump-input=never here
+// to avoid additional output that might not match expected output.
+//
+// RUN: not FileCheck -dump-input=never -match-full-lines -input-file %s %s  2>&1 \
 // RUN:   | FileCheck --check-prefix=ERROR --implicit-check-not=error: %s
-// RUN: not FileCheck -match-full-lines -strict-whitespace -input-file %s %s  2>&1 \
+// RUN: not FileCheck -dump-input=never -match-full-lines -strict-whitespace -input-file %s %s  2>&1 \
 // RUN:   | FileCheck --check-prefix=ERROR-STRICT --check-prefix=ERROR --implicit-check-not=error: %s
 
 Label 1
Index: llvm/test/FileCheck/verbose_mode.txt
===================================================================
--- llvm/test/FileCheck/verbose_mode.txt
+++ llvm/test/FileCheck/verbose_mode.txt
@@ -1,6 +1,10 @@
+; Check FILECHECK_DUMP_INPUT_ON_FAILURE and --dump-input-on-failure, which are
+; deprecated.  Set FILECHECK_DUMP_INPUT=never in case it's set otherwise in the
+; environment.
+
 ; 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
+; RUN: env FILECHECK_DUMP_INPUT=never 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
Index: llvm/utils/FileCheck/FileCheck.cpp
===================================================================
--- llvm/utils/FileCheck/FileCheck.cpp
+++ llvm/utils/FileCheck/FileCheck.cpp
@@ -18,6 +18,7 @@
 
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/FileCheck.h"
 using namespace llvm;
@@ -90,7 +91,17 @@
     "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.\n"));
+
+static const char *DumpInputEnvValue = std::getenv("FILECHECK_DUMP_INPUT");
+
+static cl::opt<std::string> DumpInput(
+    "dump-input", cl::init(DumpInputEnvValue ? DumpInputEnvValue : "never"),
+    cl::desc("Dump annotated original input to stderr either 'always', on\n"
+             "'fail', or 'never'.  The value can be also controlled using\n"
+             "FILECHECK_DUMP_INPUT environment variable.\n"),
+    cl::value_desc("mode"));
 
 typedef cl::list<std::string>::const_iterator prefix_iterator;
 
@@ -107,6 +118,467 @@
   errs() << "\n";
 }
 
+struct MatchTypeStyle {
+  char Mark;
+  bool HasTildes;
+  raw_ostream::Colors Color;
+  enum Verbosity { Quiet, Verbose, VerboseVerbose } RequiredVerbosity;
+  const char *What;
+  MatchTypeStyle(char Mark, bool HasTildes, raw_ostream::Colors Color,
+                 Verbosity RequiredVerbosity, const char *What)
+      : Mark(Mark), HasTildes(HasTildes), Color(Color),
+        RequiredVerbosity(RequiredVerbosity), What(What) {}
+};
+
+static MatchTypeStyle GetMatchTypeStyle(unsigned MatchTy) {
+  switch (MatchTy) {
+  case FileCheckDiag::MatchFinalAndExpected:
+    return MatchTypeStyle('^', true, raw_ostream::GREEN,
+                          MatchTypeStyle::Verbose,
+                          "the final match for an expected pattern (e.g., "
+                          "CHECK)");
+  case FileCheckDiag::MatchFinalButExcluded:
+    return MatchTypeStyle('!', true, raw_ostream::RED, MatchTypeStyle::Quiet,
+                          "the final match for an excluded pattern (e.g., "
+                          "CHECK-NOT)");
+  case FileCheckDiag::MatchFinalButIllegal:
+    return MatchTypeStyle('!', true, raw_ostream::RED, MatchTypeStyle::Quiet,
+                          "the final but illegal match for an expected "
+                          "pattern (e.g., CHECK-NEXT)");
+  case FileCheckDiag::MatchDiscard:
+    return MatchTypeStyle('!', true, raw_ostream::CYAN,
+                          MatchTypeStyle::VerboseVerbose,
+                          "a discarded match for an expected pattern (e.g., "
+                          "CHECK-DAG)");
+  case FileCheckDiag::MatchNoneAndExcluded:
+    return MatchTypeStyle('X', true, raw_ostream::GREEN,
+                          MatchTypeStyle::VerboseVerbose,
+                          "the search range for an unmatched excluded "
+                          "pattern (e.g., CHECK-NOT)");
+  case FileCheckDiag::MatchNoneButExpected:
+    return MatchTypeStyle('X', true, raw_ostream::RED, MatchTypeStyle::Quiet,
+                          "the search range for an unmatched expected "
+                          "pattern (e.g., CHECK)");
+  case FileCheckDiag::MatchFuzzy:
+    return MatchTypeStyle('?', false, raw_ostream::MAGENTA,
+                          MatchTypeStyle::Quiet,
+                          "a fuzzy match start for an otherwise unmatched "
+                          "pattern");
+  case FileCheckDiag::MatchTypeCount:
+    llvm_unreachable_internal("unexpected match type");
+  }
+  llvm_unreachable_internal("unexpected match type");
+}
+
+static void SetColor(raw_ostream::Colors Color, bool Bold, bool BG = false) {
+  if (WithColor::colorsEnabled(errs()))
+    errs().changeColor(Color, Bold, BG);
+}
+
+static void DumpInputAnnotationKey(const FileCheckRequest &Req) {
+  errs() << "\nKey for input dump annotations:\n\n";
+
+  // Labels for input lines.
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "L";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << ":S    labels line number L of the input file, where S is a "
+         << "single space\n";
+
+  // Labels for annotation lines.
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "T:L";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "    labels the only match result for a pattern of "
+         << "type T from line L of\n"
+         << "           the check file\n";
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "T:L'N";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "  labels the Nth match result for a pattern of "
+         << "type T from line L of\n"
+         << "           the check file\n";
+
+  // Markers on annotation lines.
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "^~~";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "    marks good match (requires -v)\n";
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "!~~";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "    marks bad match\n";
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "X~~";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "    marks search range when no match is found\n";
+  errs() << "  - ";
+  SetColor(raw_ostream::BLACK, true);
+  errs() << "?";
+  SetColor(raw_ostream::BLACK, false);
+  errs() << "      marks fuzzy match when no match is found\n";
+
+  // Colors.
+  if (WithColor::colorsEnabled(errs())) {
+    errs() << "  - color  ";
+    if (Req.Verbose) {
+      errs().changeColor(raw_ostream::GREEN, true);
+      errs() << "success";
+      errs().changeColor(raw_ostream::BLACK, false);
+      errs() << ", ";
+    }
+    errs().changeColor(raw_ostream::RED, true);
+    errs() << "error";
+    errs().changeColor(raw_ostream::BLACK, false);
+    if (Req.Verbose) {
+      errs() << ", ";
+      errs().changeColor(raw_ostream::CYAN, true, true);
+      errs() << "unmatched";
+      errs().changeColor(raw_ostream::BLACK, false, false);
+      if (Req.VerboseVerbose) {
+        errs() << ", ";
+        errs().changeColor(raw_ostream::CYAN, true);
+        errs() << "discarded";
+        errs().changeColor(raw_ostream::BLACK, false);
+      }
+    }
+    errs() << ", ";
+    errs().changeColor(raw_ostream::MAGENTA, true);
+    errs() << "fuzzy";
+    errs().changeColor(raw_ostream::BLACK, false);
+    errs() << '\n';
+  }
+  SetColor(raw_ostream::BLACK, false);
+
+  errs() << "\nDetailed description of currently enabled markers:\n\n";
+
+  MatchTypeStyle::Verbosity Verbosity =
+      Req.VerboseVerbose
+          ? MatchTypeStyle::VerboseVerbose
+          : Req.Verbose ? MatchTypeStyle::Verbose : MatchTypeStyle::Quiet;
+  for (unsigned StyleIdx = FileCheckDiag::MatchTypeFirst;
+       StyleIdx < FileCheckDiag::MatchTypeCount; ++StyleIdx) {
+    MatchTypeStyle Style = GetMatchTypeStyle(StyleIdx);
+    if (Verbosity < Style.RequiredVerbosity)
+      continue;
+    if (StyleIdx == FileCheckDiag::MatchTypeFirst ||
+        Verbosity < GetMatchTypeStyle(StyleIdx - 1).RequiredVerbosity ||
+        Style.Mark != GetMatchTypeStyle(StyleIdx - 1).Mark ||
+        Style.HasTildes != GetMatchTypeStyle(StyleIdx - 1).HasTildes ||
+        (WithColor::colorsEnabled(errs()) &&
+         Style.Color != GetMatchTypeStyle(StyleIdx - 1).Color)) {
+      errs() << "  - ";
+      SetColor(Style.Color, true);
+      errs() << Style.Mark;
+      if (Style.HasTildes)
+        errs() << "~~";
+      SetColor(raw_ostream::BLACK, false);
+      if (!Style.HasTildes)
+        errs() << "  ";
+      errs() << "  marks ";
+      if (StyleIdx + 1 != FileCheckDiag::MatchTypeCount &&
+          Verbosity >= GetMatchTypeStyle(StyleIdx + 1).RequiredVerbosity &&
+          Style.Mark == GetMatchTypeStyle(StyleIdx + 1).Mark &&
+          Style.HasTildes == GetMatchTypeStyle(StyleIdx + 1).HasTildes &&
+          (!WithColor::colorsEnabled(errs()) ||
+           Style.Color == GetMatchTypeStyle(StyleIdx + 1).Color))
+        errs() << "either:\n"
+               << "         - ";
+    } else
+      errs() << "         - ";
+    errs() << Style.What << "\n";
+  }
+
+  if (WithColor::colorsEnabled(errs()) && Req.Verbose) {
+    errs() << "  - ";
+    errs().changeColor(raw_ostream::CYAN, true, true);
+    errs() << "input";
+    errs().changeColor(raw_ostream::BLACK, false, false);
+    errs() << "  is style of input text with no final match for any "
+           << "expected pattern\n";
+  }
+
+  if (WithColor::colorsEnabled(errs()))
+    errs().resetColor();
+}
+
+/// 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 index of the match result for this check.
+  unsigned CheckDiagIndex;
+  /// 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 starting char (before tildes) for marking the line.
+  char Mark;
+  /// Whether this annotation represents a final match for an expected pattern.
+  bool FinalAndExpectedMatch;
+  /// What color to use for this annotation.
+  raw_ostream::Colors Color;
+};
+
+/// Get a three-letter abbreviation for the check type.
+std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
+  switch (Ty) {
+  case Check::CheckPlain:
+    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::CheckNone:
+    llvm_unreachable("invalid FileCheckType");
+  }
+  llvm_unreachable("unknown FileCheckType");
+}
+
+static void BuildInputAnnotations(const std::list<FileCheckDiag> &DiagList,
+                                  std::list<InputAnnotation> &AnnotationList,
+                                  unsigned &LabelWidth) {
+  // How many diagnostics has the current check seen so far?
+  unsigned CheckDiagCount = 0;
+  // What's the widest label?
+  LabelWidth = 0;
+  for (auto DiagItr = DiagList.begin(), DiagEnd = DiagList.end();
+       DiagItr != DiagEnd; ++DiagItr) {
+    AnnotationList.emplace_back();
+    InputAnnotation &A = AnnotationList.back();
+
+    // 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;
+    A.CheckDiagIndex = UINT_MAX;
+    auto DiagNext = std::next(DiagItr);
+    if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
+        DiagItr->CheckLine == DiagNext->CheckLine)
+      A.CheckDiagIndex = CheckDiagCount++;
+    else if (CheckDiagCount) {
+      A.CheckDiagIndex = CheckDiagCount;
+      CheckDiagCount = 0;
+    }
+    if (A.CheckDiagIndex != UINT_MAX)
+      Label << "'" << A.CheckDiagIndex;
+    else
+      A.CheckDiagIndex = 0;
+    Label.flush();
+    LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
+
+    MatchTypeStyle MatchTyStyle = GetMatchTypeStyle(DiagItr->MatchTy);
+    A.Mark = MatchTyStyle.Mark;
+    A.Color = MatchTyStyle.Color;
+    A.FinalAndExpectedMatch =
+        DiagItr->MatchTy == FileCheckDiag::MatchFinalAndExpected;
+
+    // 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);
+      assert((MatchTyStyle.HasTildes ||
+              A.InputStartCol + 1 == A.InputEndCol) &&
+             "expected input range to have only one character for marker "
+             "style without tildes");
+    } else {
+      assert(MatchTyStyle.HasTildes &&
+             "expected input range to have only one character for marker "
+             "style without tildes");
+      assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
+             "expected input range not to be inverted");
+      A.InputEndCol = UINT_MAX;
+      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)
+          break;
+        AnnotationList.emplace_back();
+        InputAnnotation &B = AnnotationList.back();
+        B.CheckLine = A.CheckLine;
+        B.CheckDiagIndex = A.CheckDiagIndex;
+        B.Label = A.Label;
+        B.InputLine = L;
+        B.Mark = '~';
+        B.InputStartCol = 1;
+        if (L != E)
+          B.InputEndCol = UINT_MAX;
+        else
+          B.InputEndCol = DiagItr->InputEndCol;
+        B.FinalAndExpectedMatch = A.FinalAndExpectedMatch;
+        B.Color = A.Color;
+      }
+    }
+  }
+}
+
+static void
+DumpAnnotatedInput(const FileCheckRequest &Req, StringRef InputFileText,
+                   const std::list<InputAnnotation> &AnnotationList,
+                   unsigned LabelWidth) {
+  errs() << "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) and then by the index of the match result for that
+  // directive.  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::vector<InputAnnotation> Annotations(AnnotationList.begin(),
+                                           AnnotationList.end());
+  std::sort(Annotations.begin(), Annotations.end(),
+            [](const InputAnnotation &A, const InputAnnotation &B) {
+              if (A.InputLine != B.InputLine)
+                return A.InputLine < B.InputLine;
+              if (A.CheckLine != B.CheckLine)
+                return A.CheckLine < B.CheckLine;
+              // FIXME: Sometimes CHECK-LABEL reports its match twice with
+              // other diagnostics in between, and then diag index incrementing
+              // fails to work properly, and then this assert fails.  We should
+              // suppress one of those diagnostics or do a better job of
+              // computing this index.  For now, we just produce a redundant
+              // CHECK-LABEL annotation.
+              // assert(A.CheckDiagIndex != B.CheckDiagIndex &&
+              //        "expected diagnostic indices to be unique within a "
+              //        " check line");
+              return A.CheckDiagIndex < B.CheckDiagIndex;
+            });
+
+  // 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.
+    SetColor(raw_ostream::BLACK, true);
+    errs() << format_decimal(Line, LabelWidth) << ": ";
+    SetColor(raw_ostream::BLACK, false);
+
+    // For case where -v and colors are enabled, find the annotations for final
+    // matches for expected patterns in order to highlight everything else in
+    // the line.  There are no such annotations if -v is disabled.
+    std::list<InputAnnotation> FinalAndExpectedMatches;
+    if (Req.Verbose && WithColor::colorsEnabled(errs())) {
+      for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
+           ++I) {
+        if (I->FinalAndExpectedMatch)
+          FinalAndExpectedMatches.push_back(*I);
+      }
+    }
+
+    // Print numbered line with highlighting where there are no matches for
+    // expected patterns.
+    bool Newline = false;
+    if (Req.Verbose)
+      SetColor(raw_ostream::CYAN, true, true);
+    for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
+      bool StartsMatch = false;
+      bool EndsMatch = false;
+      for (auto M : FinalAndExpectedMatches) {
+        if (Col == M.InputEndCol)
+          EndsMatch = true;
+        else if (Col == M.InputStartCol)
+          StartsMatch = true;
+      }
+      if (StartsMatch)
+        errs().changeColor(raw_ostream::BLACK, false);
+      else if (EndsMatch)
+        errs().changeColor(raw_ostream::CYAN, true, true);
+      if (*InputFilePtr == '\n')
+        Newline = true;
+      else
+        errs() << *InputFilePtr;
+      ++InputFilePtr;
+    }
+    SetColor(raw_ostream::BLACK, false);
+    errs() << '\n';
+    unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline;
+
+    // Print any annotations.
+    while (AnnotationItr != AnnotationEnd &&
+           AnnotationItr->InputLine == Line) {
+      SetColor(AnnotationItr->Color, true);
+      // The two spaces below are where the ": " appears on input lines.
+      errs() << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
+      unsigned Col;
+      for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
+        errs() << ' ';
+      errs() << AnnotationItr->Mark;
+      // If InputEndCol=UINT_MAX, stop at InputLineWidth.
+      for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
+           ++Col)
+        errs() << '~';
+      errs() << '\n';
+      ++AnnotationItr;
+    }
+  }
+
+  if (WithColor::colorsEnabled(errs()))
+    errs().resetColor();
+  errs() << ">>>>>>\n";
+}
+
 int main(int argc, char **argv) {
   InitLLVM X(argc, argv);
   cl::ParseCommandLineOptions(argc, argv);
@@ -151,6 +623,10 @@
     return 2;
   }
 
+  if (DumpInput != "never" && DumpInput != "fail" && DumpInput != "always") {
+    errs() << "Unrecognized value for dump-input option: " << DumpInput << '\n';
+    return 2;
+  }
 
   SourceMgr SM;
 
@@ -198,10 +674,21 @@
                             InputFileText, InputFile.getBufferIdentifier()),
                         SMLoc());
 
-  int ExitCode =
-      FC.CheckInput(SM, InputFileText, CheckStrings) ? EXIT_SUCCESS : 1;
+  std::list<FileCheckDiag> DiagList;
+  int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, &DiagList)
+                     ? EXIT_SUCCESS
+                     : 1;
   if (ExitCode == 1 && DumpInputOnFailure)
     errs() << "Full input was:\n<<<<<<\n" << InputFileText << "\n>>>>>>\n";
+  if (DumpInput == "always" || (ExitCode == 1 && DumpInput == "fail")) {
+    errs() << '\n';
+    DumpInputAnnotationKey(Req);
+    std::list<InputAnnotation> AnnotationList;
+    unsigned LabelWidth;
+    BuildInputAnnotations(DiagList, AnnotationList, LabelWidth);
+    errs() << '\n';
+    DumpAnnotatedInput(Req, InputFileText, AnnotationList, LabelWidth);
+  }
 
   return ExitCode;
 }
Index: llvm/utils/lit/lit/TestingConfig.py
===================================================================
--- llvm/utils/lit/lit/TestingConfig.py
+++ llvm/utils/lit/lit/TestingConfig.py
@@ -26,7 +26,7 @@
                      'LSAN_OPTIONS', 'ADB', 'ANDROID_SERIAL',
                      'SANITIZER_IGNORE_CVE_2016_2143', 'TMPDIR', 'TMP', 'TEMP',
                      'TEMPDIR', 'AVRLIT_BOARD', 'AVRLIT_PORT',
-                     'FILECHECK_DUMP_INPUT_ON_FAILURE']
+                     'FILECHECK_DUMP_INPUT', 'FILECHECK_DUMP_INPUT_ON_FAILURE']
         for var in pass_vars:
             val = os.environ.get(var, '')
             # Check for empty string as some variables such as LD_PRELOAD cannot be empty