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 &NotStrings, StringMap &VariableTable, - const FileCheckRequest &Req) const; + const FileCheckRequest &Req, + std::list *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 @@ -882,31 +882,35 @@ StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, StringRef Buffer, StringMap &VariableTable, size_t MatchPos, size_t MatchLen, - const FileCheckRequest &Req) { + const FileCheckRequest &Req, + std::list *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 &VariableTable, size_t MatchPos, - size_t MatchLen, FileCheckRequest &Req) { + size_t MatchLen, FileCheckRequest &Req, + std::list *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 &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::list *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;