mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
[FileCheck] Fix numeric error propagation
A more general name might be match-time error propagation. That is, it's conceivable we'll one day have non-numeric errors that require the handling fixed by this patch. Without this patch, FileCheck behaves as follows: ``` $ cat check CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] $ FileCheck -vv -dump-input=never check < input check:1:54: remark: implicit EOF: expected string found in input CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] ^ <stdin>:2:1: note: found here ^ check:1:15: error: unable to substitute variable or numeric expression: overflow error CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] ^ $ echo $? 0 ``` Notice that the exit status is 0 even though there's an error. Moreover, FileCheck doesn't print the error diagnostic unless both `-dump-input=never` and `-vv` are specified. The same problem occurs when `CHECK-NOT` does have a match but a capture fails due to overflow: exit status is 0, and no diagnostic is printed unless both `-dump-input=never` and `-vv` are specified. The usefulness of capturing from `CHECK-NOT` is questionable, but this case should certainly produce an error. With this patch, FileCheck always includes the error diagnostic and has non-zero exit status for the above examples. It's conceivable that this change will cause some existing tests to fail, but my assumption is that they should fail. Moreover, with nearly every project enabled, this patch didn't produce additional `check-all` failures for me. This patch also extends input dumps to include such numeric error diagnostics for both expected and excluded patterns. As noted in fixmes in some of the tests added by this patch, this patch worsens an existing issue with redundant diagnostics. I'll fix that bug in a subsequent patch. Reviewed By: thopre, jhenderson Differential Revision: https://reviews.llvm.org/D98086
This commit is contained in:
parent
1bc9df98e9
commit
20f8c79d86
@ -118,8 +118,6 @@ struct FileCheckDiag {
|
|||||||
/// depending on whether the pattern must have or must not have a match in
|
/// depending on whether the pattern must have or must not have a match in
|
||||||
/// order for the directive to succeed. For example, a CHECK directive's
|
/// order for the directive to succeed. For example, a CHECK directive's
|
||||||
/// pattern is expected, and a CHECK-NOT directive's pattern is excluded.
|
/// pattern is expected, and a CHECK-NOT directive's pattern is excluded.
|
||||||
/// All match result types whose names end with "Excluded" are for excluded
|
|
||||||
/// patterns, and all others are for expected patterns.
|
|
||||||
///
|
///
|
||||||
/// There might be more than one match result for a single pattern. For
|
/// There might be more than one match result for a single pattern. For
|
||||||
/// example, there might be several discarded matches
|
/// example, there might be several discarded matches
|
||||||
@ -136,18 +134,29 @@ struct FileCheckDiag {
|
|||||||
MatchFoundButWrongLine,
|
MatchFoundButWrongLine,
|
||||||
/// Indicates a discarded match for an expected pattern.
|
/// Indicates a discarded match for an expected pattern.
|
||||||
MatchFoundButDiscarded,
|
MatchFoundButDiscarded,
|
||||||
|
/// Indicates an error while processing a match after the match was found
|
||||||
|
/// for an expected or excluded pattern. The error is specified by \c Note,
|
||||||
|
/// to which it should be appropriate to prepend "error: " later. The full
|
||||||
|
/// match itself should be recorded in a preceding diagnostic of a different
|
||||||
|
/// \c MatchFound match type.
|
||||||
|
MatchFoundErrorNote,
|
||||||
/// Indicates no match for an excluded pattern.
|
/// Indicates no match for an excluded pattern.
|
||||||
MatchNoneAndExcluded,
|
MatchNoneAndExcluded,
|
||||||
/// Indicates no match for an expected pattern, but this might follow good
|
/// Indicates no match for an expected pattern, but this might follow good
|
||||||
/// matches when multiple matches are expected for the pattern, or it might
|
/// matches when multiple matches are expected for the pattern, or it might
|
||||||
/// follow discarded matches for the pattern.
|
/// follow discarded matches for the pattern.
|
||||||
MatchNoneButExpected,
|
MatchNoneButExpected,
|
||||||
|
/// Indicates no match due to an expected or excluded pattern that has
|
||||||
|
/// proven to be invalid at match time. The exact problems are usually
|
||||||
|
/// reported in subsequent diagnostics of the same match type but with
|
||||||
|
/// \c Note set.
|
||||||
|
MatchNoneForInvalidPattern,
|
||||||
/// Indicates a fuzzy match that serves as a suggestion for the next
|
/// 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.
|
/// intended match for an expected pattern with too few or no good matches.
|
||||||
MatchFuzzy,
|
MatchFuzzy,
|
||||||
} MatchTy;
|
} MatchTy;
|
||||||
/// The search range if MatchTy is MatchNoneAndExcluded or
|
/// The search range if MatchTy starts with MatchNone, or the match range
|
||||||
/// MatchNoneButExpected, or the match range otherwise.
|
/// otherwise.
|
||||||
unsigned InputStartLine;
|
unsigned InputStartLine;
|
||||||
unsigned InputStartCol;
|
unsigned InputStartCol;
|
||||||
unsigned InputEndLine;
|
unsigned InputEndLine;
|
||||||
|
@ -476,6 +476,7 @@ char OverflowError::ID = 0;
|
|||||||
char UndefVarError::ID = 0;
|
char UndefVarError::ID = 0;
|
||||||
char ErrorDiagnostic::ID = 0;
|
char ErrorDiagnostic::ID = 0;
|
||||||
char NotFoundError::ID = 0;
|
char NotFoundError::ID = 0;
|
||||||
|
char ErrorReported::ID = 0;
|
||||||
|
|
||||||
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
|
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
|
||||||
StringRef &Expr, FileCheckPatternContext *Context,
|
StringRef &Expr, FileCheckPatternContext *Context,
|
||||||
@ -1212,22 +1213,19 @@ void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {
|
|||||||
RegExStr += Backref;
|
RegExStr += Backref;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
|
Pattern::MatchResult Pattern::match(StringRef Buffer,
|
||||||
const SourceMgr &SM) const {
|
const SourceMgr &SM) const {
|
||||||
// If this is the EOF pattern, match it immediately.
|
// If this is the EOF pattern, match it immediately.
|
||||||
if (CheckTy == Check::CheckEOF) {
|
if (CheckTy == Check::CheckEOF)
|
||||||
MatchLen = 0;
|
return MatchResult(Buffer.size(), 0, Error::success());
|
||||||
return Buffer.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a fixed string pattern, just match it now.
|
// If this is a fixed string pattern, just match it now.
|
||||||
if (!FixedStr.empty()) {
|
if (!FixedStr.empty()) {
|
||||||
MatchLen = FixedStr.size();
|
|
||||||
size_t Pos =
|
size_t Pos =
|
||||||
IgnoreCase ? Buffer.find_lower(FixedStr) : Buffer.find(FixedStr);
|
IgnoreCase ? Buffer.find_lower(FixedStr) : Buffer.find(FixedStr);
|
||||||
if (Pos == StringRef::npos)
|
if (Pos == StringRef::npos)
|
||||||
return make_error<NotFoundError>();
|
return make_error<NotFoundError>();
|
||||||
return Pos;
|
return MatchResult(Pos, /*MatchLen=*/FixedStr.size(), Error::success());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex match.
|
// Regex match.
|
||||||
@ -1250,7 +1248,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
|
|||||||
Expected<std::string> Value = Substitution->getResult();
|
Expected<std::string> Value = Substitution->getResult();
|
||||||
if (!Value) {
|
if (!Value) {
|
||||||
// Convert to an ErrorDiagnostic to get location information. This is
|
// 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.
|
// substitution block caused the overflow.
|
||||||
Error Err =
|
Error Err =
|
||||||
handleErrors(Value.takeError(), [&](const OverflowError &E) {
|
handleErrors(Value.takeError(), [&](const OverflowError &E) {
|
||||||
@ -1289,6 +1287,14 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
|
|||||||
MatchInfo[VariableDef.second];
|
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.
|
// If this defines any numeric variables, remember their values.
|
||||||
for (const auto &NumericVariableDef : NumericVariableDefs) {
|
for (const auto &NumericVariableDef : NumericVariableDefs) {
|
||||||
const NumericVariableMatch &NumericVariableMatch =
|
const NumericVariableMatch &NumericVariableMatch =
|
||||||
@ -1303,16 +1309,11 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
|
|||||||
Expected<ExpressionValue> Value =
|
Expected<ExpressionValue> Value =
|
||||||
Format.valueFromStringRepr(MatchedValue, SM);
|
Format.valueFromStringRepr(MatchedValue, SM);
|
||||||
if (!Value)
|
if (!Value)
|
||||||
return Value.takeError();
|
return MatchResult(TheMatch, Value.takeError());
|
||||||
DefinedNumericVariable->setValue(*Value, MatchedValue);
|
DefinedNumericVariable->setValue(*Value, MatchedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
|
return MatchResult(TheMatch, Error::success());
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
|
unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
|
||||||
@ -1349,7 +1350,7 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
|||||||
bool UndefSeen = false;
|
bool UndefSeen = false;
|
||||||
handleAllErrors(
|
handleAllErrors(
|
||||||
MatchedValue.takeError(), [](const NotFoundError &E) {},
|
MatchedValue.takeError(), [](const NotFoundError &E) {},
|
||||||
// Handled in PrintNoMatch().
|
// Handled in printMatch and printNoMatch().
|
||||||
[](const ErrorDiagnostic &E) {},
|
[](const ErrorDiagnostic &E) {},
|
||||||
// Handled in match().
|
// Handled in match().
|
||||||
[](const OverflowError &E) {},
|
[](const OverflowError &E) {},
|
||||||
@ -1404,11 +1405,12 @@ void Pattern::printVariableDefs(const SourceMgr &SM,
|
|||||||
for (const auto &VariableDef : NumericVariableDefs) {
|
for (const auto &VariableDef : NumericVariableDefs) {
|
||||||
VarCapture VC;
|
VarCapture VC;
|
||||||
VC.Name = VariableDef.getKey();
|
VC.Name = VariableDef.getKey();
|
||||||
StringRef StrValue = VariableDef.getValue()
|
Optional<StringRef> StrValue =
|
||||||
.DefinedNumericVariable->getStringValue()
|
VariableDef.getValue().DefinedNumericVariable->getStringValue();
|
||||||
.getValue();
|
if (!StrValue)
|
||||||
SMLoc Start = SMLoc::getFromPointer(StrValue.data());
|
continue;
|
||||||
SMLoc End = SMLoc::getFromPointer(StrValue.data() + StrValue.size());
|
SMLoc Start = SMLoc::getFromPointer(StrValue->data());
|
||||||
|
SMLoc End = SMLoc::getFromPointer(StrValue->data() + StrValue->size());
|
||||||
VC.Range = SMRange(Start, End);
|
VC.Range = SMRange(Start, End);
|
||||||
VarCaptures.push_back(VC);
|
VarCaptures.push_back(VC);
|
||||||
}
|
}
|
||||||
@ -2036,123 +2038,179 @@ bool FileCheck::readCheckFile(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
|
/// 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,
|
StringRef Prefix, SMLoc Loc, const Pattern &Pat,
|
||||||
int MatchedCount, StringRef Buffer, size_t MatchPos,
|
int MatchedCount, StringRef Buffer,
|
||||||
size_t MatchLen, const FileCheckRequest &Req,
|
Pattern::MatchResult MatchResult,
|
||||||
|
const FileCheckRequest &Req,
|
||||||
std::vector<FileCheckDiag> *Diags) {
|
std::vector<FileCheckDiag> *Diags) {
|
||||||
|
// Suppress some verbosity if there's no error.
|
||||||
|
bool HasError = !ExpectedMatch || MatchResult.TheError;
|
||||||
bool PrintDiag = true;
|
bool PrintDiag = true;
|
||||||
if (ExpectedMatch) {
|
if (!HasError) {
|
||||||
if (!Req.Verbose)
|
if (!Req.Verbose)
|
||||||
return;
|
return ErrorReported::reportedOrSuccess(HasError);
|
||||||
if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
|
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
|
// 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
|
// gathering them for Diags to be rendered elsewhere, but we always print
|
||||||
// diagnostics.
|
// other diagnostics.
|
||||||
PrintDiag = !Diags;
|
PrintDiag = !Diags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add "found" diagnostic, substitutions, and variable definitions to Diags.
|
||||||
FileCheckDiag::MatchType MatchTy = ExpectedMatch
|
FileCheckDiag::MatchType MatchTy = ExpectedMatch
|
||||||
? FileCheckDiag::MatchFoundAndExpected
|
? FileCheckDiag::MatchFoundAndExpected
|
||||||
: FileCheckDiag::MatchFoundButExcluded;
|
: FileCheckDiag::MatchFoundButExcluded;
|
||||||
SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
|
SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
|
||||||
Buffer, MatchPos, MatchLen, Diags);
|
Buffer, MatchResult.TheMatch->Pos,
|
||||||
|
MatchResult.TheMatch->Len, Diags);
|
||||||
if (Diags) {
|
if (Diags) {
|
||||||
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
|
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
|
||||||
Pat.printVariableDefs(SM, MatchTy, Diags);
|
Pat.printVariableDefs(SM, MatchTy, Diags);
|
||||||
}
|
}
|
||||||
if (!PrintDiag)
|
if (!PrintDiag) {
|
||||||
return;
|
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",
|
std::string Message = formatv("{0}: {1} string found in input",
|
||||||
Pat.getCheckTy().getDescription(Prefix),
|
Pat.getCheckTy().getDescription(Prefix),
|
||||||
(ExpectedMatch ? "expected" : "excluded"))
|
(ExpectedMatch ? "expected" : "excluded"))
|
||||||
.str();
|
.str();
|
||||||
if (Pat.getCount() > 1)
|
if (Pat.getCount() > 1)
|
||||||
Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
|
Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
|
||||||
|
|
||||||
SM.PrintMessage(
|
SM.PrintMessage(
|
||||||
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
|
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
|
||||||
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
|
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
|
||||||
{MatchRange});
|
{MatchRange});
|
||||||
|
|
||||||
|
// Print additional information, which can be useful even if there are errors.
|
||||||
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);
|
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);
|
||||||
Pat.printVariableDefs(SM, MatchTy, nullptr);
|
Pat.printVariableDefs(SM, MatchTy, nullptr);
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
|
/// Returns either (1) \c ErrorSuccess if there was no error, or (2)
|
||||||
const FileCheckString &CheckStr, int MatchedCount,
|
/// \c ErrorReported if an error was reported, such as an expected match not
|
||||||
StringRef Buffer, size_t MatchPos, size_t MatchLen,
|
/// found.
|
||||||
FileCheckRequest &Req,
|
static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
||||||
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,
|
StringRef Prefix, SMLoc Loc, const Pattern &Pat,
|
||||||
int MatchedCount, StringRef Buffer,
|
int MatchedCount, StringRef Buffer, Error MatchError,
|
||||||
bool VerboseVerbose, std::vector<FileCheckDiag> *Diags,
|
bool VerboseVerbose,
|
||||||
Error MatchErrors) {
|
std::vector<FileCheckDiag> *Diags) {
|
||||||
assert(MatchErrors && "Called on successful match");
|
// Print any pattern errors, and record them to be added to Diags later.
|
||||||
bool PrintDiag = true;
|
bool HasError = ExpectedMatch;
|
||||||
if (!ExpectedMatch) {
|
bool HasPatternError = false;
|
||||||
if (!VerboseVerbose) {
|
|
||||||
consumeError(std::move(MatchErrors));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 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::MatchType MatchTy = ExpectedMatch
|
||||||
? FileCheckDiag::MatchNoneButExpected
|
? FileCheckDiag::MatchNoneButExpected
|
||||||
: FileCheckDiag::MatchNoneAndExcluded;
|
: FileCheckDiag::MatchNoneAndExcluded;
|
||||||
SMRange SearchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
|
SmallVector<std::string, 4> ErrorMsgs;
|
||||||
Buffer, 0, Buffer.size(), Diags);
|
handleAllErrors(
|
||||||
|
std::move(MatchError),
|
||||||
|
[&](const ErrorDiagnostic &E) {
|
||||||
|
HasError = HasPatternError = true;
|
||||||
|
MatchTy = FileCheckDiag::MatchNoneForInvalidPattern;
|
||||||
|
E.log(errs());
|
||||||
if (Diags)
|
if (Diags)
|
||||||
Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags);
|
ErrorMsgs.push_back(E.getMessage().str());
|
||||||
if (!PrintDiag) {
|
},
|
||||||
consumeError(std::move(MatchErrors));
|
// UndefVarError is reported in printSubstitutions below.
|
||||||
return;
|
// FIXME: It probably should be handled as a pattern error and actually
|
||||||
|
// change the exit status to 1, even if !ExpectedMatch. To do so, we
|
||||||
|
// could stop calling printSubstitutions and actually report the error
|
||||||
|
// here as we do ErrorDiagnostic above.
|
||||||
|
[](const UndefVarError &E) {},
|
||||||
|
// NotFoundError is why printNoMatch was invoked.
|
||||||
|
[](const NotFoundError &E) {});
|
||||||
|
|
||||||
|
// Suppress some verbosity if there's no error.
|
||||||
|
bool PrintDiag = true;
|
||||||
|
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 Diags to be rendered elsewhere, but we always print
|
||||||
|
// other diagnostics.
|
||||||
|
PrintDiag = !Diags;
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchErrors = handleErrors(std::move(MatchErrors),
|
// Add "not found" diagnostic, substitutions, and pattern errors to Diags.
|
||||||
[](const ErrorDiagnostic &E) { E.log(errs()); });
|
//
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
assert(!HasError && "expected to report more diagnostics for error");
|
||||||
|
return ErrorReported::reportedOrSuccess(HasError);
|
||||||
|
}
|
||||||
|
|
||||||
// No problem matching the string per se.
|
// Print "not found" diagnostic, except that's implied if we already printed a
|
||||||
if (!MatchErrors)
|
// pattern error.
|
||||||
return;
|
if (!HasPatternError) {
|
||||||
consumeError(std::move(MatchErrors));
|
|
||||||
|
|
||||||
// Print "not found" diagnostic.
|
|
||||||
std::string Message = formatv("{0}: {1} string not found in input",
|
std::string Message = formatv("{0}: {1} string not found in input",
|
||||||
Pat.getCheckTy().getDescription(Prefix),
|
Pat.getCheckTy().getDescription(Prefix),
|
||||||
(ExpectedMatch ? "expected" : "excluded"))
|
(ExpectedMatch ? "expected" : "excluded"))
|
||||||
.str();
|
.str();
|
||||||
if (Pat.getCount() > 1)
|
if (Pat.getCount() > 1)
|
||||||
Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
|
Message +=
|
||||||
SM.PrintMessage(
|
formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
|
||||||
Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message);
|
SM.PrintMessage(Loc,
|
||||||
|
ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
|
||||||
// Print the "scanning from here" line.
|
Message);
|
||||||
SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
|
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)
|
|
||||||
Pat.printFuzzyMatch(SM, Buffer, Diags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
// Print additional information, which can be useful even after a pattern
|
||||||
const FileCheckString &CheckStr, int MatchedCount,
|
// error.
|
||||||
StringRef Buffer, bool VerboseVerbose,
|
Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr);
|
||||||
std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
|
if (ExpectedMatch)
|
||||||
PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
|
Pat.printFuzzyMatch(SM, Buffer, Diags);
|
||||||
MatchedCount, Buffer, VerboseVerbose, Diags,
|
return ErrorReported::reportedOrSuccess(HasError);
|
||||||
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.
|
/// Counts the number of newlines in the specified range.
|
||||||
@ -2204,24 +2262,23 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
|
|||||||
assert(Pat.getCount() != 0 && "pattern count can not be zero");
|
assert(Pat.getCount() != 0 && "pattern count can not be zero");
|
||||||
for (int i = 1; i <= Pat.getCount(); i++) {
|
for (int i = 1; i <= Pat.getCount(); i++) {
|
||||||
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
|
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
|
||||||
size_t CurrentMatchLen;
|
|
||||||
// get a match at current start point
|
// get a match at current start point
|
||||||
Expected<size_t> MatchResult = Pat.match(MatchBuffer, CurrentMatchLen, SM);
|
Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);
|
||||||
|
|
||||||
// report
|
// report
|
||||||
if (!MatchResult) {
|
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix, Loc,
|
||||||
PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags,
|
Pat, i, MatchBuffer,
|
||||||
MatchResult.takeError());
|
std::move(MatchResult), Req, Diags)) {
|
||||||
|
cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
|
||||||
return StringRef::npos;
|
return StringRef::npos;
|
||||||
}
|
}
|
||||||
size_t MatchPos = *MatchResult;
|
|
||||||
PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
|
size_t MatchPos = MatchResult.TheMatch->Pos;
|
||||||
Diags);
|
|
||||||
if (i == 1)
|
if (i == 1)
|
||||||
FirstMatchPos = LastPos + MatchPos;
|
FirstMatchPos = LastPos + MatchPos;
|
||||||
|
|
||||||
// move start point after the match
|
// move start point after the match
|
||||||
LastMatchEnd += MatchPos + CurrentMatchLen;
|
LastMatchEnd += MatchPos + MatchResult.TheMatch->Len;
|
||||||
}
|
}
|
||||||
// Full match len counts from first match pos.
|
// Full match len counts from first match pos.
|
||||||
MatchLen = LastMatchEnd - FirstMatchPos;
|
MatchLen = LastMatchEnd - FirstMatchPos;
|
||||||
@ -2328,22 +2385,15 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
|
|||||||
bool DirectiveFail = false;
|
bool DirectiveFail = false;
|
||||||
for (const Pattern *Pat : NotStrings) {
|
for (const Pattern *Pat : NotStrings) {
|
||||||
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
|
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
|
||||||
|
Pattern::MatchResult MatchResult = Pat->match(Buffer, SM);
|
||||||
size_t MatchLen = 0;
|
if (Error Err = reportMatchResult(/*ExpectedMatch=*/false, SM, Prefix,
|
||||||
Expected<size_t> MatchResult = Pat->match(Buffer, MatchLen, SM);
|
Pat->getLoc(), *Pat, 1, Buffer,
|
||||||
|
std::move(MatchResult), Req, Diags)) {
|
||||||
if (!MatchResult) {
|
cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
|
||||||
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
|
DirectiveFail = true;
|
||||||
Req.VerboseVerbose, Diags, MatchResult.takeError());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
size_t Pos = *MatchResult;
|
|
||||||
|
|
||||||
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
|
|
||||||
Req, Diags);
|
|
||||||
DirectiveFail = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DirectiveFail;
|
return DirectiveFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2389,20 +2439,22 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
|||||||
// CHECK-DAG group.
|
// CHECK-DAG group.
|
||||||
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
|
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
|
||||||
StringRef MatchBuffer = Buffer.substr(MatchPos);
|
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
|
// With a group of CHECK-DAGs, a single mismatching means the match on
|
||||||
// that group of CHECK-DAGs fails immediately.
|
// that group of CHECK-DAGs fails immediately.
|
||||||
if (!MatchResult) {
|
if (MatchResult.TheError || Req.VerboseVerbose) {
|
||||||
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
|
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix,
|
||||||
Req.VerboseVerbose, Diags, MatchResult.takeError());
|
Pat.getLoc(), Pat, 1, MatchBuffer,
|
||||||
|
std::move(MatchResult), Req, Diags)) {
|
||||||
|
cantFail(
|
||||||
|
handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
|
||||||
return StringRef::npos;
|
return StringRef::npos;
|
||||||
}
|
}
|
||||||
size_t MatchPosBuf = *MatchResult;
|
}
|
||||||
// Re-calc it as the offset relative to the start of the original string.
|
MatchLen = MatchResult.TheMatch->Len;
|
||||||
MatchPos += MatchPosBuf;
|
// Re-calc it as the offset relative to the start of the original
|
||||||
if (Req.VerboseVerbose)
|
// string.
|
||||||
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
|
MatchPos += MatchResult.TheMatch->Pos;
|
||||||
MatchLen, Req, Diags);
|
|
||||||
MatchRange M{MatchPos, MatchPos + MatchLen};
|
MatchRange M{MatchPos, MatchPos + MatchLen};
|
||||||
if (Req.AllowDeprecatedDagOverlap) {
|
if (Req.AllowDeprecatedDagOverlap) {
|
||||||
// We don't need to track all matches in this mode, so we just maintain
|
// We don't need to track all matches in this mode, so we just maintain
|
||||||
@ -2453,8 +2505,10 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
|||||||
MatchPos = MI->End;
|
MatchPos = MI->End;
|
||||||
}
|
}
|
||||||
if (!Req.VerboseVerbose)
|
if (!Req.VerboseVerbose)
|
||||||
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
|
cantFail(printMatch(
|
||||||
MatchLen, Req, Diags);
|
/*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.
|
// Handle the end of a CHECK-DAG group.
|
||||||
if (std::next(PatItr) == PatEnd ||
|
if (std::next(PatItr) == PatEnd ||
|
||||||
|
@ -537,11 +537,13 @@ private:
|
|||||||
class ErrorDiagnostic : public ErrorInfo<ErrorDiagnostic> {
|
class ErrorDiagnostic : public ErrorInfo<ErrorDiagnostic> {
|
||||||
private:
|
private:
|
||||||
SMDiagnostic Diagnostic;
|
SMDiagnostic Diagnostic;
|
||||||
|
SMRange Range;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static char ID;
|
static char ID;
|
||||||
|
|
||||||
ErrorDiagnostic(SMDiagnostic &&Diag) : Diagnostic(Diag) {}
|
ErrorDiagnostic(SMDiagnostic &&Diag, SMRange Range)
|
||||||
|
: Diagnostic(Diag), Range(Range) {}
|
||||||
|
|
||||||
std::error_code convertToErrorCode() const override {
|
std::error_code convertToErrorCode() const override {
|
||||||
return inconvertibleErrorCode();
|
return inconvertibleErrorCode();
|
||||||
@ -550,13 +552,19 @@ public:
|
|||||||
/// Print diagnostic associated with this error when printing the error.
|
/// Print diagnostic associated with this error when printing the error.
|
||||||
void log(raw_ostream &OS) const override { Diagnostic.print(nullptr, OS); }
|
void log(raw_ostream &OS) const override { Diagnostic.print(nullptr, OS); }
|
||||||
|
|
||||||
static Error get(const SourceMgr &SM, SMLoc Loc, const Twine &ErrMsg) {
|
StringRef getMessage() const { return Diagnostic.getMessage(); }
|
||||||
|
SMRange getRange() const { return Range; }
|
||||||
|
|
||||||
|
static Error get(const SourceMgr &SM, SMLoc Loc, const Twine &ErrMsg,
|
||||||
|
SMRange Range = None) {
|
||||||
return make_error<ErrorDiagnostic>(
|
return make_error<ErrorDiagnostic>(
|
||||||
SM.GetMessage(Loc, SourceMgr::DK_Error, ErrMsg));
|
SM.GetMessage(Loc, SourceMgr::DK_Error, ErrMsg), Range);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error get(const SourceMgr &SM, StringRef Buffer, const Twine &ErrMsg) {
|
static Error get(const SourceMgr &SM, StringRef Buffer, const Twine &ErrMsg) {
|
||||||
return get(SM, SMLoc::getFromPointer(Buffer.data()), ErrMsg);
|
SMLoc Start = SMLoc::getFromPointer(Buffer.data());
|
||||||
|
SMLoc End = SMLoc::getFromPointer(Buffer.data() + Buffer.size());
|
||||||
|
return get(SM, Start, ErrMsg, SMRange(Start, End));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -574,6 +582,36 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// An error that has already been reported.
|
||||||
|
///
|
||||||
|
/// This class is designed to support a function whose callers may need to know
|
||||||
|
/// whether the function encountered and reported an error but never need to
|
||||||
|
/// know the nature of that error. For example, the function has a return type
|
||||||
|
/// of \c Error and always returns either \c ErrorReported or \c ErrorSuccess.
|
||||||
|
/// That interface is similar to that of a function returning bool to indicate
|
||||||
|
/// an error except, in the former case, (1) there is no confusion over polarity
|
||||||
|
/// and (2) the caller must either check the result or explicitly ignore it with
|
||||||
|
/// a call like \c consumeError.
|
||||||
|
class ErrorReported final : public ErrorInfo<ErrorReported> {
|
||||||
|
public:
|
||||||
|
static char ID;
|
||||||
|
|
||||||
|
std::error_code convertToErrorCode() const override {
|
||||||
|
return inconvertibleErrorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print diagnostic associated with this error when printing the error.
|
||||||
|
void log(raw_ostream &OS) const override {
|
||||||
|
OS << "error previously reported";
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Error reportedOrSuccess(bool HasErrorReported) {
|
||||||
|
if (HasErrorReported)
|
||||||
|
return make_error<ErrorReported>();
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Pattern {
|
class Pattern {
|
||||||
SMLoc PatternLoc;
|
SMLoc PatternLoc;
|
||||||
|
|
||||||
@ -694,11 +732,22 @@ public:
|
|||||||
/// \returns true in case of an error, false otherwise.
|
/// \returns true in case of an error, false otherwise.
|
||||||
bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
|
bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
|
||||||
const FileCheckRequest &Req);
|
const FileCheckRequest &Req);
|
||||||
/// Matches the pattern string against the input buffer \p Buffer
|
struct Match {
|
||||||
|
size_t Pos;
|
||||||
|
size_t Len;
|
||||||
|
};
|
||||||
|
struct MatchResult {
|
||||||
|
Optional<Match> TheMatch;
|
||||||
|
Error TheError;
|
||||||
|
MatchResult(size_t MatchPos, size_t MatchLen, Error E)
|
||||||
|
: TheMatch(Match{MatchPos, MatchLen}), TheError(std::move(E)) {}
|
||||||
|
MatchResult(Match M, Error E) : TheMatch(M), TheError(std::move(E)) {}
|
||||||
|
MatchResult(Error E) : TheError(std::move(E)) {}
|
||||||
|
};
|
||||||
|
/// Matches the pattern string against the input buffer \p Buffer.
|
||||||
///
|
///
|
||||||
/// \returns the position that is matched or an error indicating why matching
|
/// \returns either (1) an error resulting in no match or (2) a match possibly
|
||||||
/// failed. If there is a match, updates \p MatchLen with the size of the
|
/// with an error encountered while processing the match.
|
||||||
/// matched string.
|
|
||||||
///
|
///
|
||||||
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
|
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
|
||||||
/// instance provides the current values of FileCheck string variables and is
|
/// instance provides the current values of FileCheck string variables and is
|
||||||
@ -706,8 +755,7 @@ public:
|
|||||||
/// GlobalNumericVariableTable StringMap in the same class provides the
|
/// GlobalNumericVariableTable StringMap in the same class provides the
|
||||||
/// current values of FileCheck numeric variables and is updated if this
|
/// current values of FileCheck numeric variables and is updated if this
|
||||||
/// match defines new numeric values.
|
/// match defines new numeric values.
|
||||||
Expected<size_t> match(StringRef Buffer, size_t &MatchLen,
|
MatchResult match(StringRef Buffer, const SourceMgr &SM) const;
|
||||||
const SourceMgr &SM) const;
|
|
||||||
/// Prints the value of successful substitutions or the name of the undefined
|
/// Prints the value of successful substitutions or the name of the undefined
|
||||||
/// string or numeric variables preventing a successful substitution.
|
/// string or numeric variables preventing a successful substitution.
|
||||||
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
; Check handling of match-time diagnostics for invalid patterns (e.g.,
|
||||||
|
; substitution overflow) in the case of excluded patterns (e.g., CHECK-NOT).
|
||||||
|
;
|
||||||
|
; At one time, FileCheck's exit status was zero for this case. Moreover, it
|
||||||
|
; printed the error diagnostic only 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 in the middle of the dump. These will be fixed in a
|
||||||
|
; subsequent patch.
|
||||||
|
|
||||||
|
RUN: echo > %t.chk \
|
||||||
|
RUN: 'CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] [[UNDEFVAR]]'
|
||||||
|
RUN: echo > %t.in '10000000000000000'
|
||||||
|
|
||||||
|
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:{{.*}}: note:
|
||||||
|
ERR-NEXT:10000000000000000
|
||||||
|
ERR-NEXT:^
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: uses undefined variable(s): "UNDEFVAR"
|
||||||
|
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-NEXT:not:1'3 uses undefined variable(s): "UNDEFVAR"
|
||||||
|
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
|
@ -0,0 +1,62 @@
|
|||||||
|
; Check handling of match-time diagnostics for invalid patterns (e.g.,
|
||||||
|
; substitution overflow) in the case of expected patterns (e.g., CHECK).
|
||||||
|
;
|
||||||
|
; FIXME: We shouldn't have: (1) the blank note at the end of the trace, and
|
||||||
|
; (2) the redundant error in the middle of the dump. These will be fixed in a
|
||||||
|
; subsequent patch.
|
||||||
|
|
||||||
|
RUN: echo > %t.chk \
|
||||||
|
RUN: 'CHECK: [[#0x8000000000000000+0x8000000000000000]] [[UNDEFVAR]]'
|
||||||
|
RUN: echo > %t.in '10000000000000000'
|
||||||
|
|
||||||
|
ERR-NOT:{{.}}
|
||||||
|
ERR:{{.*}}: error: unable to substitute variable or numeric expression: overflow error
|
||||||
|
ERR-NEXT:CHECK: {{.*}}
|
||||||
|
ERR-NEXT:{{ *}}^
|
||||||
|
ERR-NEXT:{{.*}}: note:
|
||||||
|
ERR-NEXT:10000000000000000
|
||||||
|
ERR-NEXT:^
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: uses undefined variable(s): "UNDEFVAR"
|
||||||
|
ERR-NEXT:10000000000000000
|
||||||
|
ERR-NEXT:^
|
||||||
|
ERR-NOT:{{error|note|remark}}:
|
||||||
|
|
||||||
|
DUMP:<<<<<<
|
||||||
|
DUMP-NEXT: 1: 10000000000000000
|
||||||
|
DUMP-NEXT:check:1'0 X~~~~~~~~~~~~~~~~~ error: match failed for invalid pattern
|
||||||
|
DUMP-NEXT:check:1'1 unable to substitute variable or numeric expression: overflow error
|
||||||
|
DUMP-NEXT:check:1'2 X error: match failed for invalid pattern
|
||||||
|
DUMP-NEXT:check:1'3 uses undefined variable(s): "UNDEFVAR"
|
||||||
|
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
|
||||||
|
|
||||||
|
;--------------------------------------------------
|
||||||
|
; 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
|
@ -0,0 +1,88 @@
|
|||||||
|
; Check handling of diagnostics for problematic matches (e.g., variable capture
|
||||||
|
; overflow) in the case of excluded patterns (e.g., CHECK-NOT).
|
||||||
|
;
|
||||||
|
; At one time, FileCheck's exit status for the following example was zero even
|
||||||
|
; though the excluded pattern does match (it's just capturing that fails).
|
||||||
|
; Moreover, it printed the error diagnostic only if -vv was specified and input
|
||||||
|
; dumps were disabled. Test every combination as the logic is hard to get
|
||||||
|
; right.
|
||||||
|
;
|
||||||
|
; TODO: Capturing from an excluded pattern probably shouldn't be permitted
|
||||||
|
; because it seems useless: it's captured only if the pattern matches, but then
|
||||||
|
; FileCheck fails. The helpfulness of reporting overflow from that capture is
|
||||||
|
; perhaps questionable then, but it doesn't seem harmful either. Anyway, the
|
||||||
|
; goal of this test is simply to exercise the error propagation mechanism for a
|
||||||
|
; matched excluded pattern. In the future, if we have a more interesting error
|
||||||
|
; to exercise in that case, we should instead use it in this test, and then we
|
||||||
|
; might want to think more about where that error should be presented in the
|
||||||
|
; list of diagnostics.
|
||||||
|
|
||||||
|
RUN: echo > %t.chk 'CHECK-NOT: [[#122+1]] [[STR:abc]] [[#NUM:]]'
|
||||||
|
RUN: echo > %t.in '123 abc 1000000000000000000000000000000000000000000000000000'
|
||||||
|
|
||||||
|
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: CHECK-NOT: excluded string found in input
|
||||||
|
ERR-NEXT:CHECK-NOT: {{.*}}
|
||||||
|
ERR-NEXT:{{ *}}^
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: found here
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT:^~~~~~~~~{{~*}}
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: with "122+1" equal to "123"
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT:^
|
||||||
|
ERR-NEXT:<stdin>:1:5: note: captured var "STR"
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT: ^~~
|
||||||
|
ERR-NEXT:<stdin>:1:9: error: unable to represent numeric value
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT: ^
|
||||||
|
ERR-NOT:{{error|note|remark}}:
|
||||||
|
|
||||||
|
DUMP:<<<<<<
|
||||||
|
DUMP-NEXT: 1: 123 abc 10{{0*}}
|
||||||
|
DUMP-NEXT:not:1'0 !~~~~~~~~~{{~*}} error: no match expected
|
||||||
|
DUMP-NEXT:not:1'1 with "122+1" equal to "123"
|
||||||
|
DUMP-NEXT:not:1'2 !~~ captured var "STR"
|
||||||
|
DUMP-NEXT:not:1'3 !~{{~*}} error: unable to represent numeric value
|
||||||
|
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
|
@ -0,0 +1,63 @@
|
|||||||
|
; Check handling of diagnostics for problematic matches (e.g., variable capture
|
||||||
|
; overflow) in the case of expected patterns (e.g., CHECK).
|
||||||
|
|
||||||
|
RUN: echo > %t.chk 'CHECK: [[#122+1]] [[STR:abc]] [[#NUM:]]'
|
||||||
|
RUN: echo > %t.in '123 abc 1000000000000000000000000000000000000000000000000000'
|
||||||
|
|
||||||
|
ERR-NOT:{{.}}
|
||||||
|
ERR:{{.*}}: remark: CHECK: expected string found in input
|
||||||
|
ERR-NEXT:CHECK: {{.*}}
|
||||||
|
ERR-NEXT:{{ *}}^
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: found here
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT:^~~~~~~~~{{~*}}
|
||||||
|
ERR-NEXT:<stdin>:1:1: note: with "122+1" equal to "123"
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT:^
|
||||||
|
ERR-NEXT:<stdin>:1:5: note: captured var "STR"
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT: ^~~
|
||||||
|
ERR-NEXT:<stdin>:1:9: error: unable to represent numeric value
|
||||||
|
ERR-NEXT:123 abc 10{{0*}}
|
||||||
|
ERR-NEXT: ^
|
||||||
|
ERR-NOT:{{error|note|remark}}:
|
||||||
|
|
||||||
|
DUMP:<<<<<<
|
||||||
|
DUMP-NEXT: 1: 123 abc 10{{0*}}
|
||||||
|
DUMP-NEXT:check:1'0 ^~~~~~~~~~{{~*}}
|
||||||
|
DUMP-NEXT:check:1'1 with "122+1" equal to "123"
|
||||||
|
DUMP-NEXT:check:1'2 ^~~ captured var "STR"
|
||||||
|
DUMP-NEXT:check:1'3 !~{{~*}} error: unable to represent numeric value
|
||||||
|
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
|
||||||
|
|
||||||
|
;--------------------------------------------------
|
||||||
|
; 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
|
@ -1027,8 +1027,10 @@ public:
|
|||||||
|
|
||||||
Expected<size_t> match(StringRef Buffer) {
|
Expected<size_t> match(StringRef Buffer) {
|
||||||
StringRef BufferRef = bufferize(SM, Buffer);
|
StringRef BufferRef = bufferize(SM, Buffer);
|
||||||
size_t MatchLen;
|
Pattern::MatchResult Res = P.match(BufferRef, SM);
|
||||||
return P.match(BufferRef, MatchLen, SM);
|
if (Res.TheError)
|
||||||
|
return std::move(Res.TheError);
|
||||||
|
return Res.TheMatch->Pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printVariableDefs(FileCheckDiag::MatchType MatchTy,
|
void printVariableDefs(FileCheckDiag::MatchType MatchTy,
|
||||||
@ -1640,8 +1642,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
|||||||
FileCheckRequest Req;
|
FileCheckRequest Req;
|
||||||
Cxt.createLineVariable();
|
Cxt.createLineVariable();
|
||||||
ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
|
ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
|
||||||
size_t MatchLen;
|
Pattern::MatchResult Res = P.match("1", SM);
|
||||||
ASSERT_THAT_EXPECTED(P.match("1", MatchLen, SM), Succeeded());
|
ASSERT_THAT_ERROR(std::move(Res.TheError), Succeeded());
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Recreating @LINE pseudo numeric variable fails.
|
// Recreating @LINE pseudo numeric variable fails.
|
||||||
|
@ -212,11 +212,20 @@ static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
|
|||||||
case FileCheckDiag::MatchFoundButDiscarded:
|
case FileCheckDiag::MatchFoundButDiscarded:
|
||||||
return MarkerStyle('!', raw_ostream::CYAN,
|
return MarkerStyle('!', raw_ostream::CYAN,
|
||||||
"discard: overlaps earlier match");
|
"discard: overlaps earlier match");
|
||||||
|
case FileCheckDiag::MatchFoundErrorNote:
|
||||||
|
// Note should always be overridden within the FileCheckDiag.
|
||||||
|
return MarkerStyle('!', raw_ostream::RED,
|
||||||
|
"error: unknown error after match",
|
||||||
|
/*FiltersAsError=*/true);
|
||||||
case FileCheckDiag::MatchNoneAndExcluded:
|
case FileCheckDiag::MatchNoneAndExcluded:
|
||||||
return MarkerStyle('X', raw_ostream::GREEN);
|
return MarkerStyle('X', raw_ostream::GREEN);
|
||||||
case FileCheckDiag::MatchNoneButExpected:
|
case FileCheckDiag::MatchNoneButExpected:
|
||||||
return MarkerStyle('X', raw_ostream::RED, "error: no match found",
|
return MarkerStyle('X', raw_ostream::RED, "error: no match found",
|
||||||
/*FiltersAsError=*/true);
|
/*FiltersAsError=*/true);
|
||||||
|
case FileCheckDiag::MatchNoneForInvalidPattern:
|
||||||
|
return MarkerStyle('X', raw_ostream::RED,
|
||||||
|
"error: match failed for invalid pattern",
|
||||||
|
/*FiltersAsError=*/true);
|
||||||
case FileCheckDiag::MatchFuzzy:
|
case FileCheckDiag::MatchFuzzy:
|
||||||
return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
|
return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
|
||||||
/*FiltersAsError=*/true);
|
/*FiltersAsError=*/true);
|
||||||
@ -421,6 +430,11 @@ BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
|
|||||||
DiagItr->InputStartCol == DiagItr->InputEndCol)
|
DiagItr->InputStartCol == DiagItr->InputEndCol)
|
||||||
A.Marker.Lead = ' ';
|
A.Marker.Lead = ' ';
|
||||||
}
|
}
|
||||||
|
if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
|
||||||
|
assert(!DiagItr->Note.empty() &&
|
||||||
|
"expected custom note for MatchFoundErrorNote");
|
||||||
|
A.Marker.Note = "error: " + A.Marker.Note;
|
||||||
|
}
|
||||||
A.FoundAndExpectedMatch =
|
A.FoundAndExpectedMatch =
|
||||||
DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
|
DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user