Index: llvm/include/llvm/FileCheck/FileCheck.h =================================================================== --- llvm/include/llvm/FileCheck/FileCheck.h +++ llvm/include/llvm/FileCheck/FileCheck.h @@ -142,6 +142,9 @@ /// matches when multiple matches are expected for the pattern, or it might /// follow discarded matches for the pattern. MatchNoneButExpected, + /// Indicates no match due to invalid pattern. The problems are usually + /// recorded in subsequent diagnostics with notes. + MatchNoneForInvalidPattern, /// Indicates a fuzzy match that serves as a suggestion for the next /// intended match for an expected pattern with too few or no good matches. MatchFuzzy, Index: llvm/lib/FileCheck/FileCheck.cpp =================================================================== --- llvm/lib/FileCheck/FileCheck.cpp +++ llvm/lib/FileCheck/FileCheck.cpp @@ -2060,43 +2060,51 @@ MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags); } -static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, +static bool PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const Pattern &Pat, int MatchedCount, StringRef Buffer, bool VerboseVerbose, std::vector *Diags, Error MatchErrors) { + // MatchErrors should have at least one error for no match even if no match + // was expected. It might also have pattern errors. assert(MatchErrors && "Called on successful match"); + bool HasError = ExpectedMatch; + FileCheckDiag::MatchType MatchTy = ExpectedMatch + ? FileCheckDiag::MatchNoneButExpected + : FileCheckDiag::MatchNoneAndExcluded; + SmallVector PatternErrorMsgs; + MatchErrors = + handleErrors(std::move(MatchErrors), [&](const ErrorDiagnostic &E) { + HasError = true; + MatchTy = FileCheckDiag::MatchNoneForInvalidPattern; + E.log(errs()); + if (Diags) + PatternErrorMsgs.push_back(E.getMessage().str()); + }); + consumeError(std::move(MatchErrors)); + + // Suppress some verbosity if there's no error. bool PrintDiag = true; - if (!ExpectedMatch) { - if (!VerboseVerbose) { - consumeError(std::move(MatchErrors)); - return; - } + if (!HasError) { + if (!VerboseVerbose) + return HasError; // Due to their verbosity, we don't print verbose diagnostics here if we're // gathering them for a different rendering, but we always print other // diagnostics. PrintDiag = !Diags; } - FileCheckDiag::MatchType MatchTy = ExpectedMatch - ? FileCheckDiag::MatchNoneButExpected - : FileCheckDiag::MatchNoneAndExcluded; SMRange SearchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags); - if (Diags) + if (Diags) { + SMRange NoteRange = SMRange(SearchRange.Start, SearchRange.Start); + for (const std::string &PatternErrorMsg : PatternErrorMsgs) + Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange, + PatternErrorMsg); Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags); - if (!PrintDiag) { - consumeError(std::move(MatchErrors)); - return; } - - MatchErrors = handleErrors(std::move(MatchErrors), - [](const ErrorDiagnostic &E) { E.log(errs()); }); - - // No problem matching the string per se. - if (!MatchErrors) - return; - consumeError(std::move(MatchErrors)); + if (!PrintDiag) + return HasError; // Print "not found" diagnostic. std::string Message = formatv("{0}: {1} string not found in input", @@ -2116,15 +2124,16 @@ if (ExpectedMatch) Pat.printFuzzyMatch(SM, Buffer, Diags); + return HasError; } -static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, +static bool PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, StringRef Buffer, bool VerboseVerbose, std::vector *Diags, Error MatchErrors) { - PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VerboseVerbose, Diags, - std::move(MatchErrors)); + return PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, + CheckStr.Pat, MatchedCount, Buffer, VerboseVerbose, Diags, + std::move(MatchErrors)); } /// Counts the number of newlines in the specified range. @@ -2305,8 +2314,9 @@ Expected MatchResult = Pat->match(Buffer, MatchLen, SM); if (!MatchResult) { - PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, - Req.VerboseVerbose, Diags, MatchResult.takeError()); + if (PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, + Req.VerboseVerbose, Diags, MatchResult.takeError())) + DirectiveFail = true; continue; } size_t Pos = *MatchResult; Index: llvm/lib/FileCheck/FileCheckImpl.h =================================================================== --- llvm/lib/FileCheck/FileCheckImpl.h +++ llvm/lib/FileCheck/FileCheckImpl.h @@ -546,6 +546,8 @@ /// Print diagnostic associated with this error when printing the error. void log(raw_ostream &OS) const override { Diagnostic.print(nullptr, OS); } + StringRef getMessage() const { return Diagnostic.getMessage(); } + static Error get(const SourceMgr &SM, SMLoc Loc, const Twine &ErrMsg) { return make_error( SM.GetMessage(Loc, SourceMgr::DK_Error, ErrMsg)); Index: llvm/test/FileCheck/check-not-numeric-error.txt =================================================================== --- /dev/null +++ llvm/test/FileCheck/check-not-numeric-error.txt @@ -0,0 +1,74 @@ +; At one time, FileCheck's exit status was zero despite overflow in CHECK-NOT +; and --implicit-check-not. Moreover, it only printed the error diagnostic if +; -vv was specified and input dumps were disabled. Test every combination as +; the logic is hard to get right. +; +; FIXME: We shouldn't have: (1) the blank note at the end of the trace, and +; (2) the redundant error at the end of the dump. These will be fixed in a +; subsequent patch. + +RUN: echo 'CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]]' > %t.chk +RUN: echo '10000000000000000' > %t.in + + ERR-NOT:{{.}} + ERR-VV:{{.*}}: remark: implicit EOF: expected string found in input + ERR-VV-NEXT:CHECK-NOT: {{.*}} + ERR-VV-NEXT:{{ *}}^ + ERR-VV-NEXT:{{.*}}: note: found here +ERR-VV-EMPTY: + ERR-VV-NEXT:^ + ERR-NOT:{{.}} + ERR:{{.*}}: error: unable to substitute variable or numeric expression: overflow error + ERR-NEXT:CHECK-NOT: {{.*}} + ERR-NEXT:{{ *}}^ + ERR-NEXT:{{.*}}: remark: CHECK-NOT: excluded string not found in input + ERR-NEXT:CHECK-NOT: {{.*}} + ERR-NEXT:{{ *}}^ + ERR-NEXT:{{.*}}: note: scanning from here + ERR-NEXT:10000000000000000 + ERR-NEXT:^ + ERR-NEXT:{{.*}}: note: + ERR-NEXT:10000000000000000 + ERR-NEXT:^ + ERR-NOT:{{error|note|remark}}: + + DUMP:<<<<<< + DUMP-NEXT: 1: 10000000000000000 + DUMP-NEXT:not:1'0 X~~~~~~~~~~~~~~~~~ error: match failed for invalid pattern + DUMP-NEXT:not:1'1 unable to substitute variable or numeric expression: overflow error + DUMP-NEXT:not:1'2 X error: match failed for invalid pattern +DUMP-VV-NEXT: 2: +DUMP-VV-NEXT:eof:1 ^ + DUMP-NEXT:>>>>>> + +;-------------------------------------------------- +; Check -dump-input=never cases. +;-------------------------------------------------- + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=never %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=never -v %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=never -vv %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,ERR-VV + +;-------------------------------------------------- +; Check -dump-input=fail cases. +;-------------------------------------------------- + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=fail %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=fail -v %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ +RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP,DUMP-VV Index: llvm/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/utils/FileCheck/FileCheck.cpp +++ llvm/utils/FileCheck/FileCheck.cpp @@ -217,6 +217,10 @@ case FileCheckDiag::MatchNoneButExpected: return MarkerStyle('X', raw_ostream::RED, "error: no match found", /*FiltersAsError=*/true); + case FileCheckDiag::MatchNoneForInvalidPattern: + return MarkerStyle('X', raw_ostream::RED, + "error: match failed for invalid pattern", + /*FiltersAsError=*/true); case FileCheckDiag::MatchFuzzy: return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match", /*FiltersAsError=*/true);