diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -103,6 +103,8 @@ /// (MatchFoundAndExpected) or a failure to match (MatchNoneButExpected), /// and there might be a fuzzy match (MatchFuzzy) after the latter. enum MatchType { + /// Indicates an error during match. + MatchError, /// Indicates a good match for an expected pattern. MatchFoundAndExpected, /// Indicates a match for an excluded pattern. diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -1165,7 +1165,7 @@ Expected Value = Substitution->getResult(); if (!Value) { // Convert to an ErrorDiagnostic to get location information. This is - // done here rather than PrintNoMatch since now we know which + // done here rather than printNoMatch since now we know which // substitution block caused the overflow. Error Err = handleErrors(Value.takeError(), [&](const OverflowError &E) { @@ -1256,6 +1256,7 @@ for (const auto &Substitution : Substitutions) { SmallString<256> Msg; raw_svector_ostream OS(Msg); + SourceMgr::DiagKind Kind; Expected MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined @@ -1264,13 +1265,14 @@ bool UndefSeen = false; handleAllErrors( MatchedValue.takeError(), [](const NotFoundError &E) {}, - // Handled in PrintNoMatch(). + // Handled in printNoMatch(). [](const ErrorDiagnostic &E) {}, // Handled in match(). [](const OverflowError &E) {}, [&](const UndefVarError &E) { if (!UndefSeen) { OS << "uses undefined variable(s):"; + Kind = SourceMgr::DK_Error; UndefSeen = true; } OS << " "; @@ -1278,6 +1280,7 @@ }); } else { // Substitution succeeded. Print substituted value. + Kind = SourceMgr::DK_Note; OS << "with \""; OS.write_escaped(Substitution->getFromString()) << "\" equal to \""; OS.write_escaped(*MatchedValue) << "\""; @@ -1291,7 +1294,7 @@ Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy, SMRange(Range.Start, Range.Start), OS.str()); else - SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str()); + SM.PrintMessage(Range.Start, Kind, OS.str()); } } } @@ -1957,17 +1960,25 @@ MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags); } -static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, +/// Print and record diagnostics about the failure to match the input. \returns +/// true if the failure was due to a match error. +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) { assert(MatchErrors && "Called on successful match"); bool PrintDiag = true; - if (!ExpectedMatch) { + bool UndefVar = false; + + MatchErrors = + handleErrors(std::move(MatchErrors), + [&](std::unique_ptr E) { UndefVar = true; }); + + if (!ExpectedMatch && !UndefVar) { if (!VerboseVerbose) { consumeError(std::move(MatchErrors)); - return; + return false; } // 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 @@ -1978,51 +1989,62 @@ // If the current position is at the end of a line, advance to the start of // the next line. Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r")); - FileCheckDiag::MatchType MatchTy = ExpectedMatch - ? FileCheckDiag::MatchNoneButExpected - : FileCheckDiag::MatchNoneAndExcluded; + FileCheckDiag::MatchType MatchTy; + if (UndefVar) + MatchTy = FileCheckDiag::MatchError; + else if (ExpectedMatch) + MatchTy = FileCheckDiag::MatchNoneButExpected; + else + MatchTy = FileCheckDiag::MatchNoneAndExcluded; SMRange SearchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags); if (Diags) Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags); if (!PrintDiag) { consumeError(std::move(MatchErrors)); - return; + return UndefVar; } MatchErrors = handleErrors(std::move(MatchErrors), [](const ErrorDiagnostic &E) { E.log(errs()); }); // No problem matching the string per se. - if (!MatchErrors) - return; + if (!MatchErrors && !UndefVar) + return false; consumeError(std::move(MatchErrors)); - // Print "not found" diagnostic. - std::string Message = formatv("{0}: {1} string not found in input", - Pat.getCheckTy().getDescription(Prefix), - (ExpectedMatch ? "expected" : "excluded")) - .str(); - if (Pat.getCount() > 1) - Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str(); - SM.PrintMessage( - Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message); + if (!UndefVar) { + // Print "not found" diagnostic. + std::string Message = formatv("{0}: {1} string not found in input", + Pat.getCheckTy().getDescription(Prefix), + (ExpectedMatch ? "expected" : "excluded")) + .str(); + if (Pat.getCount() > 1) + Message += + formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str(); + SM.PrintMessage(Loc, + ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, + Message); - // Print the "scanning from here" line. - SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here"); + // Print the "scanning from here" line. + SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, + "scanning from here"); + } // Allow the pattern to print additional information if desired. Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr); - if (ExpectedMatch) + if (ExpectedMatch && !UndefVar) Pat.printFuzzyMatch(SM, Buffer, Diags); + + return UndefVar; } static void 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, + printNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, MatchedCount, Buffer, VerboseVerbose, Diags, std::move(MatchErrors)); } @@ -2197,6 +2219,7 @@ const std::vector &NotStrings, const FileCheckRequest &Req, std::vector *Diags) const { + bool DirectiveFail = false; for (const Pattern *Pat : NotStrings) { assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); @@ -2204,19 +2227,20 @@ 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; PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen, Req, Diags); - - return true; + DirectiveFail = true; + continue; } - return false; + return DirectiveFail; } size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, @@ -2265,7 +2289,7 @@ // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (!MatchResult) { - PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer, + printNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer, Req.VerboseVerbose, Diags, MatchResult.takeError()); return StringRef::npos; } diff --git a/llvm/test/FileCheck/dump-input-annotations.txt b/llvm/test/FileCheck/dump-input-annotations.txt --- a/llvm/test/FileCheck/dump-input-annotations.txt +++ b/llvm/test/FileCheck/dump-input-annotations.txt @@ -622,11 +622,10 @@ ; SUBST-POS-NEXT:check:1'1 with "DEF_MATCH1" equal to "def-match1" ; SUBST-POS-NEXT:check:1'2 with "DEF_MATCH2" equal to "def-match2" ; SUBST-POS-NEXT: 2: def-match1 def-nomatch -; SUBST-POS-NEXT:check:2'0 X~~~~~~~~~~~~~~~~~~~~~ error: no match found +; SUBST-POS-NEXT:check:2'0 X~~~~~~~~~~~~~~~~~~~~~ error: match error ; SUBST-POS-NEXT:check:2'1 with "DEF_MATCH1" equal to "def-match1" ; SUBST-POS-NEXT:check:2'2 uses undefined variable(s): "UNDEF" ; SUBST-POS-NEXT:check:2'3 with "DEF_NOMATCH" equal to "foobar" -; SUBST-POS-NEXT:check:2'4 ? possible intended match ; SUBST-POS-NEXT:>>>>>> ;-------------------------------------------------- @@ -654,7 +653,7 @@ ; SUBST-NEG:<<<<<< ; SUBST-NEG-NEXT: 1: def-match1 def-nomatch -; SUBST-NEG-NEXT:not:1'0 X~~~~~~~~~~~~~~~~~~~~~ +; SUBST-NEG-NEXT:not:1'0 X~~~~~~~~~~~~~~~~~~~~~ error: match error ; SUBST-NEG-NEXT:not:1'1 with "DEF_MATCH1" equal to "def-match1" ; SUBST-NEG-NEXT:not:1'2 uses undefined variable(s): "UNDEF" ; SUBST-NEG-NEXT:not:1'3 with "DEF_NOMATCH" equal to "foobar" diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -165,7 +165,7 @@ ; Numeric expressions in default matching format and explicit matching rule using ; variable defined on other lines with match failure. RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH --input-file %s %s 2>&1 \ +RUN: not FileCheck --check-prefixes CHECK,NUMEXPR-CONSTRAINT-NOMATCH --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH-MSG --strict-whitespace %s USE DEF FMT EXPL NO MATCH @@ -282,16 +282,24 @@ UNDEFVAR: 11 UNDEF-USE-LABEL: UNDEF VAR USE UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]] -UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:17: error: {{U}}NDEF-USE-NEXT: expected string not found in input -UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} -UNDEF-USE-MSG-NEXT: {{^}} ^{{$}} -UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-6]]:1: note: scanning from here -UNDEF-USE-MSG-NEXT: UNDEFVAR: 11 -UNDEF-USE-MSG-NEXT: {{^}}^{{$}} -UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2" +UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-3]]:1: error: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2" UNDEF-USE-MSG-NEXT: UNDEFVAR: 11 UNDEF-USE-MSG-NEXT: {{^}}^{{$}} +; Numeric expression in negative directive using undefined variables. +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck --check-prefix UNDEF-USE2 --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG2 %s + +CHECK NOT UNDEF VAR USE +END MARKER +UNDEF-USE2-LABEL: CHECK NOT UNDEF VAR USE +UNDEF-USE2-NOT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]] +UNDEF-USE2: END MARKER +UNDEF-USE-MSG2: numeric-expression.txt:[[#@LINE-4]]:1: error: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2" +UNDEF-USE-MSG2-NEXT: END MARKER +UNDEF-USE-MSG2-NEXT: {{^}}^{{$}} + ; Numeric expression with unsupported operator. RUN: %ProtectFileCheckOutput \ RUN: not FileCheck -D#NUMVAR=10 --check-prefix INVAL-OP \ diff --git a/llvm/test/FileCheck/var-scope.txt b/llvm/test/FileCheck/var-scope.txt --- a/llvm/test/FileCheck/var-scope.txt +++ b/llvm/test/FileCheck/var-scope.txt @@ -7,11 +7,11 @@ RUN: FileCheck --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s RUN: %ProtectFileCheckOutput not FileCheck --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \ -RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s +RUN: | FileCheck --check-prefix ERRUNDEFLOCAL %s RUN: %ProtectFileCheckOutput not FileCheck --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \ -RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s +RUN: | FileCheck --check-prefix ERRUNDEFLOCNUM %s RUN: %ProtectFileCheckOutput not FileCheck --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \ -RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s +RUN: | FileCheck --check-prefixes ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s local1 global1 @@ -33,6 +33,5 @@ LOCAL3: [[LOCAL]][[#LOCNUM+2]] GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]] -ERRUNDEF: expected string not found in input ERRUNDEFLOCAL: uses undefined variable(s): "LOCAL" ERRUNDEFLOCNUM: uses undefined variable(s): "LOCNUM" diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -197,6 +197,9 @@ static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { switch (MatchTy) { + case FileCheckDiag::MatchError: + return MarkerStyle('X', raw_ostream::RED, "error: match error", + /*FiltersAsError=*/true); case FileCheckDiag::MatchFoundAndExpected: return MarkerStyle('^', raw_ostream::GREEN); case FileCheckDiag::MatchFoundButExcluded: