OSDN Git Service

[FileCheck] Implement -v and -vv for tracing matches
authorJoel E. Denny <jdenny.ornl@gmail.com>
Fri, 13 Jul 2018 03:08:23 +0000 (03:08 +0000)
committerJoel E. Denny <jdenny.ornl@gmail.com>
Fri, 13 Jul 2018 03:08:23 +0000 (03:08 +0000)
-v prints all directive pattern matches.

-vv additionally prints info that might be noise to users but that can
be helpful to FileCheck developers.

To maximize code reuse and to make diagnostics more consistent, this
patch also adjusts and extends some of the existing diagnostics.
CHECK-NOT failures now report variables uses.  Many more diagnostics
now report the check prefix and kind of directive.

Reviewed By: probinson

Differential Revision: https://reviews.llvm.org/D47114

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@336967 91177308-0d34-0410-b5e6-96231b3b80d8

docs/CommandGuide/FileCheck.rst
test/FileCheck/check-empty.txt
test/FileCheck/check-label-dag.txt
test/FileCheck/check-multiple-prefixes-nomatch-2.txt
test/FileCheck/check-multiple-prefixes-nomatch.txt
test/FileCheck/check-not-diaginfo.txt
test/FileCheck/defines.txt
test/FileCheck/implicit-check-not.txt
test/FileCheck/match-full-lines.txt
test/FileCheck/verbose.txt [new file with mode: 0644]
utils/FileCheck/FileCheck.cpp

index ac7e810..dfa2fd2 100644 (file)
@@ -95,6 +95,16 @@ OPTIONS
 
  Show the version number of this program.
 
+.. option:: -v
+
+  Print directive pattern matches.
+
+.. option:: -vv
+
+  Print information helpful in diagnosing internal FileCheck issues, such as
+  discarded overlapping ``CHECK-DAG:`` matches, implicit EOF pattern matches,
+  and ``CHECK-NOT:`` patterns that do not have matches.  Implies ``-v``.
+
 .. option:: --allow-deprecated-dag-overlap
 
   Enable overlapping among matches in a group of consecutive ``CHECK-DAG:``
index 319b411..1caff2a 100644 (file)
@@ -9,4 +9,4 @@
 ; EMPTY-ERR: FileCheck error: '-' is empty.
 ; EMPTY-ERR-NEXT: FileCheck command line: {{.*}}FileCheck{{.*}}-check-prefix={{.*}}FOO {{.*}}check-empty.txt
 ; NO-EMPTY-ERR-NOT: FileCheck error: '-' is empty.
-; NOT-FOUND: error: expected string not found in input
+; NOT-FOUND: error: FOO: expected string not found in input
index 2f54c3e..1ce4a8d 100644 (file)
@@ -7,5 +7,5 @@ CHECK-LABEL: {{^}}bar
 CHECK-DAG: {{^}}foo
 CHECK-LABEL: {{^}}zed
 
-ERROR: error: expected string not found in input
+ERROR: error: CHECK-DAG: expected string not found in input
 ERROR-NEXT: CHECK-DAG: {{.....}}foo
index 6055471..55c5211 100644 (file)
@@ -8,5 +8,5 @@ bar
 ; FOO: fo{{o}}
 ; BAR: ba{{r}}
 
-; CHECK: {{error: expected string not found in input}}
+; CHECK: {{error: FOO: expected string not found in input}}
 ; CHECK-NEXT: {{F}}OO: fo{{[{][{]o[}][}]}}
index b1a4132..90a2a38 100644 (file)
@@ -8,5 +8,5 @@ foo
 ; BAR: ba{{z}}
 ; FOO: fo{{o}}
 
-; CHECK: {{error: expected string not found in input}}
+; CHECK: {{error: BAR: expected string not found in input}}
 ; CHECK-NEXT: {{B}}AR: ba{{[{][{]z[}][}]}}
index 44a46a3..de2d13e 100644 (file)
@@ -2,6 +2,9 @@
 
 CHECK-NOT: test
 
-DIAG:         CHECK-NOT: pattern specified here
+DIAG:         error: CHECK-NOT: excluded string found in input
+DIAG-NEXT:    CHECK-NOT: test
+DIAG-NEXT: {{^           \^}}
+DIAG-NEXT:    note: found here
 DIAG-NEXT:    CHECK-NOT: test
 DIAG-NEXT: {{^           \^}}
index d2219b7..c788124 100644 (file)
@@ -1,9 +1,18 @@
 ; RUN: FileCheck -DVALUE=10 -input-file %s %s
 ; RUN: not FileCheck -DVALUE=20 -input-file %s %s 2>&1 | FileCheck %s -check-prefix ERRMSG
+;
+; RUN: not FileCheck -DVALUE=10 -check-prefix NOT -input-file %s %s 2>&1 | FileCheck %s -check-prefix NOT-ERRMSG
+; RUN: FileCheck -DVALUE=20 -check-prefix NOT -input-file %s %s
 
 Value = 10
 ; CHECK: Value = [[VALUE]]
+; NOT-NOT: Value = [[VALUE]]
 
-; ERRMSG: defines.txt:5:10: error: expected string not found in input
+; ERRMSG: defines.txt:8:10: error: CHECK: expected string not found in input
+; ERRMSG: defines.txt:1:1: note: scanning from here
 ; ERRMSG: defines.txt:1:1: note: with variable "VALUE" equal to "20"
-; ERRMSG: defines.txt:4:1: note: possible intended match here
+; ERRMSG: defines.txt:7:1: note: possible intended match here
+
+; NOT-ERRMSG: defines.txt:9:12: error: {{NOT}}-NOT: excluded string found in input
+; NOT-ERRMSG: defines.txt:7:1: note: found here
+; NOT-ERRMSG: defines.txt:7:1: note: with variable "VALUE" equal to "10"
\ No newline at end of file
index 4267736..39b758e 100644 (file)
@@ -9,36 +9,36 @@
 
 warning: aaa
 ; CHECK-PASS: warning: aaa
-; CHECK-ERROR1: error: CHECK-FAIL1-NOT: string occurred!
-; CHECK-ERROR1: command line:1:22: note: CHECK-FAIL1-NOT: pattern specified here
+; CHECK-ERROR1: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
 ; CHECK-ERROR1-NEXT: -implicit-check-not='warning:'
+; CHECK-ERROR1: note: found here
 ; CHECK-FAIL2: warning: aaa
 ; CHECK-FAIL3: warning: aaa
-; CHECK-ERROR4: error: CHECK-FAIL1-NOT: string occurred!
-; CHECK-ERROR4: command line:1:22: note: CHECK-FAIL1-NOT: pattern specified here
+; CHECK-ERROR4: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
 ; CHECK-ERROR4-NEXT: {{-implicit-check-not='\{\{aaa\|bbb\|ccc\}\}'}}
-; CHECK-ERROR5: error: CHECK-FAIL1-NOT: string occurred!
-; CHECK-ERROR5: command line:1:22: note: CHECK-FAIL1-NOT: pattern specified here
+; CHECK-ERROR4: note: found here
+; CHECK-ERROR5: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
 ; CHECK-ERROR5-NEXT: -implicit-check-not='aaa'
+; CHECK-ERROR5: note: found here
 
 warning: bbb
 ; CHECK-PASS: warning: bbb
 ; CHECK-FAIL1: warning: bbb
-; CHECK-ERROR2: error: CHECK-FAIL2-NOT: string occurred!
-; CHECK-ERROR2: command line:1:22: note: CHECK-FAIL2-NOT: pattern specified here
+; CHECK-ERROR2: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
 ; CHECK-ERROR2-NEXT: -implicit-check-not='warning:'
+; CHECK-ERROR2: note: found here
 ; CHECK-FAIL3: warning: bbb
-; CHECK-ERROR6: error: CHECK-FAIL2-NOT: string occurred!
-; CHECK-ERROR6: command line:1:22: note: CHECK-FAIL2-NOT: pattern specified here
+; CHECK-ERROR6: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
 ; CHECK-ERROR6-NEXT: -implicit-check-not='bbb'
+; CHECK-ERROR6: note: found here
 
 warning: ccc
 ; CHECK-PASS: warning: ccc
 ; CHECK-FAIL1: warning: ccc
 ; CHECK-FAIL2: warning: ccc
-; CHECK-ERROR3: error: CHECK-FAIL3-NOT: string occurred!
-; CHECK-ERROR3: command line:1:22: note: CHECK-FAIL3-NOT: pattern specified here
+; CHECK-ERROR3: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
 ; CHECK-ERROR3-NEXT: -implicit-check-not='warning:'
-; CHECK-ERROR7: error: CHECK-FAIL3-NOT: string occurred!
-; CHECK-ERROR7: command line:1:22: note: CHECK-FAIL3-NOT: pattern specified here
+; CHECK-ERROR3: note: found here
+; CHECK-ERROR7: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
 ; CHECK-ERROR7-NEXT: -implicit-check-not='ccc'
+; CHECK-ERROR7: note: found here
index d6b10a5..1d6a965 100644 (file)
@@ -26,28 +26,28 @@ Label 66
 // CHECK:a line
 // CHECK:trailing whitespace
 // CHECK:trailing more whitespace   
-// ERROR-STRICT:error: expected string not found in input
+// ERROR-STRICT:error: {{C}}HECK: expected string not found in input
 // ERROR-STRICT:// {{C}}HECK:trailing whitespace
 
 // CHECK-LABEL:Label 2
 // CHECK:a line
 // CHECK-NEXT:leading whitespace
 // CHECK-NEXT:   leading more whitespace
-// ERROR-STRICT:error: expected string not found in input
+// ERROR-STRICT:error: {{C}}HECK-NEXT: expected string not found in input
 // ERROR-STRICT:// {{C}}HECK-NEXT:leading whitespace
 
 // CHECK-LABEL:Label 3
 // CHECK:line
-// ERROR:error: expected string not found in input
+// ERROR:error: {{C}}HECK: expected string not found in input
 // ERROR:// {{C}}HECK:line
 
 // CHECK-LABEL:Label 4
 // CHECK:a line
 // CHECK-NOT:random
-// ERROR:error: {{C}}HECK-NOT: string occurred!
+// ERROR:error: {{C}}HECK-NOT: excluded string found in input
 // ERROR:a random thing
 
 // CHECK-LABEL:Label 5
 // CHECK-LABEL:Label 6
-// ERROR:error: expected string not found in input
+// ERROR:error: {{C}}HECK-LABEL: expected string not found in input
 // ERROR:{{C}}HECK-LABEL:Label 6
diff --git a/test/FileCheck/verbose.txt b/test/FileCheck/verbose.txt
new file mode 100644 (file)
index 0000000..e6eec5e
--- /dev/null
@@ -0,0 +1,115 @@
+; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
+; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck -check-prefix V %s
+; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck -check-prefixes V,VV %s
+
+foo
+bar
+CHECK: foo
+CHECK-NOT: raboof
+CHECK-NEXT: bar
+
+V:      verbose.txt:[[@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
+V-NEXT: {{C}}HECK: foo{{$}}
+V-NEXT:  {{^       \^$}}
+V-NEXT: verbose.txt:[[@LINE-9]]:1: note: found here
+V-NEXT: {{^}}foo{{$}}
+V-NEXT: {{^}}^~~{{$}}
+
+V-NEXT: verbose.txt:[[@LINE-9]]:13: remark: {{C}}HECK-NEXT: expected string found in input
+V-NEXT: {{C}}HECK-NEXT: bar{{$}}
+V-NEXT:  {{^            \^$}}
+V-NEXT: verbose.txt:[[@LINE-15]]:1: note: found here
+V-NEXT: {{^}}bar{{$}}
+V-NEXT: {{^}}^~~{{$}}
+
+VV-NEXT: verbose.txt:[[@LINE-17]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
+VV-NEXT: {{C}}HECK-NOT: raboof{{$}}
+VV-NEXT:  {{^           \^$}}
+VV-NEXT: verbose.txt:[[@LINE-22]]:1: note: scanning from here
+VV-NEXT: {{^}}bar{{$}}
+VV-NEXT: {{^}}^{{$}}
+
+before empty
+
+after empty
+CHECK: before empty
+CHECK-EMPTY:
+CHECK-NEXT: after empty
+
+V:      verbose.txt:[[@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
+V-NEXT: {{C}}HECK: before empty{{$}}
+V-NEXT:  {{^       \^$}}
+V-NEXT: verbose.txt:[[@LINE-10]]:1: note: found here
+V-NEXT: {{^}}before empty{{$}}
+V-NEXT: {{^}}^~~~~~~~~~~~{{$}}
+
+V-NEXT:  verbose.txt:[[@LINE-10]]:13: remark: {{C}}HECK-EMPTY: expected string found in input
+V-NEXT:  {{C}}HECK-EMPTY:{{$}}
+V-NEXT:   {{^            \^$}}
+V-NEXT:  verbose.txt:[[@LINE-16]]:1: note: found here
+V-EMPTY:
+V-NEXT:  {{^}}^{{$}}
+
+V-NEXT: verbose.txt:[[@LINE-16]]:13: remark: {{C}}HECK-NEXT: expected string found in input
+V-NEXT: {{C}}HECK-NEXT: after empty{{$}}
+V-NEXT:  {{^            \^$}}
+V-NEXT: verbose.txt:[[@LINE-22]]:1: note: found here
+V-NEXT: {{^}}after empty{{$}}
+V-NEXT: {{^}}^~~~~~~~~~~{{$}}
+
+abcdef
+abcdef
+CHECK-DAG: abcdef
+CHECK-DAG: def
+
+V-NEXT: verbose.txt:[[@LINE-3]]:12: remark: {{C}}HECK-DAG: expected string found in input
+V-NEXT: {{C}}HECK-DAG: abcdef
+V-NEXT:  {{^           \^$}}
+V-NEXT: verbose.txt:[[@LINE-8]]:1: note: found here
+V-NEXT: {{^}}abcdef{{$}}
+V-NEXT: {{^}}^~~~~~{{$}}
+
+VV-NEXT: verbose.txt:[[@LINE-9]]:12: remark: {{C}}HECK-DAG: expected string found in input
+VV-NEXT: {{C}}HECK-DAG: def
+VV-NEXT:  {{^           \^$}}
+VV-NEXT: verbose.txt:[[@LINE-15]]:4: note: found here
+VV-NEXT: {{^abcdef$}}
+VV-NEXT: {{^   \^~~$}}
+VV-NEXT: verbose.txt:[[@LINE-18]]:1: note: match discarded, overlaps earlier DAG match here
+VV-NEXT: {{^}}abcdef{{$}}
+VV-NEXT: {{^}}^~~~~~{{$}}
+
+V-NEXT: verbose.txt:[[@LINE-19]]:12: remark: {{C}}HECK-DAG: expected string found in input
+V-NEXT: {{C}}HECK-DAG: def
+V-NEXT:  {{^           \^$}}
+V-NEXT: verbose.txt:[[@LINE-24]]:4: note: found here
+V-NEXT: {{^abcdef$}}
+V-NEXT: {{^   \^~~$}}
+
+xyz
+CHECK: xyz
+CHECK-NOT: {{z}}yx
+
+V:      verbose.txt:[[@LINE-3]]:8: remark: {{C}}HECK: expected string found in input
+V-NEXT: {{C}}HECK: xyz{{$}}
+V-NEXT:  {{^       \^$}}
+V-NEXT: verbose.txt:[[@LINE-7]]:1: note: found here
+V-NEXT: {{^}}xyz{{$}}
+V-NEXT: {{^}}^~~{{$}}
+
+VV-NEXT: verbose.txt:[[@LINE-9]]:19: remark: implicit EOF: expected string found in input
+VV-NEXT: {{C}}HECK-NOT: {{[{][{]z[}][}]yx$}}
+VV-NEXT:   {{^                 \^$}}
+VV-NEXT: verbose.txt:[[@LINE+13]]:1: note: found here
+VV-NOT:  {{.}}
+VV:      {{^\^$}}
+
+VV-NEXT: verbose.txt:[[@LINE-16]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
+VV-NEXT: {{C}}HECK-NOT: {{[{][{]z[}][}]yx$}}
+VV-NEXT:  {{^           \^$}}
+VV-NEXT: verbose.txt:[[@LINE-20]]:1: note: scanning from here
+VV-NEXT: {{^C}}HECK: xyz{{$}}
+VV-NEXT: {{^\^$}}
+
+QUIET-NOT: {{.}}
+V-NOT: {{.}}
index cd3175f..4efe535 100644 (file)
@@ -90,6 +90,14 @@ static cl::opt<bool> AllowDeprecatedDagOverlap(
              "provided for convenience as old tests are migrated to the new\n"
              "non-overlapping CHECK-DAG implementation.\n"));
 
+static cl::opt<bool> Verbose("v", cl::init(false),
+                             cl::desc("Print directive pattern matches.\n"));
+
+static cl::opt<bool> VerboseVerbose(
+    "vv", cl::init(false),
+    cl::desc("Print information helpful in diagnosing internal FileCheck\n"
+             "issues.  Implies -v.\n"));
+
 typedef cl::list<std::string>::const_iterator prefix_iterator;
 
 //===----------------------------------------------------------------------===//
@@ -154,8 +162,11 @@ public:
                     unsigned LineNumber);
   size_t Match(StringRef Buffer, size_t &MatchLen,
                StringMap<StringRef> &VariableTable) const;
-  void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
-                        const StringMap<StringRef> &VariableTable) const;
+  void PrintVariableUses(const SourceMgr &SM, StringRef Buffer,
+                         const StringMap<StringRef> &VariableTable,
+                         SMRange MatchRange = None) const;
+  void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
+                       const StringMap<StringRef> &VariableTable) const;
 
   bool hasVariable() const {
     return !(VariableUses.empty() && VariableDefs.empty());
@@ -484,8 +495,12 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
     VariableTable[VariableDef.first] = MatchInfo[VariableDef.second];
   }
 
-  MatchLen = FullMatch.size();
-  return FullMatch.data() - Buffer.data();
+  // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
+  // the required preceding newline, which is consumed by the pattern in the
+  // case of CHECK-EMPTY but not CHECK-NEXT.
+  size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
+  MatchLen = FullMatch.size() - MatchStartSkip;
+  return FullMatch.data() - Buffer.data() + MatchStartSkip;
 }
 
 
@@ -511,11 +526,9 @@ Pattern::ComputeMatchDistance(StringRef Buffer,
   return BufferPrefix.edit_distance(ExampleString);
 }
 
-/// Prints additional information about a failure to match involving this
-/// pattern.
-void Pattern::PrintFailureInfo(
-    const SourceMgr &SM, StringRef Buffer,
-    const StringMap<StringRef> &VariableTable) const {
+void Pattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer,
+                                const StringMap<StringRef> &VariableTable,
+                                SMRange MatchRange) const {
   // If this was a regular expression using variables, print the current
   // variable values.
   if (!VariableUses.empty()) {
@@ -547,11 +560,19 @@ void Pattern::PrintFailureInfo(
         }
       }
 
-      SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
-                      OS.str());
+      if (MatchRange.isValid())
+        SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, OS.str(),
+                        {MatchRange});
+      else
+        SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
+                        SourceMgr::DK_Note, OS.str());
     }
   }
+}
 
+void Pattern::PrintFuzzyMatch(
+    const SourceMgr &SM, StringRef Buffer,
+    const StringMap<StringRef> &VariableTable) 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
@@ -741,6 +762,33 @@ static size_t CheckTypeSize(Check::CheckType Ty) {
   llvm_unreachable("Bad check type");
 }
 
+// Get a description of the type.
+static std::string CheckTypeName(StringRef Prefix, Check::CheckType Ty) {
+  switch (Ty) {
+  case Check::CheckNone:
+    return "invalid";
+  case Check::CheckPlain:
+    return Prefix;
+  case Check::CheckNext:
+    return Prefix.str() + "-NEXT";
+  case Check::CheckSame:
+    return Prefix.str() + "-SAME";
+  case Check::CheckNot:
+    return Prefix.str() + "-NOT";
+  case Check::CheckDAG:
+    return Prefix.str() + "-DAG";
+  case Check::CheckLabel:
+    return Prefix.str() + "-LABEL";
+  case Check::CheckEmpty:
+    return Prefix.str() + "-EMPTY";
+  case Check::CheckEOF:
+    return "implicit EOF";
+  case Check::CheckBadNot:
+    return "bad NOT";
+  }
+  llvm_unreachable("unknown CheckType");
+}
+
 static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) {
   if (Buffer.size() <= Prefix.size())
     return Check::CheckNone;
@@ -990,12 +1038,49 @@ static bool ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
   return false;
 }
 
-static void PrintCheckFailed(const SourceMgr &SM, SMLoc Loc, const Pattern &Pat,
-                             StringRef Buffer,
-                             StringMap<StringRef> &VariableTable) {
+static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
+                       StringRef Prefix, SMLoc Loc, const Pattern &Pat,
+                       StringRef Buffer, StringMap<StringRef> &VariableTable,
+                       size_t MatchPos, size_t MatchLen) {
+  if (ExpectedMatch) {
+    if (!Verbose)
+      return;
+    if (!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);
+  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});
+  Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange);
+}
+
+static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
+                       const CheckString &CheckStr, StringRef Buffer,
+                       StringMap<StringRef> &VariableTable, size_t MatchPos,
+                       size_t MatchLen) {
+  PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
+             Buffer, VariableTable, MatchPos, MatchLen);
+}
+
+static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
+                         StringRef Prefix, SMLoc Loc, const Pattern &Pat,
+                         StringRef Buffer,
+                         StringMap<StringRef> &VariableTable) {
+  if (!ExpectedMatch && !VerboseVerbose)
+    return;
+
   // Otherwise, we have an error, emit an error message.
-  SM.PrintMessage(Loc, SourceMgr::DK_Error,
-                  "expected string not found in input");
+  SM.PrintMessage(Loc,
+                  ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
+                  CheckTypeName(Prefix, Pat.getCheckTy()) + ": " +
+                      (ExpectedMatch ? "expected" : "excluded") +
+                      " string not found in input");
 
   // 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.
@@ -1005,13 +1090,16 @@ static void PrintCheckFailed(const SourceMgr &SM, SMLoc Loc, const Pattern &Pat,
                   "scanning from here");
 
   // Allow the pattern to print additional information if desired.
-  Pat.PrintFailureInfo(SM, Buffer, VariableTable);
+  Pat.PrintVariableUses(SM, Buffer, VariableTable);
+  if (ExpectedMatch)
+    Pat.PrintFuzzyMatch(SM, Buffer, VariableTable);
 }
 
-static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
-                             StringRef Buffer,
-                             StringMap<StringRef> &VariableTable) {
-  PrintCheckFailed(SM, CheckStr.Loc, CheckStr.Pat, Buffer, VariableTable);
+static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
+                         const CheckString &CheckStr, StringRef Buffer,
+                         StringMap<StringRef> &VariableTable) {
+  PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
+               Buffer, VariableTable);
 }
 
 /// Count the number of newlines in the specified range.
@@ -1059,9 +1147,10 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
   StringRef MatchBuffer = Buffer.substr(LastPos);
   size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
   if (MatchPos == StringRef::npos) {
-    PrintCheckFailed(SM, *this, MatchBuffer, VariableTable);
+    PrintNoMatch(true, SM, *this, MatchBuffer, VariableTable);
     return StringRef::npos;
   }
+  PrintMatch(true, SM, *this, MatchBuffer, VariableTable, MatchPos, MatchLen);
 
   // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
   // or CHECK-NOT
@@ -1107,11 +1196,6 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
   const char *FirstNewLine = nullptr;
   unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
 
-  // For CHECK-EMPTY, the preceding new line is consumed by the pattern, so
-  // this needs to be re-added.
-  if (Pat.getCheckTy() == Check::CheckEmpty)
-    ++NumNewLines;
-
   if (NumNewLines == 0) {
     SM.PrintMessage(Loc, SourceMgr::DK_Error,
                     CheckName + ": is on the same line as previous match");
@@ -1177,13 +1261,15 @@ bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
     size_t MatchLen = 0;
     size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
 
-    if (Pos == StringRef::npos)
+    if (Pos == StringRef::npos) {
+      PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer,
+                   VariableTable);
       continue;
+    }
+
+    PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer, VariableTable,
+               Pos, MatchLen);
 
-    SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + Pos),
-                    SourceMgr::DK_Error, Prefix + "-NOT: string occurred!");
-    SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
-                    Prefix + "-NOT: pattern specified here");
     return true;
   }
 
@@ -1230,11 +1316,15 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
       // With a group of CHECK-DAGs, a single mismatching means the match on
       // that group of CHECK-DAGs fails immediately.
       if (MatchPosBuf == StringRef::npos) {
-        PrintCheckFailed(SM, Pat.getLoc(), Pat, MatchBuffer, VariableTable);
+        PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, MatchBuffer,
+                     VariableTable);
         return StringRef::npos;
       }
       // Re-calc it as the offset relative to the start of the original string.
       MatchPos += MatchPosBuf;
+      if (VerboseVerbose)
+        PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
+                   MatchPos, MatchLen);
       if (AllowDeprecatedDagOverlap)
         break;
       // Iterate previous matches until overlapping match or insertion point.
@@ -1253,8 +1343,19 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
         Matches.insert(MI, M);
         break;
       }
+      if (VerboseVerbose) {
+        SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);
+        SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);
+        SMRange OldRange(OldStart, OldEnd);
+        SM.PrintMessage(OldStart, SourceMgr::DK_Note,
+                        "match discarded, overlaps earlier DAG match here",
+                        {OldRange});
+      }
       MatchPos = MI->End;
     }
+    if (!VerboseVerbose)
+      PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
+                 MatchPos, MatchLen);
 
     if (!NotStrings.empty()) {
       if (MatchPos < LastPos) {
@@ -1455,6 +1556,9 @@ int main(int argc, char **argv) {
     return 2;
   }
 
+  if (VerboseVerbose)
+    Verbose = true;
+
   SourceMgr SM;
 
   // Read the expected strings from the check file.