aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/FileCheck/FileCheck.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
commit344a3780b2e33f6ca763666c380202b18aab72a3 (patch)
treef0b203ee6eb71d7fdd792373e3c81eb18d6934dd /llvm/lib/FileCheck/FileCheck.cpp
parentb60736ec1405bb0a8dd40989f67ef4c93da068ab (diff)
Diffstat (limited to 'llvm/lib/FileCheck/FileCheck.cpp')
-rw-r--r--llvm/lib/FileCheck/FileCheck.cpp437
1 files changed, 256 insertions, 181 deletions
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index 3169afaed58c..04476d999336 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -45,8 +45,12 @@ StringRef ExpressionFormat::toString() const {
}
Expected<std::string> ExpressionFormat::getWildcardRegex() const {
- auto CreatePrecisionRegex = [this](StringRef S) {
- return (S + Twine('{') + Twine(Precision) + "}").str();
+ StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();
+
+ auto CreatePrecisionRegex = [&](StringRef S) {
+ return (Twine(AlternateFormPrefix) + S + Twine('{') + Twine(Precision) +
+ "}")
+ .str();
};
switch (Value) {
@@ -61,11 +65,11 @@ Expected<std::string> ExpressionFormat::getWildcardRegex() const {
case Kind::HexUpper:
if (Precision)
return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]");
- return std::string("[0-9A-F]+");
+ return (Twine(AlternateFormPrefix) + Twine("[0-9A-F]+")).str();
case Kind::HexLower:
if (Precision)
return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]");
- return std::string("[0-9a-f]+");
+ return (Twine(AlternateFormPrefix) + Twine("[0-9a-f]+")).str();
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
@@ -107,34 +111,48 @@ ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const {
"trying to match value with invalid format");
}
+ StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();
+
if (Precision > AbsoluteValueStr.size()) {
unsigned LeadingZeros = Precision - AbsoluteValueStr.size();
- return (Twine(SignPrefix) + std::string(LeadingZeros, '0') +
- AbsoluteValueStr)
+ return (Twine(SignPrefix) + Twine(AlternateFormPrefix) +
+ std::string(LeadingZeros, '0') + AbsoluteValueStr)
.str();
}
- return (Twine(SignPrefix) + AbsoluteValueStr).str();
+ return (Twine(SignPrefix) + Twine(AlternateFormPrefix) + AbsoluteValueStr)
+ .str();
}
Expected<ExpressionValue>
ExpressionFormat::valueFromStringRepr(StringRef StrVal,
const SourceMgr &SM) const {
bool ValueIsSigned = Value == Kind::Signed;
- StringRef OverflowErrorStr = "unable to represent numeric value";
+ // Both the FileCheck utility and library only call this method with a valid
+ // value in StrVal. This is guaranteed by the regex returned by
+ // getWildcardRegex() above. Only underflow and overflow errors can thus
+ // occur. However new uses of this method could be added in the future so
+ // the error message does not make assumptions about StrVal.
+ StringRef IntegerParseErrorStr = "unable to represent numeric value";
if (ValueIsSigned) {
int64_t SignedValue;
if (StrVal.getAsInteger(10, SignedValue))
- return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
+ return ErrorDiagnostic::get(SM, StrVal, IntegerParseErrorStr);
return ExpressionValue(SignedValue);
}
bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
uint64_t UnsignedValue;
+ bool MissingFormPrefix = AlternateForm && !StrVal.consume_front("0x");
if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue))
- return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
+ return ErrorDiagnostic::get(SM, StrVal, IntegerParseErrorStr);
+
+ // Error out for a missing prefix only now that we know we have an otherwise
+ // valid integer. For example, "-0x18" is reported above instead.
+ if (MissingFormPrefix)
+ return ErrorDiagnostic::get(SM, StrVal, "missing alternate form prefix");
return ExpressionValue(UnsignedValue);
}
@@ -458,6 +476,7 @@ char OverflowError::ID = 0;
char UndefVarError::ID = 0;
char ErrorDiagnostic::ID = 0;
char NotFoundError::ID = 0;
+char ErrorReported::ID = 0;
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
@@ -512,7 +531,7 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
// we get below is null, it means no such variable was defined before. When
// that happens, we create a dummy variable so that parsing can continue. All
// uses of undefined variables, whether string or numeric, are then diagnosed
- // in printSubstitutions() after failing to match.
+ // in printNoMatch() after failing to match.
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
NumericVariable *NumericVariable;
if (VarTableIter != Context->GlobalNumericVariableTable.end())
@@ -767,6 +786,10 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
SM, FormatExpr,
"invalid matching format specification in expression");
+ // Parse alternate form flag.
+ SMLoc AlternateFormFlagLoc = SMLoc::getFromPointer(FormatExpr.data());
+ bool AlternateForm = FormatExpr.consume_front("#");
+
// Parse precision.
if (FormatExpr.consume_front(".")) {
if (FormatExpr.consumeInteger(10, Precision))
@@ -788,12 +811,12 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
ExpressionFormat(ExpressionFormat::Kind::Signed, Precision);
break;
case 'x':
- ExplicitFormat =
- ExpressionFormat(ExpressionFormat::Kind::HexLower, Precision);
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower,
+ Precision, AlternateForm);
break;
case 'X':
- ExplicitFormat =
- ExpressionFormat(ExpressionFormat::Kind::HexUpper, Precision);
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper,
+ Precision, AlternateForm);
break;
default:
return ErrorDiagnostic::get(SM, FmtLoc,
@@ -801,6 +824,12 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
}
}
+ if (AlternateForm && ExplicitFormat != ExpressionFormat::Kind::HexLower &&
+ ExplicitFormat != ExpressionFormat::Kind::HexUpper)
+ return ErrorDiagnostic::get(
+ SM, AlternateFormFlagLoc,
+ "alternate form only supported for hex values");
+
FormatExpr = FormatExpr.ltrim(SpaceChars);
if (!FormatExpr.empty())
return ErrorDiagnostic::get(
@@ -1054,8 +1083,15 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
if (IsPseudo) {
MatchStr = OrigMatchStr;
IsLegacyLineExpr = IsNumBlock = true;
- } else
+ } else {
+ if (!MatchStr.empty()) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
+ SourceMgr::DK_Error,
+ "invalid name in string variable use");
+ return true;
+ }
SubstStr = Name;
+ }
}
}
@@ -1184,22 +1220,19 @@ void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {
RegExStr += Backref;
}
-Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
- const SourceMgr &SM) const {
+Pattern::MatchResult Pattern::match(StringRef Buffer,
+ const SourceMgr &SM) const {
// If this is the EOF pattern, match it immediately.
- if (CheckTy == Check::CheckEOF) {
- MatchLen = 0;
- return Buffer.size();
- }
+ if (CheckTy == Check::CheckEOF)
+ return MatchResult(Buffer.size(), 0, Error::success());
// If this is a fixed string pattern, just match it now.
if (!FixedStr.empty()) {
- MatchLen = FixedStr.size();
size_t Pos =
- IgnoreCase ? Buffer.find_lower(FixedStr) : Buffer.find(FixedStr);
+ IgnoreCase ? Buffer.find_insensitive(FixedStr) : Buffer.find(FixedStr);
if (Pos == StringRef::npos)
return make_error<NotFoundError>();
- return Pos;
+ return MatchResult(Pos, /*MatchLen=*/FixedStr.size(), Error::success());
}
// Regex match.
@@ -1217,20 +1250,28 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
// Substitute all string variables and expressions whose values are only
// now known. Use of string variables defined on the same line are handled
// by back-references.
+ Error Errs = Error::success();
for (const auto &Substitution : Substitutions) {
// Substitute and check for failure (e.g. use of undefined variable).
Expected<std::string> 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 printMatch/printNoMatch since now we know which
// substitution block caused the overflow.
- Error Err =
- handleErrors(Value.takeError(), [&](const OverflowError &E) {
- return ErrorDiagnostic::get(SM, Substitution->getFromString(),
- "unable to substitute variable or "
- "numeric expression: overflow error");
- });
- return std::move(Err);
+ Errs = joinErrors(std::move(Errs),
+ handleErrors(
+ Value.takeError(),
+ [&](const OverflowError &E) {
+ return ErrorDiagnostic::get(
+ SM, Substitution->getFromString(),
+ "unable to substitute variable or "
+ "numeric expression: overflow error");
+ },
+ [&SM](const UndefVarError &E) {
+ return ErrorDiagnostic::get(SM, E.getVarName(),
+ E.message());
+ }));
+ continue;
}
// Plop it into the regex at the adjusted offset.
@@ -1238,6 +1279,8 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
Value->begin(), Value->end());
InsertOffset += Value->size();
}
+ if (Errs)
+ return std::move(Errs);
// Match the newly constructed regex.
RegExToMatch = TmpStr;
@@ -1261,6 +1304,14 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
MatchInfo[VariableDef.second];
}
+ // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
+ // the required preceding newline, which is consumed by the pattern in the
+ // case of CHECK-EMPTY but not CHECK-NEXT.
+ size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
+ Match TheMatch;
+ TheMatch.Pos = FullMatch.data() - Buffer.data() + MatchStartSkip;
+ TheMatch.Len = FullMatch.size() - MatchStartSkip;
+
// If this defines any numeric variables, remember their values.
for (const auto &NumericVariableDef : NumericVariableDefs) {
const NumericVariableMatch &NumericVariableMatch =
@@ -1275,16 +1326,11 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
Expected<ExpressionValue> Value =
Format.valueFromStringRepr(MatchedValue, SM);
if (!Value)
- return Value.takeError();
+ return MatchResult(TheMatch, Value.takeError());
DefinedNumericVariable->setValue(*Value, MatchedValue);
}
- // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
- // the required preceding newline, which is consumed by the pattern in the
- // case of CHECK-EMPTY but not CHECK-NEXT.
- size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
- MatchLen = FullMatch.size() - MatchStartSkip;
- return FullMatch.data() - Buffer.data() + MatchStartSkip;
+ return MatchResult(TheMatch, Error::success());
}
unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
@@ -1313,33 +1359,18 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
for (const auto &Substitution : Substitutions) {
SmallString<256> Msg;
raw_svector_ostream OS(Msg);
- Expected<std::string> MatchedValue = Substitution->getResult();
- // Substitution failed or is not known at match time, print the undefined
- // variables it uses.
+ Expected<std::string> MatchedValue = Substitution->getResult();
+ // Substitution failures are handled in printNoMatch().
if (!MatchedValue) {
- bool UndefSeen = false;
- handleAllErrors(
- MatchedValue.takeError(), [](const NotFoundError &E) {},
- // Handled in PrintNoMatch().
- [](const ErrorDiagnostic &E) {},
- // Handled in match().
- [](const OverflowError &E) {},
- [&](const UndefVarError &E) {
- if (!UndefSeen) {
- OS << "uses undefined variable(s):";
- UndefSeen = true;
- }
- OS << " ";
- E.log(OS);
- });
- } else {
- // Substitution succeeded. Print substituted value.
- OS << "with \"";
- OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
- OS.write_escaped(*MatchedValue) << "\"";
+ consumeError(MatchedValue.takeError());
+ continue;
}
+ OS << "with \"";
+ OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
+ OS.write_escaped(*MatchedValue) << "\"";
+
// We report only the start of the match/search range to suggest we are
// reporting the substitutions as set at the start of the match/search.
// Indicating a non-zero-length range might instead seem to imply that the
@@ -1376,11 +1407,12 @@ void Pattern::printVariableDefs(const SourceMgr &SM,
for (const auto &VariableDef : NumericVariableDefs) {
VarCapture VC;
VC.Name = VariableDef.getKey();
- StringRef StrValue = VariableDef.getValue()
- .DefinedNumericVariable->getStringValue()
- .getValue();
- SMLoc Start = SMLoc::getFromPointer(StrValue.data());
- SMLoc End = SMLoc::getFromPointer(StrValue.data() + StrValue.size());
+ Optional<StringRef> StrValue =
+ VariableDef.getValue().DefinedNumericVariable->getStringValue();
+ if (!StrValue)
+ continue;
+ SMLoc Start = SMLoc::getFromPointer(StrValue->data());
+ SMLoc End = SMLoc::getFromPointer(StrValue->data() + StrValue->size());
VC.Range = SMRange(Start, End);
VarCaptures.push_back(VC);
}
@@ -2008,126 +2040,173 @@ bool FileCheck::readCheckFile(
return false;
}
-static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
- StringRef Prefix, SMLoc Loc, const Pattern &Pat,
- int MatchedCount, StringRef Buffer, size_t MatchPos,
- size_t MatchLen, const FileCheckRequest &Req,
- std::vector<FileCheckDiag> *Diags) {
+/// Returns either (1) \c ErrorSuccess if there was no error or (2)
+/// \c ErrorReported if an error was reported, such as an unexpected match.
+static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
+ StringRef Prefix, SMLoc Loc, const Pattern &Pat,
+ int MatchedCount, StringRef Buffer,
+ Pattern::MatchResult MatchResult,
+ const FileCheckRequest &Req,
+ std::vector<FileCheckDiag> *Diags) {
+ // Suppress some verbosity if there's no error.
+ bool HasError = !ExpectedMatch || MatchResult.TheError;
bool PrintDiag = true;
- if (ExpectedMatch) {
+ if (!HasError) {
if (!Req.Verbose)
- return;
+ return ErrorReported::reportedOrSuccess(HasError);
if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
- return;
+ return ErrorReported::reportedOrSuccess(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.
+ // gathering them for Diags to be rendered elsewhere, but we always print
+ // other diagnostics.
PrintDiag = !Diags;
}
+
+ // Add "found" diagnostic, substitutions, and variable definitions to Diags.
FileCheckDiag::MatchType MatchTy = ExpectedMatch
? FileCheckDiag::MatchFoundAndExpected
: FileCheckDiag::MatchFoundButExcluded;
SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
- Buffer, MatchPos, MatchLen, Diags);
+ Buffer, MatchResult.TheMatch->Pos,
+ MatchResult.TheMatch->Len, Diags);
if (Diags) {
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
Pat.printVariableDefs(SM, MatchTy, Diags);
}
- if (!PrintDiag)
- return;
+ if (!PrintDiag) {
+ assert(!HasError && "expected to report more diagnostics for error");
+ return ErrorReported::reportedOrSuccess(HasError);
+ }
+ // Print the match.
std::string Message = formatv("{0}: {1} string 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_Remark : SourceMgr::DK_Error, Message);
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
{MatchRange});
+
+ // Print additional information, which can be useful even if there are errors.
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);
Pat.printVariableDefs(SM, MatchTy, nullptr);
-}
-static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
- const FileCheckString &CheckStr, int MatchedCount,
- StringRef Buffer, size_t MatchPos, size_t MatchLen,
- FileCheckRequest &Req,
- std::vector<FileCheckDiag> *Diags) {
- PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
- MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags);
-}
-
-static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
- StringRef Prefix, SMLoc Loc, const Pattern &Pat,
- int MatchedCount, StringRef Buffer,
- bool VerboseVerbose, std::vector<FileCheckDiag> *Diags,
- Error MatchErrors) {
- assert(MatchErrors && "Called on successful match");
+ // Print errors and add them to Diags. We report these errors after the match
+ // itself because we found them after the match. If we had found them before
+ // the match, we'd be in printNoMatch.
+ handleAllErrors(std::move(MatchResult.TheError),
+ [&](const ErrorDiagnostic &E) {
+ E.log(errs());
+ if (Diags) {
+ Diags->emplace_back(SM, Pat.getCheckTy(), Loc,
+ FileCheckDiag::MatchFoundErrorNote,
+ E.getRange(), E.getMessage().str());
+ }
+ });
+ return ErrorReported::reportedOrSuccess(HasError);
+}
+
+/// Returns either (1) \c ErrorSuccess if there was no error, or (2)
+/// \c ErrorReported if an error was reported, such as an expected match not
+/// found.
+static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
+ StringRef Prefix, SMLoc Loc, const Pattern &Pat,
+ int MatchedCount, StringRef Buffer, Error MatchError,
+ bool VerboseVerbose,
+ std::vector<FileCheckDiag> *Diags) {
+ // Print any pattern errors, and record them to be added to Diags later.
+ bool HasError = ExpectedMatch;
+ bool HasPatternError = false;
+ FileCheckDiag::MatchType MatchTy = ExpectedMatch
+ ? FileCheckDiag::MatchNoneButExpected
+ : FileCheckDiag::MatchNoneAndExcluded;
+ SmallVector<std::string, 4> ErrorMsgs;
+ handleAllErrors(
+ std::move(MatchError),
+ [&](const ErrorDiagnostic &E) {
+ HasError = HasPatternError = true;
+ MatchTy = FileCheckDiag::MatchNoneForInvalidPattern;
+ E.log(errs());
+ if (Diags)
+ ErrorMsgs.push_back(E.getMessage().str());
+ },
+ // NotFoundError is why printNoMatch was invoked.
+ [](const NotFoundError &E) {});
+
+ // 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 ErrorReported::reportedOrSuccess(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.
+ // gathering them for Diags to be rendered elsewhere, but we always print
+ // other diagnostics.
PrintDiag = !Diags;
}
- // 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;
+ // Add "not found" diagnostic, substitutions, and pattern errors to Diags.
+ //
+ // We handle Diags a little differently than the errors we print directly:
+ // we add the "not found" diagnostic to Diags even if there are pattern
+ // errors. The reason is that we need to attach pattern errors as notes
+ // somewhere in the input, and the input search range from the "not found"
+ // diagnostic is all we have to anchor them.
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 (StringRef ErrorMsg : ErrorMsgs)
+ Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange,
+ ErrorMsg);
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));
-
- // 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");
-
- // Allow the pattern to print additional information if desired.
+ if (!PrintDiag) {
+ assert(!HasError && "expected to report more diagnostics for error");
+ return ErrorReported::reportedOrSuccess(HasError);
+ }
+
+ // Print "not found" diagnostic, except that's implied if we already printed a
+ // pattern error.
+ if (!HasPatternError) {
+ 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);
+ SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note,
+ "scanning from here");
+ }
+
+ // Print additional information, which can be useful even after a pattern
+ // error.
Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr);
-
if (ExpectedMatch)
Pat.printFuzzyMatch(SM, Buffer, Diags);
+ return ErrorReported::reportedOrSuccess(HasError);
}
-static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
- const FileCheckString &CheckStr, int MatchedCount,
- StringRef Buffer, bool VerboseVerbose,
- std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
- PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
- MatchedCount, Buffer, VerboseVerbose, Diags,
- std::move(MatchErrors));
+/// Returns either (1) \c ErrorSuccess if there was no error, or (2)
+/// \c ErrorReported if an error was reported.
+static Error reportMatchResult(bool ExpectedMatch, const SourceMgr &SM,
+ StringRef Prefix, SMLoc Loc, const Pattern &Pat,
+ int MatchedCount, StringRef Buffer,
+ Pattern::MatchResult MatchResult,
+ const FileCheckRequest &Req,
+ std::vector<FileCheckDiag> *Diags) {
+ if (MatchResult.TheMatch)
+ return printMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
+ std::move(MatchResult), Req, Diags);
+ return printNoMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
+ std::move(MatchResult.TheError), Req.VerboseVerbose,
+ Diags);
}
/// Counts the number of newlines in the specified range.
@@ -2179,24 +2258,23 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
assert(Pat.getCount() != 0 && "pattern count can not be zero");
for (int i = 1; i <= Pat.getCount(); i++) {
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
- size_t CurrentMatchLen;
// get a match at current start point
- Expected<size_t> MatchResult = Pat.match(MatchBuffer, CurrentMatchLen, SM);
+ Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);
// report
- if (!MatchResult) {
- PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags,
- MatchResult.takeError());
+ if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix, Loc,
+ Pat, i, MatchBuffer,
+ std::move(MatchResult), Req, Diags)) {
+ cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
return StringRef::npos;
}
- size_t MatchPos = *MatchResult;
- PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
- Diags);
+
+ size_t MatchPos = MatchResult.TheMatch->Pos;
if (i == 1)
FirstMatchPos = LastPos + MatchPos;
// move start point after the match
- LastMatchEnd += MatchPos + CurrentMatchLen;
+ LastMatchEnd += MatchPos + MatchResult.TheMatch->Len;
}
// Full match len counts from first match pos.
MatchLen = LastMatchEnd - FirstMatchPos;
@@ -2303,22 +2381,15 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
bool DirectiveFail = false;
for (const Pattern *Pat : NotStrings) {
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
-
- size_t MatchLen = 0;
- Expected<size_t> MatchResult = Pat->match(Buffer, MatchLen, SM);
-
- if (!MatchResult) {
- PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
- Req.VerboseVerbose, Diags, MatchResult.takeError());
+ Pattern::MatchResult MatchResult = Pat->match(Buffer, SM);
+ if (Error Err = reportMatchResult(/*ExpectedMatch=*/false, SM, Prefix,
+ Pat->getLoc(), *Pat, 1, Buffer,
+ std::move(MatchResult), Req, Diags)) {
+ cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
+ DirectiveFail = true;
continue;
}
- size_t Pos = *MatchResult;
-
- PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
- Req, Diags);
- DirectiveFail = true;
}
-
return DirectiveFail;
}
@@ -2364,20 +2435,22 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// CHECK-DAG group.
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
StringRef MatchBuffer = Buffer.substr(MatchPos);
- Expected<size_t> MatchResult = Pat.match(MatchBuffer, MatchLen, SM);
+ Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);
// 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,
- Req.VerboseVerbose, Diags, MatchResult.takeError());
- return StringRef::npos;
+ if (MatchResult.TheError || Req.VerboseVerbose) {
+ if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix,
+ Pat.getLoc(), Pat, 1, MatchBuffer,
+ std::move(MatchResult), Req, Diags)) {
+ cantFail(
+ handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
+ return StringRef::npos;
+ }
}
- size_t MatchPosBuf = *MatchResult;
- // Re-calc it as the offset relative to the start of the original string.
- MatchPos += MatchPosBuf;
- if (Req.VerboseVerbose)
- PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
- MatchLen, Req, Diags);
+ MatchLen = MatchResult.TheMatch->Len;
+ // Re-calc it as the offset relative to the start of the original
+ // string.
+ MatchPos += MatchResult.TheMatch->Pos;
MatchRange M{MatchPos, MatchPos + MatchLen};
if (Req.AllowDeprecatedDagOverlap) {
// We don't need to track all matches in this mode, so we just maintain
@@ -2428,8 +2501,10 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
MatchPos = MI->End;
}
if (!Req.VerboseVerbose)
- PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
- MatchLen, Req, Diags);
+ cantFail(printMatch(
+ /*ExpectedMatch=*/true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer,
+ Pattern::MatchResult(MatchPos, MatchLen, Error::success()), Req,
+ Diags));
// Handle the end of a CHECK-DAG group.
if (std::next(PatItr) == PatEnd ||