mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-04 03:06:28 +00:00
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:
parent
04a7ee7970
commit
04e17bdb7c
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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.
|
||||
|
@ -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'
|
||||
|
@ -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: {{^\^$}}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user