FileCheck [7/12]: Arbitrary long numeric expressions

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch extend numeric expression to
support an arbitrary number of operands, either variable or literals.

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/D60387

llvm-svn: 366001
This commit is contained in:
Thomas Preud'homme 2019-07-13 13:24:30 +00:00
parent 04a7ee7970
commit 04e17bdb7c
7 changed files with 447 additions and 295 deletions

View File

@ -107,10 +107,12 @@ and from the command line.
Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines.
.. option:: -D#<NUMVAR>=<VALUE>
.. option:: -D#<NUMVAR>=<VALUE EXPRESSION>
Sets a filecheck numeric variable ``NUMVAR`` to ``<VALUE>`` that can be used
in ``CHECK:`` lines.
Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
``<VALUE EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
``FileCheck Numeric Variables and Expressions`` for details on the format
and meaning of ``<VALUE EXPRESSION>``.
.. option:: -version
@ -590,18 +592,15 @@ For example:
would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
The syntax of a numeric substitution is ``[[#<NUMVAR><op><offset>]]`` where:
The syntax of a numeric substitution is ``[[#<expr>]]`` where ``<expr>`` is an
expression. An expression is recursively defined as:
* ``<NUMVAR>`` is the name of a defined numeric variable.
* a numeric operand, or
* an expression followed by an operator and a numeric operand.
* ``<op>`` is an optional operation to perform on the value of ``<NUMVAR>``.
Currently supported operations are ``+`` and ``-``.
* ``<offset>`` is the immediate value that constitutes the second operand of
the operation ``<op>``. It must be present if ``<op>`` is present, absent
otherwise.
Spaces are accepted before, after and between any of these elements.
A numeric operand is a previously defined numeric variable, or an integer
literal. The supported operators are ``+`` and ``-``. Spaces are accepted
before, after and between any of these elements.
For example:

View File

@ -40,6 +40,54 @@ struct FileCheckRequest {
// Numeric substitution handling code.
//===----------------------------------------------------------------------===//
/// Base class representing the AST of a given expression.
class FileCheckExpressionAST {
public:
virtual ~FileCheckExpressionAST() = default;
/// Evaluates and \returns the value of the expression represented by this
/// AST or an error if evaluation fails.
virtual Expected<uint64_t> eval() const = 0;
};
/// Class representing an unsigned literal in the AST of an expression.
class FileCheckExpressionLiteral : public FileCheckExpressionAST {
private:
/// Actual value of the literal.
uint64_t Value;
public:
/// Constructs a literal with the specified value.
FileCheckExpressionLiteral(uint64_t Val) : Value(Val) {}
/// \returns the literal's value.
Expected<uint64_t> eval() const { return Value; }
};
/// Class to represent an undefined variable error, which quotes that
/// variable's name when printed.
class FileCheckUndefVarError : public ErrorInfo<FileCheckUndefVarError> {
private:
StringRef VarName;
public:
static char ID;
FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {}
StringRef getVarName() const { return VarName; }
std::error_code convertToErrorCode() const override {
return inconvertibleErrorCode();
}
/// Print name of variable associated with this error.
void log(raw_ostream &OS) const override {
OS << "\"";
OS.write_escaped(VarName) << "\"";
}
};
/// Class representing a numeric variable and its associated current value.
class FileCheckNumericVariable {
private:
@ -81,56 +129,53 @@ public:
size_t getDefLineNumber() { return DefLineNumber; }
};
/// Class representing the use of a numeric variable in the AST of an
/// expression.
class FileCheckNumericVariableUse : public FileCheckExpressionAST {
private:
/// Name of the numeric variable.
StringRef Name;
/// Pointer to the class instance for the variable this use is about.
FileCheckNumericVariable *NumericVariable;
public:
FileCheckNumericVariableUse(StringRef Name,
FileCheckNumericVariable *NumericVariable)
: Name(Name), NumericVariable(NumericVariable) {}
/// \returns the value of the variable referenced by this instance.
Expected<uint64_t> eval() const;
};
/// Type of functions evaluating a given binary operation.
using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
/// Class to represent an undefined variable error which prints that variable's
/// name between quotes when printed.
class FileCheckUndefVarError : public ErrorInfo<FileCheckUndefVarError> {
private:
StringRef VarName;
public:
static char ID;
FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {}
StringRef getVarName() const { return VarName; }
std::error_code convertToErrorCode() const override {
return inconvertibleErrorCode();
}
/// Print name of variable associated with this error.
void log(raw_ostream &OS) const override {
OS << "\"";
OS.write_escaped(VarName) << "\"";
}
};
/// Class representing an expression consisting of either a single numeric
/// variable or a binary operation between a numeric variable and an
/// immediate.
class FileCheckExpression {
/// Class representing a single binary operation in the AST of an expression.
class FileCheckASTBinop : public FileCheckExpressionAST {
private:
/// Left operand.
FileCheckNumericVariable *LeftOp;
std::unique_ptr<FileCheckExpressionAST> LeftOperand;
/// Right operand.
uint64_t RightOp;
std::unique_ptr<FileCheckExpressionAST> RightOperand;
/// Pointer to function that can evaluate this binary operation.
binop_eval_t EvalBinop;
public:
FileCheckExpression(binop_eval_t EvalBinop,
FileCheckNumericVariable *OperandLeft,
uint64_t OperandRight)
: LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {}
FileCheckASTBinop(binop_eval_t EvalBinop,
std::unique_ptr<FileCheckExpressionAST> LeftOp,
std::unique_ptr<FileCheckExpressionAST> RightOp)
: EvalBinop(EvalBinop) {
LeftOperand = std::move(LeftOp);
RightOperand = std::move(RightOp);
}
/// Evaluates the value of this expression, using EvalBinop to perform the
/// binary operation it consists of. \returns an error if the numeric
/// variable used is undefined, or the expression value otherwise.
/// Evaluates the value of the binary operation represented by this AST,
/// using EvalBinop on the result of recursively evaluating the operands.
/// \returns the expression value or an error if an undefined numeric
/// variable is used in one of the operands.
Expected<uint64_t> eval() const;
};
@ -187,15 +232,15 @@ class FileCheckNumericSubstitution : public FileCheckSubstitution {
private:
/// Pointer to the class representing the expression whose value is to be
/// substituted.
FileCheckExpression *Expression;
std::unique_ptr<FileCheckExpressionAST> ExpressionAST;
public:
FileCheckNumericSubstitution(FileCheckPatternContext *Context,
StringRef ExpressionStr,
FileCheckExpression *Expression,
FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
std::unique_ptr<FileCheckExpressionAST> ExprAST,
size_t InsertIdx)
: FileCheckSubstitution(Context, ExpressionStr, InsertIdx),
Expression(Expression) {}
: FileCheckSubstitution(Context, Expr, InsertIdx) {
ExpressionAST = std::move(ExprAST);
}
/// \returns a string containing the result of evaluating the expression in
/// this substitution, or an error if evaluation failed.
@ -278,10 +323,6 @@ private:
/// easily updating its value.
FileCheckNumericVariable *LineVariable = nullptr;
/// Vector holding pointers to all parsed expressions. Used to automatically
/// free the expressions once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<FileCheckExpression>> Expressions;
/// Vector holding pointers to all parsed numeric variables. Used to
/// automatically free them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
@ -313,12 +354,6 @@ public:
void clearLocalVars();
private:
/// Makes a new expression instance and registers it for destruction when
/// the context is destroyed.
FileCheckExpression *makeExpression(binop_eval_t EvalBinop,
FileCheckNumericVariable *OperandLeft,
uint64_t OperandRight);
/// Makes a new numeric variable and registers it for destruction when the
/// context is destroyed.
template <class... Types>
@ -333,7 +368,8 @@ private:
/// the context is destroyed.
FileCheckSubstitution *
makeNumericSubstitution(StringRef ExpressionStr,
FileCheckExpression *Expression, size_t InsertIdx);
std::unique_ptr<FileCheckExpressionAST> ExpressionAST,
size_t InsertIdx);
};
/// Class to represent an error holding a diagnostic with location information
@ -458,13 +494,20 @@ public:
/// \returns whether \p C is a valid first character for a variable name.
static bool isValidVarNameStart(char C);
/// Parsing information about a variable.
struct VariableProperties {
StringRef Name;
bool IsPseudo;
};
/// Parses the string at the start of \p Str for a variable name. \returns
/// an error holding a diagnostic against \p SM if parsing fail, or the
/// name of the variable otherwise. In the latter case, sets \p IsPseudo to
/// indicate if it is a pseudo variable and strips \p Str from the variable
/// name.
static Expected<StringRef> parseVariable(StringRef &Str, bool &IsPseudo,
const SourceMgr &SM);
/// a VariableProperties structure holding the variable name and whether it
/// is the name of a pseudo variable, or an error holding a diagnostic
/// against \p SM if parsing fail. If parsing was successful, also strips
/// \p Str from the variable name.
static Expected<VariableProperties> parseVariable(StringRef &Str,
const SourceMgr &SM);
/// Parses \p Expr for the name of a numeric variable to be defined at line
/// \p LineNumber. \returns a pointer to the class instance representing that
/// variable, creating it if needed, or an error holding a diagnostic against
@ -473,16 +516,19 @@ public:
parseNumericVariableDefinition(StringRef &Expr,
FileCheckPatternContext *Context,
size_t LineNumber, const SourceMgr &SM);
/// Parses \p Expr for a numeric substitution block. \returns the class
/// representing the AST of the expression whose value must be substituted,
/// or an error holding a diagnostic against \p SM if parsing fails. If
/// substitution was successful, sets \p DefinedNumericVariable to point to
/// the class representing the numeric variable defined in this numeric
/// Parses \p Expr for a numeric substitution block. Parameter
/// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
/// expression. \returns a pointer to the class instance representing the AST
/// of the expression whose value must be substituted, or an error holding a
/// diagnostic against \p SM if parsing fails. If substitution was
/// successful, sets \p DefinedNumericVariable to point to the class
/// representing the numeric variable being defined in this numeric
/// substitution block, or None if this block does not define any variable.
Expected<FileCheckExpression *> parseNumericSubstitutionBlock(
Expected<std::unique_ptr<FileCheckExpressionAST>>
parseNumericSubstitutionBlock(
StringRef Expr,
Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
const SourceMgr &SM) const;
bool IsLegacyLineExpr, const SourceMgr &SM) const;
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
/// instance accordingly.
///
@ -507,7 +553,7 @@ public:
Expected<size_t> match(StringRef Buffer, size_t &MatchLen,
const SourceMgr &SM) const;
/// Prints the value of successful substitutions or the name of the undefined
/// string or numeric variable preventing a successful substitution.
/// string or numeric variables preventing a successful substitution.
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
SMRange MatchRange = None) const;
void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
@ -536,16 +582,28 @@ private:
/// was not found.
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
/// Parses \p Expr for the use of a numeric variable. \returns the pointer to
/// the class instance representing that variable if successful, or an error
/// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use.
/// \returns the pointer to the class instance representing that variable if
/// successful, or an error holding a diagnostic against \p SM otherwise.
Expected<std::unique_ptr<FileCheckNumericVariableUse>>
parseNumericVariableUse(StringRef Name, bool IsPseudo,
const SourceMgr &SM) const;
enum class AllowedOperand { LineVar, Literal, Any };
/// Parses \p Expr for use of a numeric operand. Accepts both literal values
/// and numeric variables, depending on the value of \p AO. \returns the
/// class representing that operand in the AST of the expression or an error
/// holding a diagnostic against \p SM otherwise.
Expected<FileCheckNumericVariable *>
parseNumericVariableUse(StringRef &Expr, const SourceMgr &SM) const;
/// Parses \p Expr for a binary operation.
/// \returns the class representing the binary operation of the expression,
/// or an error holding a diagnostic against \p SM otherwise.
Expected<FileCheckExpression *> parseBinop(StringRef &Expr,
const SourceMgr &SM) const;
Expected<std::unique_ptr<FileCheckExpressionAST>>
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
const SourceMgr &SM) const;
/// 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.
Expected<std::unique_ptr<FileCheckExpressionAST>>
parseBinop(StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
bool IsLegacyLineExpr, const SourceMgr &SM) const;
};
//===----------------------------------------------------------------------===//

View File

@ -35,17 +35,33 @@ void FileCheckNumericVariable::clearValue() {
Value = None;
}
Expected<uint64_t> FileCheckExpression::eval() const {
assert(LeftOp && "Evaluating an empty expression");
Optional<uint64_t> LeftOpValue = LeftOp->getValue();
// Variable is undefined.
if (!LeftOpValue)
return make_error<FileCheckUndefVarError>(LeftOp->getName());
return EvalBinop(*LeftOpValue, RightOp);
Expected<uint64_t> FileCheckNumericVariableUse::eval() const {
Optional<uint64_t> Value = NumericVariable->getValue();
if (Value)
return *Value;
return make_error<FileCheckUndefVarError>(Name);
}
Expected<uint64_t> FileCheckASTBinop::eval() const {
Expected<uint64_t> LeftOp = LeftOperand->eval();
Expected<uint64_t> RightOp = RightOperand->eval();
// Bubble up any error (e.g. undefined variables) in the recursive
// evaluation.
if (!LeftOp || !RightOp) {
Error Err = Error::success();
if (!LeftOp)
Err = joinErrors(std::move(Err), LeftOp.takeError());
if (!RightOp)
Err = joinErrors(std::move(Err), RightOp.takeError());
return std::move(Err);
}
return EvalBinop(*LeftOp, *RightOp);
}
Expected<std::string> FileCheckNumericSubstitution::getResult() const {
Expected<uint64_t> EvaluatedValue = Expression->eval();
Expected<uint64_t> EvaluatedValue = ExpressionAST->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
return utostr(*EvaluatedValue);
@ -63,15 +79,14 @@ bool FileCheckPattern::isValidVarNameStart(char C) {
return C == '_' || isalpha(C);
}
Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str,
bool &IsPseudo,
const SourceMgr &SM) {
Expected<FileCheckPattern::VariableProperties>
FileCheckPattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
if (Str.empty())
return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name");
bool ParsedOneChar = false;
unsigned I = 0;
IsPseudo = Str[0] == '@';
bool IsPseudo = Str[0] == '@';
// Global vars start with '$'.
if (Str[0] == '$' || IsPseudo)
@ -89,7 +104,7 @@ Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str,
StringRef Name = Str.take_front(I);
Str = Str.substr(I);
return Name;
return VariableProperties {Name, IsPseudo};
}
// StringRef holding all characters considered as horizontal whitespaces by
@ -111,13 +126,12 @@ Expected<FileCheckNumericVariable *>
FileCheckPattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber,
const SourceMgr &SM) {
bool IsPseudo;
Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
StringRef Name = *ParseVarResult;
StringRef Name = ParseVarResult->Name;
if (IsPseudo)
if (ParseVarResult->IsPseudo)
return FileCheckErrorDiagnostic::get(
SM, Name, "definition of pseudo numeric variable unsupported");
@ -143,15 +157,9 @@ FileCheckPattern::parseNumericVariableDefinition(
return DefinedNumericVariable;
}
Expected<FileCheckNumericVariable *>
FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
Expected<std::unique_ptr<FileCheckNumericVariableUse>>
FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo,
const SourceMgr &SM) const {
bool IsPseudo;
Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
StringRef Name = *ParseVarResult;
if (IsPseudo && !Name.equals("@LINE"))
return FileCheckErrorDiagnostic::get(
SM, Name, "invalid pseudo numeric variable '" + Name + "'");
@ -178,7 +186,32 @@ FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
SM, Name,
"numeric variable '" + Name + "' defined on the same line as used");
return NumericVariable;
return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable);
}
Expected<std::unique_ptr<FileCheckExpressionAST>>
FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO,
const SourceMgr &SM) const {
if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
// Try to parse as a numeric variable use.
Expected<FileCheckPattern::VariableProperties> ParseVarResult =
parseVariable(Expr, SM);
if (ParseVarResult)
return parseNumericVariableUse(ParseVarResult->Name,
ParseVarResult->IsPseudo, SM);
if (AO == AllowedOperand::LineVar)
return ParseVarResult.takeError();
// Ignore the error and retry parsing as a literal.
consumeError(ParseVarResult.takeError());
}
// Otherwise, parse it as a literal.
uint64_t LiteralValue;
if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
return llvm::make_unique<FileCheckExpressionLiteral>(LiteralValue);
return FileCheckErrorDiagnostic::get(SM, Expr,
"invalid operand format '" + Expr + "'");
}
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
@ -189,20 +222,16 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp - RightOp;
}
Expected<FileCheckExpression *>
FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const {
Expected<FileCheckNumericVariable *> LeftParseResult =
parseNumericVariableUse(Expr, SM);
if (!LeftParseResult) {
return LeftParseResult.takeError();
}
FileCheckNumericVariable *LeftOp = *LeftParseResult;
Expected<std::unique_ptr<FileCheckExpressionAST>>
FileCheckPattern::parseBinop(StringRef &Expr,
std::unique_ptr<FileCheckExpressionAST> LeftOp,
bool IsLegacyLineExpr, const SourceMgr &SM) const {
Expr = Expr.ltrim(SpaceChars);
if (Expr.empty())
return std::move(LeftOp);
// Check if this is a supported operation and select a function to perform
// it.
Expr = Expr.ltrim(SpaceChars);
if (Expr.empty())
return Context->makeExpression(add, LeftOp, 0);
SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
char Operator = popFront(Expr);
binop_eval_t EvalBinop;
@ -223,22 +252,24 @@ FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const {
if (Expr.empty())
return FileCheckErrorDiagnostic::get(SM, Expr,
"missing operand in expression");
uint64_t RightOp;
if (Expr.consumeInteger(10, RightOp))
return FileCheckErrorDiagnostic::get(
SM, Expr, "invalid offset in expression '" + Expr + "'");
Expr = Expr.ltrim(SpaceChars);
if (!Expr.empty())
return FileCheckErrorDiagnostic::get(
SM, Expr, "unexpected characters at end of expression '" + Expr + "'");
// The second operand in a legacy @LINE expression is always a literal.
AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult =
parseNumericOperand(Expr, AO, SM);
if (!RightOpResult)
return RightOpResult;
return Context->makeExpression(EvalBinop, LeftOp, RightOp);
Expr = Expr.ltrim(SpaceChars);
return llvm::make_unique<FileCheckASTBinop>(EvalBinop, std::move(LeftOp),
std::move(*RightOpResult));
}
Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock(
Expected<std::unique_ptr<FileCheckExpressionAST>>
FileCheckPattern::parseNumericSubstitutionBlock(
StringRef Expr,
Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
const SourceMgr &SM) const {
bool IsLegacyLineExpr, const SourceMgr &SM) const {
// Parse the numeric variable definition.
DefinedNumericVariable = None;
size_t DefEnd = Expr.find(':');
@ -259,12 +290,29 @@ Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock(
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
return Context->makeExpression(add, nullptr, 0);
return nullptr;
}
// Parse the expression itself.
Expr = Expr.ltrim(SpaceChars);
return parseBinop(Expr, SM);
// The first operand in a legacy @LINE expression is always the @LINE pseudo
// variable.
AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
parseNumericOperand(Expr, AO, SM);
while (ParseResult && !Expr.empty()) {
ParseResult =
parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM);
// Legacy @LINE expressions only allow 2 operands.
if (ParseResult && IsLegacyLineExpr && !Expr.empty())
return FileCheckErrorDiagnostic::get(
SM, Expr,
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
return ParseResult;
return std::move(*ParseResult);
}
bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@ -375,12 +423,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
PatternStr = UnparsedPatternStr.substr(End + 2);
bool IsDefinition = false;
// Whether the substitution block is a legacy use of @LINE with string
// substitution block syntax.
bool IsLegacyLineExpr = false;
StringRef DefName;
StringRef SubstStr;
StringRef MatchRegexp;
size_t SubstInsertIdx = RegExStr.size();
// Parse string variable or legacy expression.
// Parse string variable or legacy @LINE expression.
if (!IsNumBlock) {
size_t VarEndIdx = MatchStr.find(":");
size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
@ -391,15 +442,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
}
// Get the name (e.g. "foo") and verify it is well formed.
bool IsPseudo;
StringRef OrigMatchStr = MatchStr;
Expected<StringRef> ParseVarResult =
parseVariable(MatchStr, IsPseudo, SM);
Expected<FileCheckPattern::VariableProperties> ParseVarResult =
parseVariable(MatchStr, SM);
if (!ParseVarResult) {
logAllUnhandledErrors(ParseVarResult.takeError(), errs());
return true;
}
StringRef Name = *ParseVarResult;
StringRef Name = ParseVarResult->Name;
bool IsPseudo = ParseVarResult->IsPseudo;
IsDefinition = (VarEndIdx != StringRef::npos);
if (IsDefinition) {
@ -424,23 +475,24 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
} else {
if (IsPseudo) {
MatchStr = OrigMatchStr;
IsNumBlock = true;
IsLegacyLineExpr = IsNumBlock = true;
} else
SubstStr = Name;
}
}
// Parse numeric substitution block.
FileCheckExpression *Expression;
std::unique_ptr<FileCheckExpressionAST> ExpressionAST;
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
if (IsNumBlock) {
Expected<FileCheckExpression *> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, SM);
if (!ParseResult) {
logAllUnhandledErrors(ParseResult.takeError(), errs());
return true;
}
Expression = *ParseResult;
ExpressionAST = std::move(*ParseResult);
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName();
@ -468,8 +520,8 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
// previous CHECK patterns, and substitution of expressions.
FileCheckSubstitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(SubstStr, Expression,
SubstInsertIdx)
? Context->makeNumericSubstitution(
SubstStr, std::move(ExpressionAST), SubstInsertIdx)
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
@ -660,7 +712,7 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
Expected<std::string> MatchedValue = Substitution->getResult();
// Substitution failed or is not known at match time, print the undefined
// variable it uses.
// variables it uses.
if (!MatchedValue) {
bool UndefSeen = false;
handleAllErrors(MatchedValue.takeError(),
@ -669,13 +721,11 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
[](const FileCheckErrorDiagnostic &E) {},
[&](const FileCheckUndefVarError &E) {
if (!UndefSeen) {
OS << "uses undefined variable ";
OS << "uses undefined variable(s):";
UndefSeen = true;
}
OS << " ";
E.log(OS);
},
[](const ErrorInfoBase &E) {
llvm_unreachable("Unexpected error");
});
} else {
// Substitution succeeded. Print substituted value.
@ -768,15 +818,6 @@ FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
return VarIter->second;
}
FileCheckExpression *
FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop,
FileCheckNumericVariable *OperandLeft,
uint64_t OperandRight) {
Expressions.push_back(llvm::make_unique<FileCheckExpression>(
EvalBinop, OperandLeft, OperandRight));
return Expressions.back().get();
}
template <class... Types>
FileCheckNumericVariable *
FileCheckPatternContext::makeNumericVariable(Types... args) {
@ -794,10 +835,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
}
FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution(
StringRef ExpressionStr, FileCheckExpression *Expression,
size_t InsertIdx) {
StringRef ExpressionStr,
std::unique_ptr<FileCheckExpressionAST> ExpressionAST, size_t InsertIdx) {
Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>(
this, ExpressionStr, Expression, InsertIdx));
this, ExpressionStr, std::move(ExpressionAST), InsertIdx));
return Substitutions.back().get();
}
@ -1777,9 +1818,8 @@ Error FileCheckPatternContext::defineCmdlineVariables(
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
StringRef CmdlineName = CmdlineNameVal.first;
StringRef OrigCmdlineName = CmdlineName;
bool IsPseudo;
Expected<StringRef> ParseVarResult =
FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM);
Expected<FileCheckPattern::VariableProperties> ParseVarResult =
FileCheckPattern::parseVariable(CmdlineName, SM);
if (!ParseVarResult) {
Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
continue;
@ -1787,7 +1827,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// Check that CmdlineName does not denote a pseudo variable is only
// composed of the parsed numeric variable. This catches cases like
// "FOO+2" in a "FOO+2=10" definition.
if (IsPseudo || !CmdlineName.empty()) {
if (ParseVarResult->IsPseudo || !CmdlineName.empty()) {
Errs = joinErrors(std::move(Errs),
FileCheckErrorDiagnostic::get(
SM, OrigCmdlineName,
@ -1795,7 +1835,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(
OrigCmdlineName + "'"));
continue;
}
StringRef Name = *ParseVarResult;
StringRef Name = ParseVarResult->Name;
// Detect collisions between string and numeric variables when the former
// is created later than the latter.

View File

@ -50,7 +50,7 @@
50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*'
51
52 BAD10: [[@LINE-x]]
53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in expression 'x'
53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x'
54
55 BAD11: [[@LINE-1x]]
56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'

View File

@ -59,8 +59,8 @@ CHECK-NEXT: [[# VAR1 -1]]
CHECK-NEXT: [[# VAR1 - 1]]
CHECK-NEXT: [[# VAR1 - 1 ]]
; Numeric expressions using variables defined on the command-line and an
; immediate interpreted as an unsigned value.
; Numeric expressions using variables defined on other lines and an immediate
; interpreted as an unsigned value.
; Note: 9223372036854775819 = 0x8000000000000000 + 11
; 9223372036854775808 = 0x8000000000000000
USE UNSIGNED IMM
@ -68,21 +68,29 @@ USE UNSIGNED IMM
CHECK-LABEL: USE UNSIGNED IMM
CHECK-NEXT: [[#VAR1+9223372036854775808]]
; Numeric expression using undefined variable.
; Numeric expressions using more than one variable defined on other lines.
USE MULTI VAR
31
42
CHECK-LABEL: USE MULTI VAR
CHECK-NEXT: [[#VAR2:]]
CHECK-NEXT: [[#VAR1+VAR2]]
; Numeric expression using undefined variables.
RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
UNDEF VAR USE
UNDEFVAR: 11
UNDEF-USE-LABEL: UNDEF VAR USE
UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]]
UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]]
UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:17: error: {{U}}NDEF-USE-NEXT: expected string not found in input
UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}}
UNDEF-USE-MSG-NEXT: {{^ \^$}}
UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-6]]:1: note: scanning from here
UNDEF-USE-MSG-NEXT: UNDEFVAR: 11
UNDEF-USE-MSG-NEXT: {{^\^$}}
UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable "UNDEFVAR"
UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2"
UNDEF-USE-MSG-NEXT: UNDEFVAR: 11
UNDEF-USE-MSG-NEXT: {{^\^$}}

View File

@ -34,5 +34,5 @@ LOCAL3: [[LOCAL]][[#LOCNUM+2]]
GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]]
ERRUNDEF: expected string not found in input
ERRUNDEFLOCAL: uses undefined variable "LOCAL"
ERRUNDEFLOCNUM: uses undefined variable "LOCNUM"
ERRUNDEFLOCAL: uses undefined variable(s): "LOCAL"
ERRUNDEFLOCNUM: uses undefined variable(s): "LOCNUM"

View File

@ -8,56 +8,112 @@
#include "llvm/Support/FileCheck.h"
#include "gtest/gtest.h"
#include <unordered_set>
using namespace llvm;
namespace {
class FileCheckTest : public ::testing::Test {};
TEST_F(FileCheckTest, Literal) {
// Eval returns the literal's value.
FileCheckExpressionLiteral Ten(10);
Expected<uint64_t> Value = Ten.eval();
EXPECT_TRUE(bool(Value));
EXPECT_EQ(10U, *Value);
// Max value can be correctly represented.
FileCheckExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
Value = Max.eval();
EXPECT_TRUE(bool(Value));
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
}
static std::string toString(const std::unordered_set<std::string> &Set) {
bool First = true;
std::string Str;
for (StringRef S : Set) {
Str += Twine(First ? "{" + S : ", " + S).str();
First = false;
}
Str += '}';
return Str;
}
static void
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
Error Err) {
handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
ExpectedUndefVarNames.erase(E.getVarName());
});
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
}
static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
}
TEST_F(FileCheckTest, NumericVariable) {
// Undefined variable: getValue fails, setValue does not trigger assert.
// Undefined variable: getValue and eval fail, error returned by eval holds
// the name of the undefined variable and setValue does not trigger assert.
FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO");
EXPECT_EQ("FOO", FooVar.getName());
llvm::Optional<uint64_t> Value = FooVar.getValue();
EXPECT_FALSE(Value);
FooVar.clearValue();
FileCheckNumericVariableUse FooVarUse =
FileCheckNumericVariableUse("FOO", &FooVar);
EXPECT_FALSE(FooVar.getValue());
Expected<uint64_t> EvalResult = FooVarUse.eval();
EXPECT_FALSE(EvalResult);
expectUndefError("FOO", EvalResult.takeError());
FooVar.setValue(42);
// Defined variable: getValue returns value set.
Value = FooVar.getValue();
EXPECT_TRUE(Value);
// Defined variable: getValue and eval return value set.
Optional<uint64_t> Value = FooVar.getValue();
EXPECT_TRUE(bool(Value));
EXPECT_EQ(42U, *Value);
EvalResult = FooVarUse.eval();
EXPECT_TRUE(bool(EvalResult));
EXPECT_EQ(42U, *EvalResult);
// Clearing variable: getValue fails.
// Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable.
FooVar.clearValue();
Value = FooVar.getValue();
EXPECT_FALSE(Value);
EvalResult = FooVarUse.eval();
EXPECT_FALSE(EvalResult);
expectUndefError("FOO", EvalResult.takeError());
}
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
static void expectUndefError(const Twine &ExpectedStr, Error Err) {
handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
EXPECT_EQ(ExpectedStr.str(), E.getVarName());
});
}
TEST_F(FileCheckTest, Expression) {
TEST_F(FileCheckTest, Binop) {
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
FileCheckExpression Expression = FileCheckExpression(doAdd, &FooVar, 18);
std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 18);
std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
FileCheckASTBinop Binop =
FileCheckASTBinop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
// Defined variable: eval returns right value.
Expected<uint64_t> Value = Expression.eval();
Expected<uint64_t> Value = Binop.eval();
EXPECT_TRUE(bool(Value));
EXPECT_EQ(60U, *Value);
// Undefined variable: eval fails, undefined variable returned. We call
// getUndefVarName first to check that it can be called without calling
// eval() first.
// 1 undefined variable: eval fails, error contains name of undefined
// variable.
FooVar.clearValue();
Error EvalError = Expression.eval().takeError();
EXPECT_TRUE(errorToBool(std::move(EvalError)));
expectUndefError("FOO", std::move(EvalError));
Value = Binop.eval();
EXPECT_FALSE(Value);
expectUndefError("FOO", Value.takeError());
// 2 undefined variables: eval fails, error contains names of all undefined
// variables.
BarVar.clearValue();
Value = Binop.eval();
EXPECT_FALSE(Value);
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
}
TEST_F(FileCheckTest, ValidVarNameStart) {
@ -84,77 +140,69 @@ TEST_F(FileCheckTest, ParseVar) {
SourceMgr SM;
StringRef OrigVarName = bufferize(SM, "GoodVar42");
StringRef VarName = OrigVarName;
bool IsPseudo = true;
Expected<StringRef> ParsedName =
FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
EXPECT_EQ(*ParsedName, OrigVarName);
Expected<FileCheckPattern::VariableProperties> ParsedVarResult =
FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_FALSE(IsPseudo);
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
EXPECT_EQ(*ParsedName, OrigVarName);
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_FALSE(IsPseudo);
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
EXPECT_EQ(*ParsedName, OrigVarName);
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_TRUE(IsPseudo);
EXPECT_TRUE(ParsedVarResult->IsPseudo);
VarName = bufferize(SM, "42BadVar");
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(errorToBool(ParsedName.takeError()));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
VarName = bufferize(SM, "$@");
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(errorToBool(ParsedName.takeError()));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
VarName = OrigVarName = bufferize(SM, "B@dVar");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(VarName, OrigVarName.substr(1));
EXPECT_EQ(*ParsedName, "B");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(ParsedVarResult->Name, "B");
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = OrigVarName = bufferize(SM, "B$dVar");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(VarName, OrigVarName.substr(1));
EXPECT_EQ(*ParsedName, "B");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(ParsedVarResult->Name, "B");
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = bufferize(SM, "BadVar+");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(VarName, "+");
EXPECT_EQ(*ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = bufferize(SM, "BadVar-");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(VarName, "-");
EXPECT_EQ(*ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
EXPECT_FALSE(ParsedVarResult->IsPseudo);
VarName = bufferize(SM, "BadVar:");
IsPseudo = true;
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
EXPECT_TRUE(bool(ParsedName));
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
EXPECT_TRUE(bool(ParsedVarResult));
EXPECT_EQ(VarName, ":");
EXPECT_EQ(*ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
EXPECT_FALSE(ParsedVarResult->IsPseudo);
}
class PatternTester {
@ -197,7 +245,7 @@ public:
StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
return errorToBool(P.parseNumericSubstitutionBlock(
ExprBufferRef, DefinedNumericVariable, SM)
ExprBufferRef, DefinedNumericVariable, false, SM)
.takeError());
}
@ -269,15 +317,12 @@ TEST_F(FileCheckTest, ParseExpr) {
// Missing offset operand.
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));
// Cannot parse offset operand.
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x"));
// Unexpected string at end of numeric expression.
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x"));
// Valid expression.
EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
Tester.initNextPattern();
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
}
TEST_F(FileCheckTest, ParsePattern) {
@ -306,7 +351,6 @@ TEST_F(FileCheckTest, ParsePattern) {
EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#2+@LINE]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]"));
// Valid numeric expressions and numeric variable definition.
@ -365,35 +409,37 @@ TEST_F(FileCheckTest, Substitution) {
// the right value.
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10);
FileCheckExpression LineExpression = FileCheckExpression(doAdd, &LineVar, 0);
FileCheckExpression NExpression = FileCheckExpression(doAdd, &NVar, 3);
FileCheckNumericSubstitution SubstitutionLine =
FileCheckNumericSubstitution(&Context, "@LINE", &LineExpression, 12);
auto LineVarUse =
llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
auto NVarUse = llvm::make_unique<FileCheckNumericVariableUse>("N", &NVar);
FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(
&Context, "@LINE", std::move(LineVarUse), 12);
FileCheckNumericSubstitution SubstitutionN =
FileCheckNumericSubstitution(&Context, "N", &NExpression, 30);
Expected<std::string> Value = SubstitutionLine.getResult();
EXPECT_TRUE(bool(Value));
EXPECT_EQ("42", *Value);
Value = SubstitutionN.getResult();
EXPECT_TRUE(bool(Value));
EXPECT_EQ("13", *Value);
FileCheckNumericSubstitution(&Context, "N", std::move(NVarUse), 30);
SubstValue = SubstitutionLine.getResult();
EXPECT_TRUE(bool(SubstValue));
EXPECT_EQ("42", *SubstValue);
SubstValue = SubstitutionN.getResult();
EXPECT_TRUE(bool(SubstValue));
EXPECT_EQ("10", *SubstValue);
// Substitution of an undefined numeric variable fails.
// Substitution of an undefined numeric variable fails, error holds name of
// undefined variable.
LineVar.clearValue();
SubstValue = SubstitutionLine.getResult().takeError();
SubstValue = SubstitutionLine.getResult();
EXPECT_FALSE(bool(SubstValue));
expectUndefError("@LINE", SubstValue.takeError());
NVar.clearValue();
SubstValue = SubstitutionN.getResult().takeError();
SubstValue = SubstitutionN.getResult();
EXPECT_FALSE(bool(SubstValue));
expectUndefError("N", SubstValue.takeError());
// Substitution of a defined string variable returns the right value.
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
Value = StringSubstitution.getResult();
EXPECT_TRUE(bool(Value));
EXPECT_EQ("BAR", *Value);
SubstValue = StringSubstitution.getResult();
EXPECT_TRUE(bool(SubstValue));
EXPECT_EQ("BAR", *SubstValue);
}
TEST_F(FileCheckTest, FileCheckContext) {
@ -456,14 +502,15 @@ TEST_F(FileCheckTest, FileCheckContext) {
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
Expected<FileCheckExpression *> Expression = P.parseNumericSubstitutionBlock(
LocalNumVarRef, DefinedNumericVariable, SM);
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, SM);
EXPECT_TRUE(bool(LocalVar));
EXPECT_EQ(*LocalVar, "FOO");
EXPECT_TRUE(bool(Expression));
Expected<uint64_t> ExpressionVal = (*Expression)->eval();
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
EXPECT_TRUE(bool(ExpressionAST));
Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal));
EXPECT_EQ(*ExpressionVal, 18U);
EXPECT_TRUE(bool(EmptyVar));
@ -478,12 +525,12 @@ TEST_F(FileCheckTest, FileCheckContext) {
// local variables, if it was created before. This is important because local
// variable clearing due to --enable-var-scope happens after numeric
// expressions are linked to the numeric variables they use.
EXPECT_TRUE(errorToBool((*Expression)->eval().takeError()));
EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
Expression = P.parseNumericSubstitutionBlock(LocalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_TRUE(bool(Expression));
ExpressionVal = (*Expression)->eval();
ExpressionAST = P.parseNumericSubstitutionBlock(
LocalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
EXPECT_TRUE(errorToBool(EmptyVar.takeError()));
@ -501,10 +548,10 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_TRUE(bool(GlobalVar));
EXPECT_EQ(*GlobalVar, "BAR");
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_TRUE(bool(Expression));
ExpressionVal = (*Expression)->eval();
ExpressionAST = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal));
EXPECT_EQ(*ExpressionVal, 36U);
@ -512,10 +559,10 @@ TEST_F(FileCheckTest, FileCheckContext) {
Cxt.clearLocalVars();
EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_TRUE(bool(Expression));
ExpressionVal = (*Expression)->eval();
ExpressionAST = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
EXPECT_TRUE(bool(ExpressionAST));
ExpressionVal = (*ExpressionAST)->eval();
EXPECT_TRUE(bool(ExpressionVal));
EXPECT_EQ(*ExpressionVal, 36U);
}