Index: llvm/include/llvm/Support/FileCheck.h =================================================================== --- llvm/include/llvm/Support/FileCheck.h +++ llvm/include/llvm/Support/FileCheck.h @@ -164,6 +164,8 @@ /// example, there might be a fuzzy match after a fail. enum MatchType { // TODO: More members will appear with later patches in this series. + /// the final match for an excluded pattern + MatchFinalButExcluded, /// the final match for an expected pattern but on the wrong line MatchFinalButWrongLine, /// no match for an expected pattern @@ -210,7 +212,8 @@ 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, Index: llvm/lib/Support/FileCheck.cpp =================================================================== --- llvm/lib/Support/FileCheck.cpp +++ llvm/lib/Support/FileCheck.cpp @@ -896,16 +896,18 @@ 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 @@ 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 @@ 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 @@ // 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 @@ } /// 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 @@ } PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable, - Pos, MatchLen, Req); + Pos, MatchLen, Req, Diags); return true; } @@ -1236,7 +1241,7 @@ 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 @@ } 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 @@ // 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 @@ -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 ;-------------------------------------------------- Index: llvm/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/utils/FileCheck/FileCheck.cpp +++ llvm/utils/FileCheck/FileCheck.cpp @@ -143,6 +143,8 @@ static MarkerStyle GetMarker(unsigned 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: @@ -180,6 +182,7 @@ 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"