FileCheck [8/12]: Define numeric var from expr

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch lift the restriction for a
numeric expression to either be a variable definition or a numeric
expression to try to match.

This commit allows a numeric variable to be set to the result of the
evaluation of a numeric expression after it has been matched
successfully. When it happens, the variable is allowed to be used on
the same line since its value is known at match time.

It also makes use of this possibility to reuse the parsing code to
parse a command-line definition by crafting a mirror string of the
-D option with the equal sign replaced by a colon sign, e.g. for option
'-D#NUMVAL=10' it creates the string
'-D#NUMVAL=10 (parsed as [[#NUMVAL:10]])' where the numeric expression
is parsed to define NUMVAL. This result in a few tests needing updating
for the location diagnostics on top of the tests for the new feature.

It also enables empty numeric expression which match any number without
defining a variable. This is done here rather than in commit #5 of the
patch series because it requires to dissociate automatic regex insertion
in RegExStr from variable definition which would make commit #5 even
bigger than it already is.

Copyright:
    - Linaro (changes up to diff 183612 of revision D55940)
    - GraphCore (changes in later versions of revision D55940 and
                 in new revision created off D55940)

Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar, arichardson, rnk

Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D60388

> llvm-svn: 366860

llvm-svn: 366897
This commit is contained in:
Thomas Preud'homme 2019-07-24 12:38:22 +00:00
parent 7d79b552e3
commit 4cd9b853b5
7 changed files with 483 additions and 259 deletions

View File

@ -107,12 +107,12 @@ and from the command line.
Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines. used in ``CHECK:`` lines.
.. option:: -D#<NUMVAR>=<VALUE EXPRESSION> .. option:: -D#<NUMVAR>=<NUMERIC EXPRESSION>
Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
``<VALUE EXPRESSION>`` that can be used in ``CHECK:`` lines. See section ``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
``FileCheck Numeric Variables and Expressions`` for details on the format ``FileCheck Numeric Variables and Expressions`` for details on supported
and meaning of ``<VALUE EXPRESSION>``. numeric expressions.
.. option:: -version .. option:: -version
@ -625,11 +625,27 @@ but would not match the text:
due to ``7`` being unequal to ``5 + 1``. due to ``7`` being unequal to ``5 + 1``.
The syntax also supports an empty expression, equivalent to writing {{[0-9]+}},
for cases where the input must contain a numeric value but the value itself
does not matter:
.. code-block:: gas
; CHECK-NOT: mov r0, r[[#]]
to check that a value is synthesized rather than moved around.
A numeric variable can also be defined to the result of a numeric expression,
in which case the numeric expression is checked and if verified the variable is
assigned to the value. The unified syntax for both defining numeric variables
and checking a numeric expression is thus ``[[#<NUMVAR>: <expr>]]`` with each
element as described previously.
The ``--enable-var-scope`` option has the same effect on numeric variables as The ``--enable-var-scope`` option has the same effect on numeric variables as
on string variables. on string variables.
Important note: In its current implementation, an expression cannot use a Important note: In its current implementation, an expression cannot use a
numeric variable defined on the same line. numeric variable with a non-empty expression defined on the same line.
FileCheck Pseudo Numeric Variables FileCheck Pseudo Numeric Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -94,6 +94,11 @@ private:
/// Name of the numeric variable. /// Name of the numeric variable.
StringRef Name; StringRef Name;
/// Pointer to expression defining this numeric variable. Null for pseudo
/// variable whose value is known at parse time (e.g. @LINE pseudo variable)
/// or cleared local variable.
FileCheckExpressionAST *ExpressionAST;
/// Value of numeric variable, if defined, or None otherwise. /// Value of numeric variable, if defined, or None otherwise.
Optional<uint64_t> Value; Optional<uint64_t> Value;
@ -104,10 +109,14 @@ private:
public: public:
/// Constructor for a variable \p Name defined at line \p DefLineNumber or /// Constructor for a variable \p Name defined at line \p DefLineNumber or
/// defined before input is parsed if DefLineNumber is None. /// defined before input is parsed if \p DefLineNumber is None. If not null,
/// the value set with setValue must match the result of evaluating
/// \p ExpressionAST.
FileCheckNumericVariable(StringRef Name, FileCheckNumericVariable(StringRef Name,
Optional<size_t> DefLineNumber = None) Optional<size_t> DefLineNumber = None,
: Name(Name), DefLineNumber(DefLineNumber) {} FileCheckExpressionAST *ExpressionAST = nullptr)
: Name(Name), ExpressionAST(ExpressionAST), DefLineNumber(DefLineNumber) {
}
/// \returns name of this numeric variable. /// \returns name of this numeric variable.
StringRef getName() const { return Name; } StringRef getName() const { return Name; }
@ -115,12 +124,25 @@ public:
/// \returns this variable's value. /// \returns this variable's value.
Optional<uint64_t> getValue() const { return Value; } Optional<uint64_t> getValue() const { return Value; }
/// Sets value of this numeric variable to \p NewValue. /// \returns the pointer to the expression defining this numeric variable, if
void setValue(uint64_t NewValue) { Value = NewValue; } /// any, or null otherwise.
FileCheckExpressionAST *getExpressionAST() const { return ExpressionAST; }
/// \returns whether this variable's value is known when performing the
/// substitutions of the line where it is defined.
bool isValueKnownAtMatchTime() const;
/// Sets value of this numeric variable to \p NewValue. Triggers an assertion
/// failure if the variable is defined by an expression and the expression
/// cannot be evaluated to be equal to \p NewValue.
void setValue(uint64_t NewValue);
/// Clears value of this numeric variable, regardless of whether it is /// Clears value of this numeric variable, regardless of whether it is
/// currently defined or not. /// currently defined or not.
void clearValue() { Value = None; } void clearValue() {
Value = None;
ExpressionAST = nullptr;
}
/// \returns the line number where this variable is defined, if any, or None /// \returns the line number where this variable is defined, if any, or None
/// if defined before input is parsed. /// if defined before input is parsed.
@ -507,27 +529,22 @@ public:
/// \p Str from the variable name. /// \p Str from the variable name.
static Expected<VariableProperties> parseVariable(StringRef &Str, static Expected<VariableProperties> parseVariable(StringRef &Str,
const SourceMgr &SM); const SourceMgr &SM);
/// Parses \p Expr for the name of a numeric variable to be defined at line /// Parses \p Expr for a numeric substitution block at line \p LineNumber,
/// \p LineNumber or before input is parsed if \p LineNumber is None. /// or before input is parsed if \p LineNumber is None. Parameter
/// \returns a pointer to the class instance representing that variable,
/// creating it if needed, or an error holding a diagnostic against \p SM
/// should defining such a variable be invalid.
static Expected<FileCheckNumericVariable *> parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
Optional<size_t> LineNumber, const SourceMgr &SM);
/// Parses \p Expr for a numeric substitution block. Parameter
/// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
/// expression. \returns a pointer to the class instance representing the AST /// expression and \p Context points to the class instance holding the live
/// of the expression whose value must be substituted, or an error holding a /// string and numeric variables. \returns a pointer to the class instance
/// diagnostic against \p SM if parsing fails. If substitution was /// representing the AST of the expression whose value must be substitued, or
/// successful, sets \p DefinedNumericVariable to point to the class /// an error holding a diagnostic against \p SM if parsing fails. If
/// representing the numeric variable being defined in this numeric /// substitution was successful, sets \p DefinedNumericVariable to point to
/// the class representing the numeric variable defined in this numeric
/// substitution block, or None if this block does not define any variable. /// substitution block, or None if this block does not define any variable.
Expected<std::unique_ptr<FileCheckExpressionAST>> static Expected<std::unique_ptr<FileCheckExpressionAST>>
parseNumericSubstitutionBlock( parseNumericSubstitutionBlock(
StringRef Expr, StringRef Expr,
Optional<FileCheckNumericVariable *> &DefinedNumericVariable, Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, const SourceMgr &SM) const; bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
/// instance accordingly. /// instance accordingly.
/// ///
@ -581,28 +598,49 @@ private:
/// was not found. /// was not found.
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
/// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use. /// Parses \p Expr for the name of a numeric variable to be defined at line
/// \returns the pointer to the class instance representing that variable if /// \p LineNumber, or before input is parsed if \p LineNumber is None.
/// successful, or an error holding a diagnostic against \p SM otherwise. /// \returns a pointer to the class instance representing that variable,
Expected<std::unique_ptr<FileCheckNumericVariableUse>> /// creating it if needed, or an error holding a diagnostic against \p SM
parseNumericVariableUse(StringRef Name, bool IsPseudo, /// should defining such a variable be invalid.
const SourceMgr &SM) const; static Expected<FileCheckNumericVariable *> parseNumericVariableDefinition(
enum class AllowedOperand { LineVar, Literal, Any }; StringRef &Expr, FileCheckPatternContext *Context,
/// Parses \p Expr for use of a numeric operand. Accepts both literal values Optional<size_t> LineNumber, FileCheckExpressionAST *ExpressionAST,
/// and numeric variables, depending on the value of \p AO. \returns the const SourceMgr &SM);
/// class representing that operand in the AST of the expression or an error /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use
/// holding a diagnostic against \p SM otherwise. /// at line \p LineNumber, or before input is parsed if \p LineNumber is
Expected<std::unique_ptr<FileCheckExpressionAST>> /// None. Parameter \p Context points to the class instance holding the live
parseNumericOperand(StringRef &Expr, AllowedOperand AO, /// string and numeric variables. \returns the pointer to the class instance
const SourceMgr &SM) const; /// representing that variable if successful, or an error holding a
/// Parses \p Expr for a binary operation. The left operand of this binary
/// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
/// we are parsing a legacy @LINE expression. \returns the class representing
/// the binary operation in the AST of the expression, or an error holding a
/// diagnostic against \p SM otherwise. /// diagnostic against \p SM otherwise.
Expected<std::unique_ptr<FileCheckExpressionAST>> static Expected<std::unique_ptr<FileCheckNumericVariableUse>>
parseNumericVariableUse(StringRef Name, bool IsPseudo,
Optional<size_t> LineNumber,
FileCheckPatternContext *Context,
const SourceMgr &SM);
enum class AllowedOperand { LineVar, Literal, Any };
/// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
/// before input is parsed if \p LineNumber is None. Accepts both literal
/// values and numeric variables, depending on the value of \p AO. Parameter
/// \p Context points to the class instance holding the live string and
/// numeric variables. \returns the class representing that operand in the
/// AST of the expression or an error holding a diagnostic against \p SM
/// otherwise.
static Expected<std::unique_ptr<FileCheckExpressionAST>>
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
/// Parses \p Expr for a binary operation at line \p LineNumber, or before
/// input is parsed if \p LineNumber is None. The left operand of this binary
/// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
/// we are parsing a legacy @LINE expression. Parameter \p Context points to
/// the class instance holding the live string and numeric variables.
/// \returns the class representing the binary operation in the AST of the
/// expression, or an error holding a diagnostic against \p SM otherwise.
static Expected<std::unique_ptr<FileCheckExpressionAST>>
parseBinop(StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp, parseBinop(StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
bool IsLegacyLineExpr, const SourceMgr &SM) const; bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
}; };
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -24,11 +24,38 @@
using namespace llvm; using namespace llvm;
bool FileCheckNumericVariable::isValueKnownAtMatchTime() const {
if (Value)
return true;
return ExpressionAST != nullptr;
}
void FileCheckNumericVariable::setValue(uint64_t NewValue) {
if (ExpressionAST != nullptr) {
// Caller is expected to call setValue only if substitution was successful.
assert(NewValue == cantFail(ExpressionAST->eval(),
"Failed to evaluate associated expression when "
"sanity checking value") &&
"Value being set to different from variable evaluation");
}
Value = NewValue;
// Clear pointer to AST to ensure it is not used after the numeric
// substitution defining this variable is processed since it's the
// substitution that owns the pointer.
ExpressionAST = nullptr;
}
Expected<uint64_t> FileCheckNumericVariableUse::eval() const { Expected<uint64_t> FileCheckNumericVariableUse::eval() const {
Optional<uint64_t> Value = NumericVariable->getValue(); Optional<uint64_t> Value = NumericVariable->getValue();
if (Value) if (Value)
return *Value; return *Value;
return make_error<FileCheckUndefVarError>(Name);
FileCheckExpressionAST *ExpressionAST = NumericVariable->getExpressionAST();
if (!ExpressionAST)
return make_error<FileCheckUndefVarError>(Name);
return ExpressionAST->eval();
} }
Expected<uint64_t> FileCheckASTBinop::eval() const { Expected<uint64_t> FileCheckASTBinop::eval() const {
@ -114,7 +141,8 @@ char FileCheckNotFoundError::ID = 0;
Expected<FileCheckNumericVariable *> Expected<FileCheckNumericVariable *>
FileCheckPattern::parseNumericVariableDefinition( FileCheckPattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context, StringRef &Expr, FileCheckPatternContext *Context,
Optional<size_t> LineNumber, const SourceMgr &SM) { Optional<size_t> LineNumber, FileCheckExpressionAST *ExpressionAST,
const SourceMgr &SM) {
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM); Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult) if (!ParseVarResult)
return ParseVarResult.takeError(); return ParseVarResult.takeError();
@ -141,14 +169,17 @@ FileCheckPattern::parseNumericVariableDefinition(
if (VarTableIter != Context->GlobalNumericVariableTable.end()) if (VarTableIter != Context->GlobalNumericVariableTable.end())
DefinedNumericVariable = VarTableIter->second; DefinedNumericVariable = VarTableIter->second;
else else
DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber); DefinedNumericVariable =
Context->makeNumericVariable(Name, LineNumber, ExpressionAST);
return DefinedNumericVariable; return DefinedNumericVariable;
} }
Expected<std::unique_ptr<FileCheckNumericVariableUse>> Expected<std::unique_ptr<FileCheckNumericVariableUse>>
FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo,
const SourceMgr &SM) const { Optional<size_t> LineNumber,
FileCheckPatternContext *Context,
const SourceMgr &SM) {
if (IsPseudo && !Name.equals("@LINE")) if (IsPseudo && !Name.equals("@LINE"))
return FileCheckErrorDiagnostic::get( return FileCheckErrorDiagnostic::get(
SM, Name, "invalid pseudo numeric variable '" + Name + "'"); SM, Name, "invalid pseudo numeric variable '" + Name + "'");
@ -171,24 +202,29 @@ FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo,
} }
Optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber(); Optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber();
if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber) if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber &&
!NumericVariable->isValueKnownAtMatchTime())
return FileCheckErrorDiagnostic::get( return FileCheckErrorDiagnostic::get(
SM, Name, SM, Name,
"numeric variable '" + Name + "' defined on the same line as used"); "numeric variable '" + Name +
"' defined from input on the same line as used");
return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable); return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable);
} }
Expected<std::unique_ptr<FileCheckExpressionAST>> Expected<std::unique_ptr<FileCheckExpressionAST>>
FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO,
const SourceMgr &SM) const { Optional<size_t> LineNumber,
FileCheckPatternContext *Context,
const SourceMgr &SM) {
if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
// Try to parse as a numeric variable use. // Try to parse as a numeric variable use.
Expected<FileCheckPattern::VariableProperties> ParseVarResult = Expected<FileCheckPattern::VariableProperties> ParseVarResult =
parseVariable(Expr, SM); parseVariable(Expr, SM);
if (ParseVarResult) if (ParseVarResult)
return parseNumericVariableUse(ParseVarResult->Name, return parseNumericVariableUse(ParseVarResult->Name,
ParseVarResult->IsPseudo, SM); ParseVarResult->IsPseudo, LineNumber,
Context, SM);
if (AO == AllowedOperand::LineVar) if (AO == AllowedOperand::LineVar)
return ParseVarResult.takeError(); return ParseVarResult.takeError();
// Ignore the error and retry parsing as a literal. // Ignore the error and retry parsing as a literal.
@ -212,10 +248,10 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp - RightOp; return LeftOp - RightOp;
} }
Expected<std::unique_ptr<FileCheckExpressionAST>> Expected<std::unique_ptr<FileCheckExpressionAST>> FileCheckPattern::parseBinop(
FileCheckPattern::parseBinop(StringRef &Expr, StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
std::unique_ptr<FileCheckExpressionAST> LeftOp, bool IsLegacyLineExpr, Optional<size_t> LineNumber,
bool IsLegacyLineExpr, const SourceMgr &SM) const { FileCheckPatternContext *Context, const SourceMgr &SM) {
Expr = Expr.ltrim(SpaceChars); Expr = Expr.ltrim(SpaceChars);
if (Expr.empty()) if (Expr.empty())
return std::move(LeftOp); return std::move(LeftOp);
@ -246,7 +282,7 @@ FileCheckPattern::parseBinop(StringRef &Expr,
AllowedOperand AO = AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult = Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult =
parseNumericOperand(Expr, AO, SM); parseNumericOperand(Expr, AO, LineNumber, Context, SM);
if (!RightOpResult) if (!RightOpResult)
return RightOpResult; return RightOpResult;
@ -259,50 +295,54 @@ Expected<std::unique_ptr<FileCheckExpressionAST>>
FileCheckPattern::parseNumericSubstitutionBlock( FileCheckPattern::parseNumericSubstitutionBlock(
StringRef Expr, StringRef Expr,
Optional<FileCheckNumericVariable *> &DefinedNumericVariable, Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, const SourceMgr &SM) const { bool IsLegacyLineExpr, Optional<size_t> LineNumber,
// Parse the numeric variable definition. FileCheckPatternContext *Context, const SourceMgr &SM) {
std::unique_ptr<FileCheckExpressionAST> ExpressionAST = nullptr;
StringRef DefExpr = StringRef();
DefinedNumericVariable = None; DefinedNumericVariable = None;
// Save variable definition expression if any.
size_t DefEnd = Expr.find(':'); size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) { if (DefEnd != StringRef::npos) {
StringRef DefExpr = Expr.substr(0, DefEnd); DefExpr = Expr.substr(0, DefEnd);
StringRef UseExpr = Expr.substr(DefEnd + 1); Expr = Expr.substr(DefEnd + 1);
UseExpr = UseExpr.ltrim(SpaceChars);
if (!UseExpr.empty())
return FileCheckErrorDiagnostic::get(
SM, UseExpr,
"unexpected string after variable definition: '" + UseExpr + "'");
DefExpr = DefExpr.ltrim(SpaceChars);
Expected<FileCheckNumericVariable *> ParseResult =
parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
return nullptr;
} }
// Parse the expression itself. // Parse the expression itself.
Expr = Expr.ltrim(SpaceChars); Expr = Expr.ltrim(SpaceChars);
// The first operand in a legacy @LINE expression is always the @LINE pseudo if (!Expr.empty()) {
// variable. // The first operand in a legacy @LINE expression is always the @LINE
AllowedOperand AO = // pseudo variable.
IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; AllowedOperand AO =
Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
parseNumericOperand(Expr, AO, SM); Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
while (ParseResult && !Expr.empty()) { parseNumericOperand(Expr, AO, LineNumber, Context, SM);
ParseResult = while (ParseResult && !Expr.empty()) {
parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM); ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
// Legacy @LINE expressions only allow 2 operands. LineNumber, Context, SM);
if (ParseResult && IsLegacyLineExpr && !Expr.empty()) // Legacy @LINE expressions only allow 2 operands.
return FileCheckErrorDiagnostic::get( if (ParseResult && IsLegacyLineExpr && !Expr.empty())
SM, Expr, return FileCheckErrorDiagnostic::get(
"unexpected characters at end of expression '" + Expr + "'"); SM, Expr,
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
return ParseResult;
ExpressionAST = std::move(*ParseResult);
} }
if (!ParseResult)
return ParseResult; // Parse the numeric variable definition.
return std::move(*ParseResult); if (DefEnd != StringRef::npos) {
DefExpr = DefExpr.ltrim(SpaceChars);
Expected<FileCheckNumericVariable *> ParseResult =
parseNumericVariableDefinition(DefExpr, Context, LineNumber,
ExpressionAST.get(), SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
}
return std::move(ExpressionAST);
} }
bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@ -385,14 +425,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
continue; continue;
} }
// String and numeric substitution blocks. String substitution blocks come // String and numeric substitution blocks. Pattern substitution blocks come
// in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
// other regex) and assigns it to the string variable 'foo'. The latter // other regex) and assigns it to the string variable 'foo'. The latter
// substitutes foo's value. Numeric substitution blocks work the same way // substitutes foo's value. Numeric substitution blocks recognize the same
// as string ones, but start with a '#' sign after the double brackets. // form as string ones, but start with a '#' sign after the double
// Both string and numeric variable names must satisfy the regular // brackets. They also accept a combined form which sets a numeric variable
// expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch // to the evaluation of an expression. Both string and numeric variable
// some common errors. // names must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" to be
// valid, as this helps catch some common errors.
if (PatternStr.startswith("[[")) { if (PatternStr.startswith("[[")) {
StringRef UnparsedPatternStr = PatternStr.substr(2); StringRef UnparsedPatternStr = PatternStr.substr(2);
// Find the closing bracket pair ending the match. End is going to be an // Find the closing bracket pair ending the match. End is going to be an
@ -413,6 +454,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
PatternStr = UnparsedPatternStr.substr(End + 2); PatternStr = UnparsedPatternStr.substr(End + 2);
bool IsDefinition = false; bool IsDefinition = false;
bool SubstNeeded = false;
// Whether the substitution block is a legacy use of @LINE with string // Whether the substitution block is a legacy use of @LINE with string
// substitution block syntax. // substitution block syntax.
bool IsLegacyLineExpr = false; bool IsLegacyLineExpr = false;
@ -443,6 +485,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
bool IsPseudo = ParseVarResult->IsPseudo; bool IsPseudo = ParseVarResult->IsPseudo;
IsDefinition = (VarEndIdx != StringRef::npos); IsDefinition = (VarEndIdx != StringRef::npos);
SubstNeeded = !IsDefinition;
if (IsDefinition) { if (IsDefinition) {
if ((IsPseudo || !MatchStr.consume_front(":"))) { if ((IsPseudo || !MatchStr.consume_front(":"))) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
@ -477,22 +520,61 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
if (IsNumBlock) { if (IsNumBlock) {
Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, SM); IsLegacyLineExpr, LineNumber, Context,
SM);
if (!ParseResult) { if (!ParseResult) {
logAllUnhandledErrors(ParseResult.takeError(), errs()); logAllUnhandledErrors(ParseResult.takeError(), errs());
return true; return true;
} }
ExpressionAST = std::move(*ParseResult); ExpressionAST = std::move(*ParseResult);
SubstNeeded = ExpressionAST != nullptr;
if (DefinedNumericVariable) { if (DefinedNumericVariable) {
IsDefinition = true; IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName(); DefName = (*DefinedNumericVariable)->getName();
MatchRegexp = StringRef("[0-9]+"); }
} else if (SubstNeeded)
SubstStr = MatchStr; SubstStr = MatchStr;
else
MatchRegexp = "[0-9]+";
} }
// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
if (IsDefinition) {
RegExStr += '(';
++SubstInsertIdx;
if (IsNumBlock) {
FileCheckNumericVariableMatch NumericVariableDefinition = {
*DefinedNumericVariable, CurParen};
NumericVariableDefs[DefName] = NumericVariableDefinition;
// This store is done here rather than in match() to allow
// parseNumericVariableUse() to get the pointer to the class instance
// of the right variable definition corresponding to a given numeric
// variable use.
Context->GlobalNumericVariableTable[DefName] =
*DefinedNumericVariable;
} else {
VariableDefs[DefName] = CurParen;
// Mark string variable as defined to detect collisions between
// string and numeric variables in parseNumericVariableUse() and
// defineCmdlineVariables() when the latter is created later than the
// former. We cannot reuse GlobalVariableTable for this by populating
// it with an empty string since we would then lose the ability to
// detect the use of an undefined variable in match().
Context->DefinedVariableTable[DefName] = true;
}
++CurParen;
}
if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM))
return true;
if (IsDefinition)
RegExStr += ')';
// Handle substitutions: [[foo]] and [[#<foo expr>]]. // Handle substitutions: [[foo]] and [[#<foo expr>]].
if (!IsDefinition) { if (SubstNeeded) {
// Handle substitution of string variables that were defined earlier on // Handle substitution of string variables that were defined earlier on
// the same line by emitting a backreference. Expressions do not // the same line by emitting a backreference. Expressions do not
// support substituting a numeric variable defined on the same line. // support substituting a numeric variable defined on the same line.
@ -515,37 +597,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx); : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution); Substitutions.push_back(Substitution);
} }
continue;
} }
// Handle variable definitions: [[<def>:(...)]] and
// [[#(...)<def>:(...)]].
if (IsNumBlock) {
FileCheckNumericVariableMatch NumericVariableDefinition = {
*DefinedNumericVariable, CurParen};
NumericVariableDefs[DefName] = NumericVariableDefinition;
// This store is done here rather than in match() to allow
// parseNumericVariableUse() to get the pointer to the class instance
// of the right variable definition corresponding to a given numeric
// variable use.
Context->GlobalNumericVariableTable[DefName] = *DefinedNumericVariable;
} else {
VariableDefs[DefName] = CurParen;
// Mark the string variable as defined to detect collisions between
// string and numeric variables in parseNumericVariableUse() and
// DefineCmdlineVariables() when the latter is created later than the
// former. We cannot reuse GlobalVariableTable for this by populating
// it with an empty string since we would then lose the ability to
// detect the use of an undefined variable in match().
Context->DefinedVariableTable[DefName] = true;
}
RegExStr += '(';
++CurParen;
if (AddRegExToRegEx(MatchRegexp, CurParen, SM))
return true;
RegExStr += ')';
} }
// Handle fixed string matches. // Handle fixed string matches.
@ -1745,11 +1797,32 @@ Error FileCheckPatternContext::defineCmdlineVariables(
unsigned I = 0; unsigned I = 0;
Error Errs = Error::success(); Error Errs = Error::success();
std::string CmdlineDefsDiag; std::string CmdlineDefsDiag;
StringRef Prefix1 = "Global define #"; SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices;
StringRef Prefix2 = ": "; for (StringRef CmdlineDef : CmdlineDefines) {
for (StringRef CmdlineDef : CmdlineDefines) std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str();
CmdlineDefsDiag += size_t EqIdx = CmdlineDef.find('=');
(Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str(); if (EqIdx == StringRef::npos) {
CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0));
continue;
}
// Numeric variable definition.
if (CmdlineDef[0] == '#') {
// Append a copy of the command-line definition adapted to use the same
// format as in the input file to be able to reuse
// parseNumericSubstitutionBlock.
CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();
std::string SubstitutionStr = CmdlineDef;
SubstitutionStr[EqIdx] = ':';
CmdlineDefsIndices.push_back(
std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));
CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str();
} else {
CmdlineDefsDiag += DefPrefix;
CmdlineDefsIndices.push_back(
std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size()));
CmdlineDefsDiag += (CmdlineDef + "\n").str();
}
}
// Create a buffer with fake command line content in order to display // Create a buffer with fake command line content in order to display
// parsing diagnostic with location information and point to the // parsing diagnostic with location information and point to the
@ -1759,14 +1832,10 @@ Error FileCheckPatternContext::defineCmdlineVariables(
StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer(); StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc()); SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());
SmallVector<StringRef, 4> CmdlineDefsDiagVec; for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) {
CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/, StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first,
false /*KeepEmpty*/); CmdlineDefIndices.second);
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) { if (CmdlineDef.empty()) {
unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
size_t EqIdx = CmdlineDef.find('=');
if (EqIdx == StringRef::npos) {
Errs = joinErrors( Errs = joinErrors(
std::move(Errs), std::move(Errs),
FileCheckErrorDiagnostic::get( FileCheckErrorDiagnostic::get(
@ -1776,31 +1845,35 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// Numeric variable definition. // Numeric variable definition.
if (CmdlineDef[0] == '#') { if (CmdlineDef[0] == '#') {
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1); // Now parse the definition both to check that the syntax is correct and
Expected<FileCheckNumericVariable *> ParseResult = // to create the necessary class instance.
FileCheckPattern::parseNumericVariableDefinition(CmdlineName, this, StringRef CmdlineDefExpr = CmdlineDef.substr(1);
None, SM); Optional<FileCheckNumericVariable *> DefinedNumericVariable;
if (!ParseResult) { Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionASTResult =
Errs = joinErrors(std::move(Errs), ParseResult.takeError()); FileCheckPattern::parseNumericSubstitutionBlock(
CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
if (!ExpressionASTResult) {
Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
continue;
}
std::unique_ptr<FileCheckExpressionAST> ExpressionAST =
std::move(*ExpressionASTResult);
// Now evaluate the expression whose value this variable should be set
// to, since the expression of a command-line variable definition should
// only use variables defined earlier on the command-line. If not, this
// is an error and we report it.
Expected<uint64_t> Value = ExpressionAST->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue; continue;
} }
StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1); assert(DefinedNumericVariable && "No variable defined");
uint64_t Val; (*DefinedNumericVariable)->setValue(*Value);
if (CmdlineVal.getAsInteger(10, Val)) {
Errs = joinErrors(std::move(Errs),
FileCheckErrorDiagnostic::get(
SM, CmdlineVal,
"invalid value in numeric variable definition '" +
CmdlineVal + "'"));
continue;
}
FileCheckNumericVariable *DefinedNumericVariable = *ParseResult;
DefinedNumericVariable->setValue(Val);
// Record this variable definition. // Record this variable definition.
GlobalNumericVariableTable[DefinedNumericVariable->getName()] = GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] =
DefinedNumericVariable; *DefinedNumericVariable;
} else { } else {
// String variable definition. // String variable definition.
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('='); std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
@ -1837,7 +1910,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(
} }
GlobalVariableTable.insert(CmdlineNameVal); GlobalVariableTable.insert(CmdlineNameVal);
// Mark the string variable as defined to detect collisions between // Mark the string variable as defined to detect collisions between
// string and numeric variables in DefineCmdlineVariables when the latter // string and numeric variables in defineCmdlineVariables when the latter
// is created later than the former. We cannot reuse GlobalVariableTable // is created later than the former. We cannot reuse GlobalVariableTable
// for this by populating it with an empty string since we would then // for this by populating it with an empty string since we would then
// lose the ability to detect the use of an undefined variable in // lose the ability to detect the use of an undefined variable in

View File

@ -4,30 +4,22 @@
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
NUMERRCLIFMT: Global defines:1:20: error: invalid variable name NUMERRCLIFMT: Global defines:1:46: error: invalid variable name
NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
NUMERRCLIFMT-NEXT: {{^ \^$}} NUMERRCLIFMT-NEXT: {{^ \^$}}
; Invalid definition of pseudo variable. ; Invalid definition of pseudo variable.
RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \ RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO
NUMERRCLIPSEUDO: Global defines:1:20: error: definition of pseudo numeric variable unsupported NUMERRCLIPSEUDO: Global defines:1:45: error: definition of pseudo numeric variable unsupported
NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 (parsed as: {{\[\[#@VALUE:10\]\]}})
NUMERRCLIPSEUDO-NEXT: {{^ \^$}} NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
; Invalid definition of an expression. ; Invalid definition of an expression.
RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \ RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL
NUMERRCLITRAIL: Global defines:1:25: error: unexpected characters after numeric variable name NUMERRCLITRAIL: Global defines:1:51: error: unexpected characters after numeric variable name
NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
NUMERRCLITRAIL-NEXT: {{^ \^$}} NUMERRCLITRAIL-NEXT: {{^ \^$}}
; Invalid value: numeric expression instead of literal.
RUN: not FileCheck -D#VALUE1=3 -D#VALUE2='VALUE1 + 2' --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIEXPR
NUMERRCLIEXPR: Global defines:2:27: error: invalid value in numeric variable definition 'VALUE1 + 2'
NUMERRCLIEXPR-NEXT: Global define #2: #VALUE2=VALUE1 + 2
NUMERRCLIEXPR-NEXT: {{^ \^$}}

View File

@ -1,22 +1,38 @@
; Test functionality of -D# option: numeric variables are defined to the right ; Test functionality of -D# option: numeric variables are defined to the right
; value and CHECK directives using them match as expected given the value set. ; value and CHECK directives using them match as expected given the value set.
RUN: FileCheck -D#NUMVAL=12 --check-prefix CHECKNUM --input-file %s %s RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s
RUN: not FileCheck -D#NUMVAL=8 --check-prefix CHECKNUM --input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1
RUN: not FileCheck -D#NUMVAL=12 --check-prefix NUMNOT --input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2
RUN: FileCheck -D#NUMVAL=8 --check-prefixes NUMNOT --input-file %s %s RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2
RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
Numeric value = 12 Numeric value #1 = 8
CHECKNUM: Numeric value = [[#NUMVAL]] Numeric value #2 = 12
NUMNOT-NOT: Numeric value = [[#NUMVAL]] CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]]
NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]]
NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
NUMERRMSG: defines.txt:1:1: note: scanning from here NUMERRMSG1: defines.txt:1:1: note: scanning from here
NUMERRMSG: defines.txt:1:1: note: with "NUMVAL" equal to "8" NUMERRMSG1: defines.txt:1:1: note: with "NUMVAL1" equal to "7"
NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here NUMERRMSG1: defines.txt:[[#@LINE-10]]:1: note: possible intended match here
NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input NUMERRMSG2: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM2: expected string not found in input
NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here NUMERRMSG2: defines.txt:1:1: note: scanning from here
NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "NUMVAL" equal to "12" NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8"
NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8"
NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here
NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12"

View File

@ -82,6 +82,20 @@ CHECK-LABEL: USE MULTI VAR
CHECK-NEXT: [[#VAR2:]] CHECK-NEXT: [[#VAR2:]]
CHECK-NEXT: [[#VAR1+VAR2]] CHECK-NEXT: [[#VAR1+VAR2]]
; Numeric expression using a variable defined from a numeric expression.
DEF EXPR GOOD MATCH
42
41 43
; CHECK-LABEL: DEF EXPR GOOD MATCH
; CHECK-NEXT: [[# VAR42:VAR1+31]]
; CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]]
; Empty numeric expression.
EMPTY NUM EXPR
foo 104 bar
; CHECK-LABEL: EMPTY NUM EXPR
; CHECK-NEXT: foo [[#]] bar
; Numeric expression using undefined variables. ; Numeric expression using undefined variables.
RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
@ -145,9 +159,9 @@ CLI-STR-CONFLICT-NEXT: {{^ \^$}}
INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists
INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#STRVAR:\]\]}} INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#STRVAR:\]\]}}
INPUT-NUM-CONFLICT-NEXT: {{^ \^$}} INPUT-NUM-CONFLICT-NEXT: {{^ \^$}}
CLI-NUM-CONFLICT: Global defines:2:20: error: string variable with name 'STRVAR' already exists CLI-NUM-CONFLICT: Global defines:2:45: error: string variable with name 'STRVAR' already exists
CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42 CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42 (parsed as: {{\[\[#STRVAR:42\]\]}})
CLI-NUM-CONFLICT-NEXT: {{^ \^$}} CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
; Numeric variable definition with too big value. ; Numeric variable definition with too big value.
RUN: not FileCheck --check-prefix BIGVAL --input-file %s %s 2>&1 \ RUN: not FileCheck --check-prefix BIGVAL --input-file %s %s 2>&1 \
@ -160,3 +174,18 @@ BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value
BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
BIGVAL-MSG-NEXT: {{^ \^$}} BIGVAL-MSG-NEXT: {{^ \^$}}
; Verify that when a variable is set to an expression the expression is still
; checked.
RUN: not FileCheck --check-prefix DEF-EXPR-FAIL --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix DEF-EXPR-FAIL-MSG %s
DEF EXPR WRONG MATCH
20
43
DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH
DEF-EXPR-FAIL-NEXT: [[# VAR20:]]
DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]]
DEF-EXPR-FAIL-MSG: numeric-expression.txt:[[#@LINE-1]]:21: error: {{D}}EF-EXPR-FAIL-NEXT: is not on the line after the previous match
DEF-EXPR-FAIL-MSG-NEXT: {{D}}EF-EXPR-FAIL-NEXT: {{\[\[# VAR42: VAR20\+22\]\]}}
DEF-EXPR-FAIL-MSG-NEXT: {{^ \^$}}

View File

@ -49,15 +49,21 @@ expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames); EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
} }
// Return whether Err contains any FileCheckUndefVarError whose associated name
// is not ExpectedUndefVarName.
static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) { static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err)); expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
} }
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
TEST_F(FileCheckTest, NumericVariable) { TEST_F(FileCheckTest, NumericVariable) {
// Undefined variable: getValue and eval fail, error returned by eval holds // Undefined variable: isValueKnownAtMatchTime returns false, getValue and
// the name of the undefined variable. // eval fail, error returned by eval holds the name of the undefined
// variable.
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1); FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
EXPECT_EQ("FOO", FooVar.getName()); EXPECT_EQ("FOO", FooVar.getName());
EXPECT_FALSE(FooVar.isValueKnownAtMatchTime());
FileCheckNumericVariableUse FooVarUse = FileCheckNumericVariableUse FooVarUse =
FileCheckNumericVariableUse("FOO", &FooVar); FileCheckNumericVariableUse("FOO", &FooVar);
EXPECT_FALSE(FooVar.getValue()); EXPECT_FALSE(FooVar.getValue());
@ -67,7 +73,9 @@ TEST_F(FileCheckTest, NumericVariable) {
FooVar.setValue(42); FooVar.setValue(42);
// Defined variable: getValue and eval return value set. // Defined variable: isValueKnownAtMatchTime returns true, getValue and eval
// return value set.
EXPECT_TRUE(FooVar.isValueKnownAtMatchTime());
Optional<uint64_t> Value = FooVar.getValue(); Optional<uint64_t> Value = FooVar.getValue();
EXPECT_TRUE(bool(Value)); EXPECT_TRUE(bool(Value));
EXPECT_EQ(42U, *Value); EXPECT_EQ(42U, *Value);
@ -75,24 +83,51 @@ TEST_F(FileCheckTest, NumericVariable) {
EXPECT_TRUE(bool(EvalResult)); EXPECT_TRUE(bool(EvalResult));
EXPECT_EQ(42U, *EvalResult); EXPECT_EQ(42U, *EvalResult);
// Variable defined by numeric expression: isValueKnownAtMatchTime
// returns true, getValue and eval return value of expression, setValue
// clears expression.
std::unique_ptr<FileCheckNumericVariableUse> FooVarUsePtr =
llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
std::unique_ptr<FileCheckExpressionLiteral> One =
llvm::make_unique<FileCheckExpressionLiteral>(1);
FileCheckASTBinop Binop =
FileCheckASTBinop(doAdd, std::move(FooVarUsePtr), std::move(One));
FileCheckNumericVariable FoobarExprVar =
FileCheckNumericVariable("FOOBAR", 2, &Binop);
EXPECT_TRUE(FoobarExprVar.isValueKnownAtMatchTime());
EXPECT_FALSE(FoobarExprVar.getValue());
FileCheckNumericVariableUse FoobarExprVarUse =
FileCheckNumericVariableUse("FOOBAR", &FoobarExprVar);
EvalResult = FoobarExprVarUse.eval();
EXPECT_TRUE(bool(EvalResult));
EXPECT_EQ(43U, *EvalResult);
EXPECT_TRUE(FoobarExprVar.getExpressionAST());
FoobarExprVar.setValue(43);
EXPECT_FALSE(FoobarExprVar.getExpressionAST());
FoobarExprVar = FileCheckNumericVariable("FOOBAR", 2, &Binop);
EXPECT_TRUE(FoobarExprVar.getExpressionAST());
// Clearing variable: getValue and eval fail. Error returned by eval holds // Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable. // the name of the cleared variable.
FooVar.clearValue(); FooVar.clearValue();
Value = FooVar.getValue(); FoobarExprVar.clearValue();
EXPECT_FALSE(Value); EXPECT_FALSE(FoobarExprVar.getExpressionAST());
EXPECT_FALSE(FooVar.getValue());
EXPECT_FALSE(FoobarExprVar.getValue());
EvalResult = FooVarUse.eval(); EvalResult = FooVarUse.eval();
EXPECT_FALSE(EvalResult); EXPECT_FALSE(EvalResult);
expectUndefError("FOO", EvalResult.takeError()); expectUndefError("FOO", EvalResult.takeError());
EvalResult = FoobarExprVarUse.eval();
EXPECT_FALSE(EvalResult);
expectUndefError("FOOBAR", EvalResult.takeError());
} }
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
TEST_F(FileCheckTest, Binop) { TEST_F(FileCheckTest, Binop) {
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO"); FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
FooVar.setValue(42); FooVar.setValue(42);
std::unique_ptr<FileCheckNumericVariableUse> FooVarUse = std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar); llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR"); FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 2);
BarVar.setValue(18); BarVar.setValue(18);
std::unique_ptr<FileCheckNumericVariableUse> BarVarUse = std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar); llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
@ -237,19 +272,13 @@ public:
P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++); P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
} }
bool parseNumVarDefExpect(StringRef Expr) {
StringRef ExprBufferRef = bufferize(SM, Expr);
return errorToBool(FileCheckPattern::parseNumericVariableDefinition(
ExprBufferRef, &Context, LineNumber, SM)
.takeError());
}
bool parseSubstExpect(StringRef Expr) { bool parseSubstExpect(StringRef Expr) {
StringRef ExprBufferRef = bufferize(SM, Expr); StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<FileCheckNumericVariable *> DefinedNumericVariable; Optional<FileCheckNumericVariable *> DefinedNumericVariable;
return errorToBool(P.parseNumericSubstitutionBlock( return errorToBool(
ExprBufferRef, DefinedNumericVariable, false, SM) P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable,
.takeError()); false, LineNumber - 1, &Context, SM)
.takeError());
} }
bool parsePatternExpect(StringRef Pattern) { bool parsePatternExpect(StringRef Pattern) {
@ -264,19 +293,6 @@ public:
} }
}; };
TEST_F(FileCheckTest, ParseNumericVariableDefinition) {
PatternTester Tester;
// Invalid definition of pseudo.
EXPECT_TRUE(Tester.parseNumVarDefExpect("@LINE"));
// Conflict with pattern variable.
EXPECT_TRUE(Tester.parseNumVarDefExpect("BAR"));
// Defined variable.
EXPECT_FALSE(Tester.parseNumVarDefExpect("FOO"));
}
TEST_F(FileCheckTest, ParseExpr) { TEST_F(FileCheckTest, ParseExpr) {
PatternTester Tester; PatternTester Tester;
@ -287,17 +303,18 @@ TEST_F(FileCheckTest, ParseExpr) {
EXPECT_TRUE(Tester.parseSubstExpect("@FOO:")); EXPECT_TRUE(Tester.parseSubstExpect("@FOO:"));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE:")); EXPECT_TRUE(Tester.parseSubstExpect("@LINE:"));
// Conflict with pattern variable.
EXPECT_TRUE(Tester.parseSubstExpect("BAR:"));
// Garbage after name of variable being defined. // Garbage after name of variable being defined.
EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:")); EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:"));
// Variable defined to numeric expression.
EXPECT_TRUE(Tester.parseSubstExpect("VAR1: FOO"));
// Acceptable variable definition. // Acceptable variable definition.
EXPECT_FALSE(Tester.parseSubstExpect("VAR1:")); EXPECT_FALSE(Tester.parseSubstExpect("VAR1:"));
EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:")); EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:"));
EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :")); EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :"));
EXPECT_FALSE(Tester.parseSubstExpect("VAR3: ")); EXPECT_FALSE(Tester.parseSubstExpect("VAR3: "));
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOOBAR: FOO+1]]"));
// Numeric expression. // Numeric expression.
@ -310,9 +327,21 @@ TEST_F(FileCheckTest, ParseExpr) {
EXPECT_FALSE(Tester.parseSubstExpect("FOO")); EXPECT_FALSE(Tester.parseSubstExpect("FOO"));
EXPECT_FALSE(Tester.parseSubstExpect("UNDEF")); EXPECT_FALSE(Tester.parseSubstExpect("UNDEF"));
// Use variable defined on same line. // Valid empty expression.
EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:]]")); EXPECT_FALSE(Tester.parseSubstExpect(""));
EXPECT_TRUE(Tester.parseSubstExpect("LINE1VAR"));
// Valid use of variable defined on the same line from expression. Note that
// the same pattern object is used for the parsePatternExpect and
// parseSubstExpect since no initNextPattern is called, thus appearing as
// being on the same line from the pattern's point of view.
EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:FOO+1]]"));
EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR"));
// Invalid use of variable defined on same line from input. As above, the
// absence of a call to initNextPattern makes it appear to be on the same
// line from the pattern's point of view.
EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE2VAR:]]"));
EXPECT_TRUE(Tester.parseSubstExpect("LINE2VAR"));
// Unsupported operator. // Unsupported operator.
EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2")); EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));
@ -323,6 +352,7 @@ TEST_F(FileCheckTest, ParseExpr) {
// Valid expression. // Valid expression.
EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5")); EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
EXPECT_FALSE(Tester.parseSubstExpect("FOO+4")); EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
EXPECT_FALSE(Tester.parseSubstExpect("FOOBAR"));
Tester.initNextPattern(); Tester.initNextPattern();
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
@ -354,7 +384,6 @@ TEST_F(FileCheckTest, ParsePattern) {
EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]")); EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]")); EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]")); EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]"));
// Valid numeric expressions and numeric variable definition. // Valid numeric expressions and numeric variable definition.
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]"));
@ -365,9 +394,16 @@ TEST_F(FileCheckTest, ParsePattern) {
TEST_F(FileCheckTest, Match) { TEST_F(FileCheckTest, Match) {
PatternTester Tester; PatternTester Tester;
// Check matching an empty expression only matches a number.
Tester.parsePatternExpect("[[#]]");
EXPECT_TRUE(Tester.matchExpect("FAIL"));
EXPECT_FALSE(Tester.matchExpect("18"));
// Check matching a definition only matches a number. // Check matching a definition only matches a number.
Tester.initNextPattern();
Tester.parsePatternExpect("[[#NUMVAR:]]"); Tester.parsePatternExpect("[[#NUMVAR:]]");
EXPECT_TRUE(Tester.matchExpect("FAIL")); EXPECT_TRUE(Tester.matchExpect("FAIL"));
EXPECT_TRUE(Tester.matchExpect(""));
EXPECT_FALSE(Tester.matchExpect("18")); EXPECT_FALSE(Tester.matchExpect("18"));
// Check matching the variable defined matches the correct number only // Check matching the variable defined matches the correct number only
@ -381,16 +417,16 @@ TEST_F(FileCheckTest, Match) {
// the correct value for @LINE. // the correct value for @LINE.
Tester.initNextPattern(); Tester.initNextPattern();
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
// Ok, @LINE is 4 now. // Ok, @LINE is 5 now.
EXPECT_FALSE(Tester.matchExpect("4")); EXPECT_FALSE(Tester.matchExpect("5"));
Tester.initNextPattern(); Tester.initNextPattern();
// @LINE is now 5, match with substitution failure. // @LINE is now 6, match with substitution failure.
EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]"));
EXPECT_TRUE(Tester.matchExpect("FOO")); EXPECT_TRUE(Tester.matchExpect("FOO"));
Tester.initNextPattern(); Tester.initNextPattern();
// Check that @LINE is 6 as expected. // Check that @LINE is 7 as expected.
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
EXPECT_FALSE(Tester.matchExpect("6")); EXPECT_FALSE(Tester.matchExpect("7"));
} }
TEST_F(FileCheckTest, Substitution) { TEST_F(FileCheckTest, Substitution) {
@ -410,9 +446,9 @@ TEST_F(FileCheckTest, Substitution) {
// Substitutions of defined pseudo and non-pseudo numeric variables return // Substitutions of defined pseudo and non-pseudo numeric variables return
// the right value. // the right value.
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE"); FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 1);
FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 1);
LineVar.setValue(42); LineVar.setValue(42);
FileCheckNumericVariable NVar = FileCheckNumericVariable("N");
NVar.setValue(10); NVar.setValue(10);
auto LineVarUse = auto LineVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar); llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
@ -494,22 +530,29 @@ TEST_F(FileCheckTest, FileCheckContext) {
// Define local variables from command-line. // Define local variables from command-line.
GlobalDefines.clear(); GlobalDefines.clear();
// Clear local variables to remove dummy numeric variable x that
// parseNumericSubstitutionBlock would have created and stored in
// GlobalNumericVariableTable.
Cxt.clearLocalVars();
GlobalDefines.emplace_back(std::string("LocalVar=FOO")); GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar=")); GlobalDefines.emplace_back(std::string("EmptyVar="));
GlobalDefines.emplace_back(std::string("#LocalNumVar=18")); GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
EXPECT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM))); EXPECT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
// Check defined variables are present and undefined is absent. // Check defined variables are present and undefined is absent.
StringRef LocalVarStr = "LocalVar"; StringRef LocalVarStr = "LocalVar";
StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar"); StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
StringRef EmptyVarStr = "EmptyVar"; StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar"; StringRef UnknownVarStr = "UnknownVar";
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr); Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
Optional<FileCheckNumericVariable *> DefinedNumericVariable; Optional<FileCheckNumericVariable *> DefinedNumericVariable;
Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST = Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, SM); /*IsLegacyLineExpr=*/false,
/*LineNumber=*/1, &Cxt, SM);
EXPECT_TRUE(bool(LocalVar)); EXPECT_TRUE(bool(LocalVar));
EXPECT_EQ(*LocalVar, "FOO"); EXPECT_EQ(*LocalVar, "FOO");
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
@ -518,6 +561,14 @@ TEST_F(FileCheckTest, FileCheckContext) {
Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval(); Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal)); EXPECT_TRUE(bool(ExpressionVal));
EXPECT_EQ(*ExpressionVal, 18U); EXPECT_EQ(*ExpressionVal, 18U);
ExpressionAST =
P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false,
/*LineNumber=*/1, &Cxt, SM);
EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal));
EXPECT_EQ(*ExpressionVal, 20U);
EXPECT_TRUE(bool(EmptyVar)); EXPECT_TRUE(bool(EmptyVar));
EXPECT_EQ(*EmptyVar, ""); EXPECT_EQ(*EmptyVar, "");
EXPECT_TRUE(errorToBool(UnknownVar.takeError())); EXPECT_TRUE(errorToBool(UnknownVar.takeError()));
@ -533,7 +584,14 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError())); EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
ExpressionAST = P.parseNumericSubstitutionBlock( ExpressionAST = P.parseNumericSubstitutionBlock(
LocalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
/*LineNumber=*/2, &Cxt, SM);
EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
ExpressionAST = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
/*LineNumber=*/2, &Cxt, SM);
EXPECT_TRUE(bool(ExpressionAST)); EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval(); ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
@ -554,7 +612,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_EQ(*GlobalVar, "BAR"); EXPECT_EQ(*GlobalVar, "BAR");
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
ExpressionAST = P.parseNumericSubstitutionBlock( ExpressionAST = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
/*LineNumber=*/3, &Cxt, SM);
EXPECT_TRUE(bool(ExpressionAST)); EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval(); ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal)); EXPECT_TRUE(bool(ExpressionVal));
@ -565,7 +624,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
ExpressionAST = P.parseNumericSubstitutionBlock( ExpressionAST = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
/*LineNumber=*/4, &Cxt, SM);
EXPECT_TRUE(bool(ExpressionAST)); EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval(); ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal)); EXPECT_TRUE(bool(ExpressionVal));