From 3ef7d597d860134d1ef1939a7511f35fbbdde9de Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 18 Dec 2018 00:02:47 +0000 Subject: [PATCH] [FileCheck] Annotate input dump (4/7) This patch implements input annotations for diagnostics that report unexpected matches for CHECK-NOT. Like wrong-line matches for CHECK-NEXT, CHECK-SAME, and CHECK-EMPTY, these annotations mark match ranges using red `!~~` to indicate bad matches that are errors. For example: ``` $ FileCheck -dump-input=help The following description was requested by -dump-input=help to explain the input annotations printed by -dump-input=always and -dump-input=fail: - L: labels line number L of the input file - T:L labels the only match result for a pattern of type T from line L of the check file - T:L'N labels the Nth match result for a pattern of type T from line L of the check file - !~~ marks bad match, such as: - CHECK-NEXT on same line as previous match (error) - CHECK-NOT found (error) - X~~ marks search range when no match is found, such as: - CHECK-NEXT not found (error) - ? marks fuzzy match when no match is found - colors error, fuzzy match If you are not seeing color above or in input dumps, try: -color $ FileCheck -v -dump-input=always check3 < input3 |& sed -n '/^<<<>>>>> $ cat check3 CHECK: abc CHECK-NOT: foobar CHECK: def $ cat input3 abc foobar def ``` Reviewed By: george.karpenkov, probinson Differential Revision: https://reviews.llvm.org/D53896 llvm-svn: 349421 --- include/llvm/Support/FileCheck.h | 5 ++- lib/Support/FileCheck.cpp | 41 +++++++++++--------- test/FileCheck/dump-input-annotations.txt | 47 +++++++++++++++++++++++ utils/FileCheck/FileCheck.cpp | 3 ++ 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/include/llvm/Support/FileCheck.h b/include/llvm/Support/FileCheck.h index 8c0777731ac..b1c6689e711 100644 --- a/include/llvm/Support/FileCheck.h +++ b/include/llvm/Support/FileCheck.h @@ -163,6 +163,8 @@ struct FileCheckDiag { /// example, there might be a fuzzy match after a fail. enum MatchType { // TODO: More members will appear with later patches in this series. + /// Indicates the final match for an excluded pattern. + MatchFinalButExcluded, /// Indicates the final match for an expected pattern, but the match is on /// the wrong line. MatchFinalButWrongLine, @@ -210,7 +212,8 @@ struct FileCheckString { bool CheckNot(const SourceMgr &SM, StringRef Buffer, const std::vector &NotStrings, StringMap &VariableTable, - const FileCheckRequest &Req) const; + const FileCheckRequest &Req, + std::vector *Diags) const; size_t CheckDag(const SourceMgr &SM, StringRef Buffer, std::vector &NotStrings, StringMap &VariableTable, diff --git a/lib/Support/FileCheck.cpp b/lib/Support/FileCheck.cpp index 817e8d95bf0..a2f5db1e2c1 100644 --- a/lib/Support/FileCheck.cpp +++ b/lib/Support/FileCheck.cpp @@ -896,16 +896,18 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, int MatchedCount, StringRef Buffer, StringMap &VariableTable, size_t MatchPos, - size_t MatchLen, const FileCheckRequest &Req) { + size_t MatchLen, const FileCheckRequest &Req, + std::vector *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); std::string Message = formatv("{0}: {1} string found in input", Pat.getCheckTy().getDescription(Prefix), (ExpectedMatch ? "expected" : "excluded")) @@ -915,17 +917,19 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, SM.PrintMessage( Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message); - 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, int MatchedCount, StringRef Buffer, StringMap &VariableTable, - size_t MatchPos, size_t MatchLen, - FileCheckRequest &Req) { + size_t MatchPos, size_t MatchLen, FileCheckRequest &Req, + std::vector *Diags) { PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req); + MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req, + Diags); } static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, @@ -1036,7 +1040,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, return StringRef::npos; } PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos, - CurrentMatchLen, Req); + CurrentMatchLen, Req, Diags); // move start point after the match LastMatchEnd += MatchPos + CurrentMatchLen; @@ -1071,7 +1075,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, // 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; } @@ -1154,10 +1158,11 @@ bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { } /// Verify there's no "not strings" in the given buffer. -bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer, - const std::vector &NotStrings, - StringMap &VariableTable, - const FileCheckRequest &Req) const { +bool FileCheckString::CheckNot( + const SourceMgr &SM, StringRef Buffer, + const std::vector &NotStrings, + StringMap &VariableTable, const FileCheckRequest &Req, + std::vector *Diags) const { for (const FileCheckPattern *Pat : NotStrings) { assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); @@ -1171,7 +1176,7 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer, } PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable, - Pos, MatchLen, Req); + Pos, MatchLen, Req, Diags); return true; } @@ -1236,7 +1241,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, MatchPos += MatchPosBuf; if (Req.VerboseVerbose) PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, - VariableTable, MatchPos, MatchLen, Req); + VariableTable, 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 @@ -1278,7 +1283,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, } if (!Req.VerboseVerbose) PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable, - MatchPos, MatchLen, Req); + MatchPos, MatchLen, Req, Diags); // Handle the end of a CHECK-DAG group. if (std::next(PatItr) == PatEnd || @@ -1289,7 +1294,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // 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(); diff --git a/test/FileCheck/dump-input-annotations.txt b/test/FileCheck/dump-input-annotations.txt index d225ed2bd14..c808fb2c6d4 100644 --- a/test/FileCheck/dump-input-annotations.txt +++ b/test/FileCheck/dump-input-annotations.txt @@ -234,6 +234,53 @@ ; EMP2-NEXT: >>>>>> ; EMP2-NOT: {{.}} +;-------------------------------------------------- +; CHECK-NOT +;-------------------------------------------------- + +; No match (success) and unexpected match (error). + +; 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 !~~~~ error: no match expected +; 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 !~~~~ error: no match expected +; NOT2-NEXT: 3: again +; NOT2-NEXT: >>>>>> +; NOT2-NOT: {{.}} + ;-------------------------------------------------- ; CHECK-DAG ;-------------------------------------------------- diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index d027f3e6676..1f6caeee2e1 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -143,6 +143,8 @@ struct MarkerStyle { static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { switch (MatchTy) { + case FileCheckDiag::MatchFinalButExcluded: + return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); case FileCheckDiag::MatchFinalButWrongLine: return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); case FileCheckDiag::MatchNoneButExpected: @@ -182,6 +184,7 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) { WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; OS << " marks bad match, such as:\n" << " - CHECK-NEXT on same line as previous match (error)\n" + << " - CHECK-NOT found (error)\n" << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; OS << " marks search range when no match is found, such as:\n"