Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -95,6 +95,16 @@ 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: test/FileCheck/check-empty.txt =================================================================== --- test/FileCheck/check-empty.txt +++ test/FileCheck/check-empty.txt @@ -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: test/FileCheck/check-label-dag.txt =================================================================== --- test/FileCheck/check-label-dag.txt +++ test/FileCheck/check-label-dag.txt @@ -7,5 +7,5 @@ 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: test/FileCheck/check-multiple-prefixes-nomatch-2.txt =================================================================== --- test/FileCheck/check-multiple-prefixes-nomatch-2.txt +++ test/FileCheck/check-multiple-prefixes-nomatch-2.txt @@ -8,5 +8,5 @@ ; 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: test/FileCheck/check-multiple-prefixes-nomatch.txt =================================================================== --- test/FileCheck/check-multiple-prefixes-nomatch.txt +++ test/FileCheck/check-multiple-prefixes-nomatch.txt @@ -8,5 +8,5 @@ ; 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: test/FileCheck/check-not-diaginfo.txt =================================================================== --- test/FileCheck/check-not-diaginfo.txt +++ test/FileCheck/check-not-diaginfo.txt @@ -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: test/FileCheck/defines.txt =================================================================== --- test/FileCheck/defines.txt +++ test/FileCheck/defines.txt @@ -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: test/FileCheck/implicit-check-not.txt =================================================================== --- test/FileCheck/implicit-check-not.txt +++ test/FileCheck/implicit-check-not.txt @@ -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: test/FileCheck/match-full-lines.txt =================================================================== --- test/FileCheck/match-full-lines.txt +++ test/FileCheck/match-full-lines.txt @@ -26,28 +26,28 @@ // 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 Index: test/FileCheck/verbose.txt =================================================================== --- /dev/null +++ test/FileCheck/verbose.txt @@ -0,0 +1,87 @@ +; 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: {{^}}^{{$}} + +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: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -90,6 +90,14 @@ "provided for convenience as old tests are migrated to the new\n" "non-overlapping CHECK-DAG implementation.\n")); +static cl::opt Verbose("v", cl::init(false), + cl::desc("Print directive pattern matches.\n")); + +static cl::opt VerboseVerbose( + "vv", cl::init(false), + cl::desc("Print information helpful in diagnosing internal FileCheck\n" + "issues. Implies -v.\n")); + typedef cl::list::const_iterator prefix_iterator; //===----------------------------------------------------------------------===// @@ -153,8 +161,11 @@ unsigned LineNumber); size_t Match(StringRef Buffer, size_t &MatchLen, StringMap &VariableTable) const; - void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, - const StringMap &VariableTable) const; + void PrintVariableUses(const SourceMgr &SM, StringRef Buffer, + const StringMap &VariableTable, + SMRange MatchRange = None) const; + void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer, + const StringMap &VariableTable) const; bool hasVariable() const { return !(VariableUses.empty() && VariableDefs.empty()); @@ -497,11 +508,9 @@ 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 &VariableTable) const { +void Pattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer, + const StringMap &VariableTable, + SMRange MatchRange) const { // If this was a regular expression using variables, print the current // variable values. if (!VariableUses.empty()) { @@ -533,11 +542,19 @@ } } - 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 &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 @@ -724,6 +741,31 @@ 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::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; @@ -966,12 +1008,49 @@ return false; } -static void PrintCheckFailed(const SourceMgr &SM, SMLoc Loc, const Pattern &Pat, - StringRef Buffer, - StringMap &VariableTable) { +static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, + StringRef Prefix, SMLoc Loc, const Pattern &Pat, + StringRef Buffer, StringMap &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 &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 &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. @@ -981,13 +1060,16 @@ "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 &VariableTable) { - PrintCheckFailed(SM, CheckStr.Loc, CheckStr.Pat, Buffer, VariableTable); +static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, + const CheckString &CheckStr, StringRef Buffer, + StringMap &VariableTable) { + PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, + Buffer, VariableTable); } /// Count the number of newlines in the specified range. @@ -1035,9 +1117,10 @@ 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 @@ -1143,13 +1226,15 @@ 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; } @@ -1196,11 +1281,15 @@ // 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. @@ -1219,8 +1308,19 @@ 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) { @@ -1421,6 +1521,9 @@ return 2; } + if (VerboseVerbose) + Verbose = true; + SourceMgr SM; // Read the expected strings from the check file.