Index: llvm/include/llvm/Support/FileCheck.h
===================================================================
--- llvm/include/llvm/Support/FileCheck.h
+++ llvm/include/llvm/Support/FileCheck.h
@@ -142,11 +142,14 @@
   /// example, there might be a fuzzy match after a fail.
   ///
   /// We iterate these types, so they must have contiguous values in
-  /// [0, MatchTypeCount).
+  /// [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 {
     // TODO: More members will appear with later patches in this series.
+    MatchFinalButExcluded, //< the final match for an excluded pattern
+    MatchTypeFirst = MatchFinalButExcluded,
     MatchFinalButIllegal,  //< the final but illegal match for an expected pattern
-    MatchTypeFirst = MatchFinalButIllegal,
     MatchNoneButExpected,  //< no match for an expected pattern
     MatchFuzzy,            //< a fuzzy match (because no perfect match)
     MatchTypeCount,
@@ -189,7 +192,8 @@
   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,
Index: llvm/lib/Support/FileCheck.cpp
===================================================================
--- llvm/lib/Support/FileCheck.cpp
+++ llvm/lib/Support/FileCheck.cpp
@@ -882,31 +882,35 @@
                        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::MatchTypeCount
+                    : 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,
@@ -1000,7 +1004,8 @@
                  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
@@ -1027,7 +1032,7 @@
 
     // 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;
   }
 
@@ -1110,10 +1115,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!");
 
@@ -1127,7 +1133,7 @@
     }
 
     PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer, VariableTable,
-               Pos, MatchLen, Req);
+               Pos, MatchLen, Req, Diags);
 
     return true;
   }
@@ -1192,7 +1198,7 @@
       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
@@ -1234,7 +1240,7 @@
     }
     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 ||
@@ -1245,7 +1251,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();
Index: llvm/test/FileCheck/dump-input-annotations.txt
===================================================================
--- llvm/test/FileCheck/dump-input-annotations.txt
+++ llvm/test/FileCheck/dump-input-annotations.txt
@@ -203,6 +203,53 @@
 ; EMP2-NEXT:   >>>>>>
 ; EMP2-NOT:    {{.}}
 
+;--------------------------------------------------
+; CHECK-NOT
+;--------------------------------------------------
+
+; 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-NEXT:           2: world
+; NOT-NEXT:    not:2     !~~~~
+; NOT-NEXT:           3: again
+; 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-NEXT:             2: world
+; NOT2-NEXT:    not:2       !~~~~
+; NOT2-NEXT:             3: again
+; NOT2-NEXT:    >>>>>>
+; NOT2-NOT:     {{.}}
+
 ;--------------------------------------------------
 ; CHECK-DAG
 ;--------------------------------------------------
Index: llvm/utils/FileCheck/FileCheck.cpp
===================================================================
--- llvm/utils/FileCheck/FileCheck.cpp
+++ llvm/utils/FileCheck/FileCheck.cpp
@@ -118,24 +118,30 @@
 
 struct MatchTypeStyle {
   char Mark;
+  bool HasTildes;
   raw_ostream::Colors Color;
   const char *What;
-  MatchTypeStyle(char Mark, raw_ostream::Colors Color, const char *What)
-      : Mark(Mark), Color(Color), What(What) {}
+  MatchTypeStyle(char Mark, bool HasTildes, raw_ostream::Colors Color,
+                 const char *What)
+      : Mark(Mark), HasTildes(HasTildes), Color(Color), What(What) {}
 };
 
 static MatchTypeStyle GetMatchTypeStyle(unsigned MatchTy) {
   switch (MatchTy) {
+  case FileCheckDiag::MatchFinalButExcluded:
+    return MatchTypeStyle('!', true, raw_ostream::RED,
+                          "the final match for an excluded pattern (e.g., "
+                          "CHECK-NOT)");
   case FileCheckDiag::MatchFinalButIllegal:
-    return MatchTypeStyle('!', raw_ostream::RED,
+    return MatchTypeStyle('!', true, raw_ostream::RED,
                           "the final but illegal match for an expected "
                           "pattern (e.g., CHECK-NEXT)");
   case FileCheckDiag::MatchNoneButExpected:
-    return MatchTypeStyle('X', raw_ostream::RED,
+    return MatchTypeStyle('X', true, raw_ostream::RED,
                           "the search range for an unmatched expected "
                           "pattern (e.g., CHECK)");
   case FileCheckDiag::MatchFuzzy:
-    return MatchTypeStyle('?', raw_ostream::MAGENTA,
+    return MatchTypeStyle('?', false, raw_ostream::MAGENTA,
                           "a fuzzy match start for an otherwise unmatched "
                           "pattern");
   case FileCheckDiag::MatchTypeCount:
@@ -186,6 +192,32 @@
     OS << '\n';
   }
 
+  OS << "\nDetailed description of currently enabled markers:\n\n";
+
+  for (unsigned StyleIdx = FileCheckDiag::MatchTypeFirst;
+       StyleIdx < FileCheckDiag::MatchTypeCount; ++StyleIdx) {
+    MatchTypeStyle Style = GetMatchTypeStyle(StyleIdx);
+    if (StyleIdx == FileCheckDiag::MatchTypeFirst ||
+        Style.Mark != GetMatchTypeStyle(StyleIdx - 1).Mark ||
+        Style.HasTildes != GetMatchTypeStyle(StyleIdx - 1).HasTildes ||
+        (WithColor(OS).colorsEnabled() &&
+         Style.Color != GetMatchTypeStyle(StyleIdx - 1).Color)) {
+      OS << "  - ";
+      WithColor(OS, Style.Color, true)
+          << Style.Mark << (Style.HasTildes ? "~~" : "  ");
+      OS << "    marks ";
+      if (StyleIdx + 1 != FileCheckDiag::MatchTypeCount &&
+          Style.Mark == GetMatchTypeStyle(StyleIdx + 1).Mark &&
+          Style.HasTildes == GetMatchTypeStyle(StyleIdx + 1).HasTildes &&
+          (!WithColor(OS).colorsEnabled() ||
+           Style.Color == GetMatchTypeStyle(StyleIdx + 1).Color))
+        OS << "either:\n"
+           << "           - ";
+    } else
+      OS << "           - ";
+    OS << Style.What << "\n";
+  }
+
   // Files.
   OS << "\nInput file: ";
   if (InputFilename == "-")
@@ -293,7 +325,14 @@
       // 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;