FileCheck [9/12]: Add support for matching formats

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch adds support for selecting a
matching format to match a numeric value against (ie. decimal, hex lower
case letters or hex upper case letters).

This commit allows to select what format a numeric value should be
matched against. The following formats are supported: decimal value,
lower case hex value and upper case hex value. Matching formats impact
both the format of numeric value to be matched as well as the format of
accepted numbers in a definition with empty numeric expression
constraint.

Default for absence of format is decimal value unless the numeric
expression constraint is non null and use a variable in which case the
format is the one used to define that variable. Conclict of format in
case of several variable being used is diagnosed and forces the user to
select a matching format explicitely.

This commit also enables immediates in numeric expressions to be in any
radix known to StringRef's GetAsInteger method, except for legacy
numeric expressions (ie [[@LINE+<offset>]] which only support decimal
immediates.

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

Reviewed By: jhenderson, arichardson

Subscribers: daltenty, MaskRay, hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D60389
This commit is contained in:
Thomas Preud'homme 2019-03-05 23:20:29 +00:00
parent 0a002f679b
commit 8e96697c7d
9 changed files with 1095 additions and 294 deletions

View File

@ -112,10 +112,11 @@ and from the command line.
Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines.
.. option:: -D#<NUMVAR>=<NUMERIC EXPRESSION>
.. option:: -D#<FMT>,<NUMVAR>=<NUMERIC EXPRESSION>
Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to
the result of evaluating ``<NUMERIC EXPRESSION>`` that can be used in
``CHECK:`` lines. See section
``FileCheck Numeric Variables and Expressions`` for details on supported
numeric expressions.
@ -588,27 +589,44 @@ numeric expression constraint based on those variables via a numeric
substitution. This allows ``CHECK:`` directives to verify a numeric relation
between two numbers, such as the need for consecutive registers to be used.
The syntax to define a numeric variable is ``[[#<NUMVAR>:]]`` where
``<NUMVAR>`` is the name of the numeric variable to define to the matching
value.
The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
* ``%<fmtspec>`` is an optional scanf-style matching format specifier to
indicate what number format to match (e.g. hex number). Currently accepted
format specifiers are ``%u``, ``%x`` and ``%X``. If absent, the format
specifier defaults to ``%u``.
* ``<NUMVAR>`` is the name of the numeric variable to define to the matching
value.
For example:
.. code-block:: llvm
; CHECK: mov r[[#REG:]], 42
; CHECK: mov r[[#REG:]], 0x[[#%X,IMM:]]
would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM``
to the value ``0xF0F0``.
The syntax of a numeric substitution is ``[[#<expr>]]`` where ``<expr>`` is an
expression. An expression is recursively defined as:
The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
* a numeric operand, or
* an expression followed by an operator and a numeric operand.
* ``%<fmtspec>`` is the same matching format specifier as for defining numeric
variables but acting as a printf-style format to indicate how a numeric
expression value should be matched against. If absent, the format specifier
is inferred from the matching format of the numeric variable(s) used by the
expression constraint if any, and defaults to ``%u`` if no numeric variable
is used. In case of conflict between matching formats of several numeric
variables the format specifier is mandatory.
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.
* ``<expr>`` is an expression. An expression is in turn recursively defined
as:
* a numeric operand, or
* an expression followed by an operator and a numeric operand.
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:
@ -616,6 +634,7 @@ For example:
; CHECK: load r[[#REG:]], [r0]
; CHECK: load r[[#REG+1]], [r1]
; CHECK: Loading from 0x[[#%x,ADDR:] to 0x[[#ADDR + 7]]
The above example would match the text:
@ -623,6 +642,7 @@ The above example would match the text:
load r5, [r0]
load r6, [r1]
Loading from 0xa0463440 to 0xa0463447
but would not match the text:
@ -630,8 +650,10 @@ but would not match the text:
load r5, [r0]
load r7, [r1]
Loading from 0xa0463440 to 0xa0463443
due to ``7`` being unequal to ``5 + 1``.
Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to
``a0463440 + 7``.
The syntax also supports an empty expression, equivalent to writing {{[0-9]+}},
for cases where the input must contain a numeric value but the value itself
@ -646,8 +668,21 @@ to check that a value is synthesized rather than moved around.
A numeric variable can also be defined to the result of a numeric expression,
in which case the numeric expression is checked and if verified the variable is
assigned to the value. The unified syntax for both defining numeric variables
and checking a numeric expression is thus ``[[#<NUMVAR>: <expr>]]`` with each
element as described previously.
and checking a numeric expression is thus ``[[#%<fmtspec>,<NUMVAR>: <expr>]]``
with each element as described previously. One can use this syntax to make a
testcase more self-describing by using variables instead of values:
.. code-block:: gas
; CHECK: mov r[[#REG_OFFSET:]], 0x[[#%X,FIELD_OFFSET:12]]
; CHECK-NEXT: load r[[#]], [r[[#REG_BASE:]], r[[#REG_OFFSET]]]
which would match:
.. code-block:: gas
mov r4, 0xC
load r6, [r5, r4]
The ``--enable-var-scope`` option has the same effect on numeric variables as
on string variables.

View File

@ -25,6 +25,47 @@
using namespace llvm;
Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
switch (Value) {
case Kind::Unsigned:
return StringRef("[0-9]+");
case Kind::HexUpper:
return StringRef("[0-9A-F]+");
case Kind::HexLower:
return StringRef("[0-9a-f]+");
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
}
}
Expected<std::string>
ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
switch (Value) {
case Kind::Unsigned:
return utostr(IntegerValue);
case Kind::HexUpper:
return utohexstr(IntegerValue, /*LowerCase=*/false);
case Kind::HexLower:
return utohexstr(IntegerValue, /*LowerCase=*/true);
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
}
}
Expected<uint64_t>
ExpressionFormat::valueFromStringRepr(StringRef StrVal,
const SourceMgr &SM) const {
bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
uint64_t IntegerValue;
if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
return ErrorDiagnostic::get(SM, StrVal,
"unable to represent numeric value");
return IntegerValue;
}
Expected<uint64_t> NumericVariableUse::eval() const {
Optional<uint64_t> Value = Variable->getValue();
if (Value)
@ -51,11 +92,28 @@ Expected<uint64_t> BinaryOperation::eval() const {
return EvalBinop(*LeftOp, *RightOp);
}
ExpressionFormat BinaryOperation::getImplicitFormat() const {
ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
ExpressionFormat Format =
LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
RightFormat != ExpressionFormat::Kind::NoFormat &&
LeftFormat != RightFormat)
Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
return Format;
}
Expected<std::string> NumericSubstitution::getResult() const {
Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval();
assert(ExpressionPointer->getAST() != nullptr &&
"Substituting empty expression");
Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
return utostr(*EvaluatedValue);
ExpressionFormat Format = ExpressionPointer->getFormat();
return Format.getMatchingString(*EvaluatedValue);
}
Expected<std::string> StringSubstitution::getResult() const {
@ -113,7 +171,8 @@ char NotFoundError::ID = 0;
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
Optional<size_t> LineNumber, const SourceMgr &SM) {
Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
const SourceMgr &SM) {
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
@ -137,10 +196,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
NumericVariable *DefinedNumericVariable;
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
if (VarTableIter != Context->GlobalNumericVariableTable.end())
if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
DefinedNumericVariable = VarTableIter->second;
else
DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
return ErrorDiagnostic::get(
SM, Expr, "format different from previous variable definition");
} else
DefinedNumericVariable =
Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
return DefinedNumericVariable;
}
@ -165,7 +228,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
if (VarTableIter != Context->GlobalNumericVariableTable.end())
NumericVariable = VarTableIter->second;
else {
NumericVariable = Context->makeNumericVariable(Name);
NumericVariable = Context->makeNumericVariable(
Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Context->GlobalNumericVariableTable[Name] = NumericVariable;
}
@ -198,7 +262,8 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
// Otherwise, parse it as a literal.
uint64_t LiteralValue;
if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
LiteralValue))
return std::make_unique<ExpressionLiteral>(LiteralValue);
return ErrorDiagnostic::get(SM, Expr,
@ -244,7 +309,7 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
// The second operand in a legacy @LINE expression is always a literal.
AllowedOperand AO =
IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
if (!RightOpResult)
@ -255,13 +320,47 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
std::move(*RightOpResult));
}
Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
StringRef DefExpr = StringRef();
DefinedNumericVariable = None;
ExpressionFormat ExplicitFormat = ExpressionFormat();
// Parse format specifier.
size_t FormatSpecEnd = Expr.find(',');
if (FormatSpecEnd != StringRef::npos) {
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front("%"))
return ErrorDiagnostic::get(
SM, Expr, "invalid matching format specification in expression");
// Check for unknown matching format specifier and set matching format in
// class instance representing this expression.
SMLoc fmtloc = SMLoc::getFromPointer(Expr.data());
switch (popFront(Expr)) {
case 'u':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
break;
case 'x':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
break;
case 'X':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper);
break;
default:
return ErrorDiagnostic::get(SM, fmtloc,
"invalid format specifier in expression");
}
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front(","))
return ErrorDiagnostic::get(
SM, Expr, "invalid matching format specification in expression");
}
// Save variable definition expression if any.
size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) {
@ -271,6 +370,7 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
// Parse the expression itself.
Expr = Expr.ltrim(SpaceChars);
StringRef UseExpr = Expr;
if (!Expr.empty()) {
// The first operand in a legacy @LINE expression is always the @LINE
// pseudo variable.
@ -288,22 +388,44 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
return ParseResult;
return ParseResult.takeError();
ExpressionASTPointer = std::move(*ParseResult);
}
// Select format of the expression, i.e. (i) its explicit format, if any,
// otherwise (ii) its implicit format, if any, otherwise (iii) the default
// format (unsigned). Error out in case of conflicting implicit format
// without explicit format.
ExpressionFormat Format,
ImplicitFormat = ExpressionASTPointer
? ExpressionASTPointer->getImplicitFormat()
: ExpressionFormat(ExpressionFormat::Kind::NoFormat);
if (bool(ExplicitFormat))
Format = ExplicitFormat;
else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
return ErrorDiagnostic::get(
SM, UseExpr,
"variables with conflicting format specifier: need an explicit one");
else if (bool(ImplicitFormat))
Format = ImplicitFormat;
else
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
std::unique_ptr<Expression> ExpressionPointer =
std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
// Parse the numeric variable definition.
if (DefEnd != StringRef::npos) {
DefExpr = DefExpr.ltrim(SpaceChars);
Expected<NumericVariable *> ParseResult =
parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
}
return std::move(ExpressionASTPointer);
return std::move(ExpressionPointer);
}
bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@ -476,10 +598,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
}
// Parse numeric substitution block.
std::unique_ptr<ExpressionAST> ExpressionASTPointer;
std::unique_ptr<Expression> ExpressionPointer;
Optional<NumericVariable *> DefinedNumericVariable;
if (IsNumBlock) {
Expected<std::unique_ptr<ExpressionAST>> ParseResult =
Expected<std::unique_ptr<Expression>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, LineNumber, Context,
SM);
@ -487,16 +609,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
logAllUnhandledErrors(ParseResult.takeError(), errs());
return true;
}
ExpressionASTPointer = std::move(*ParseResult);
SubstNeeded = ExpressionASTPointer != nullptr;
ExpressionPointer = std::move(*ParseResult);
SubstNeeded = ExpressionPointer->getAST() != nullptr;
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName();
}
if (SubstNeeded)
SubstStr = MatchStr;
else
MatchRegexp = "[0-9]+";
else {
ExpressionFormat Format = ExpressionPointer->getFormat();
MatchRegexp = cantFail(Format.getWildcardRegex());
}
}
// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
@ -554,8 +678,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
Substitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(
SubstStr, std::move(ExpressionASTPointer),
SubstInsertIdx)
SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
@ -676,11 +799,11 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
NumericVariableMatch.DefinedNumericVariable;
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
uint64_t Val;
if (MatchedValue.getAsInteger(10, Val))
return ErrorDiagnostic::get(SM, MatchedValue,
"Unable to represent numeric value");
DefinedNumericVariable->setValue(Val);
ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
if (!Value)
return Value.takeError();
DefinedNumericVariable->setValue(*Value);
}
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
@ -837,10 +960,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
}
Substitution *FileCheckPatternContext::makeNumericSubstitution(
StringRef ExpressionStr,
std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) {
StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
size_t InsertIdx) {
Substitutions.push_back(std::make_unique<NumericSubstitution>(
this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx));
this, ExpressionStr, std::move(Expression), InsertIdx));
return Substitutions.back().get();
}
@ -1104,7 +1227,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
void FileCheckPatternContext::createLineVariable() {
assert(!LineVariable && "@LINE pseudo numeric variable already created");
StringRef LineName = "@LINE";
LineVariable = makeNumericVariable(LineName);
LineVariable = makeNumericVariable(
LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
GlobalNumericVariableTable[LineName] = LineVariable;
}
@ -1815,20 +1939,19 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// to create the necessary class instance.
StringRef CmdlineDefExpr = CmdlineDef.substr(1);
Optional<NumericVariable *> DefinedNumericVariable;
Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult =
Expected<std::unique_ptr<Expression>> ExpressionResult =
Pattern::parseNumericSubstitutionBlock(
CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
if (!ExpressionASTResult) {
Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
if (!ExpressionResult) {
Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
continue;
}
std::unique_ptr<ExpressionAST> ExpressionASTPointer =
std::move(*ExpressionASTResult);
std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
// Now evaluate the expression whose value this variable should be set
// to, since the expression of a command-line variable definition should
// only use variables defined earlier on the command-line. If not, this
// is an error and we report it.
Expected<uint64_t> Value = ExpressionASTPointer->eval();
Expected<uint64_t> Value = Expression->getAST()->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue;

View File

@ -30,6 +30,68 @@ namespace llvm {
// Numeric substitution handling code.
//===----------------------------------------------------------------------===//
/// Type representing the format an expression value should be textualized into
/// for matching. Used to represent both explicit format specifiers as well as
/// implicit format from using numeric variables.
struct ExpressionFormat {
enum class Kind {
/// Denote absence of format. Used for implicit format of literals and
/// empty expressions.
NoFormat,
/// Used when there are several conflicting implicit formats in an
/// expression.
Conflict,
/// Value is an unsigned integer and should be printed as a decimal number.
Unsigned,
/// Value should be printed as an uppercase hex number.
HexUpper,
/// Value should be printed as a lowercase hex number.
HexLower
};
private:
Kind Value;
public:
/// Evaluates a format to true if it can be used in a match.
explicit operator bool() const {
return Value != Kind::NoFormat && Value != Kind::Conflict;
}
/// Define format equality: formats are equal if neither is NoFormat and
/// their kinds are the same.
bool operator==(const ExpressionFormat &Other) const {
return Value != Kind::NoFormat && Value == Other.Value;
}
bool operator!=(const ExpressionFormat &other) const {
return !(*this == other);
}
bool operator==(Kind OtherValue) const { return Value == OtherValue; }
bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); }
ExpressionFormat() : Value(Kind::NoFormat){};
explicit ExpressionFormat(Kind Value) : Value(Value){};
/// \returns a wildcard regular expression StringRef that matches any value
/// in the format represented by this instance, or an error if the format is
/// NoFormat or Conflict.
Expected<StringRef> getWildcardRegex() const;
/// \returns the string representation of \p Value in the format represented
/// by this instance, or an error if the format is NoFormat or Conflict.
Expected<std::string> getMatchingString(uint64_t Value) const;
/// \returns the value corresponding to string representation \p StrVal
/// according to the matching format represented by this instance or an error
/// with diagnostic against \p SM if \p StrVal does not correspond to a valid
/// and representable value.
Expected<uint64_t> valueFromStringRepr(StringRef StrVal,
const SourceMgr &SM) const;
};
/// Base class representing the AST of a given expression.
class ExpressionAST {
public:
@ -38,6 +100,13 @@ public:
/// 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;
/// \returns either the implicit format of this AST, FormatConflict if
/// implicit formats of the AST's components conflict, or NoFormat if the AST
/// has no implicit format (e.g. AST is made up of a single literal).
virtual ExpressionFormat getImplicitFormat() const {
return ExpressionFormat();
}
};
/// Class representing an unsigned literal in the AST of an expression.
@ -78,12 +147,38 @@ public:
}
};
/// Class representing an expression and its matching format.
class Expression {
private:
/// Pointer to AST of the expression.
std::unique_ptr<ExpressionAST> AST;
/// Format to use (e.g. hex upper case letters) when matching the value.
ExpressionFormat Format;
public:
/// Generic constructor for an expression represented by the given \p AST and
/// whose matching format is \p Format.
Expression(std::unique_ptr<ExpressionAST> AST, ExpressionFormat Format)
: AST(std::move(AST)), Format(Format) {}
/// \returns pointer to AST of the expression. Pointer is guaranteed to be
/// valid as long as this object is.
ExpressionAST *getAST() const { return AST.get(); }
ExpressionFormat getFormat() const { return Format; }
};
/// Class representing a numeric variable and its associated current value.
class NumericVariable {
private:
/// Name of the numeric variable.
StringRef Name;
/// Format to use for expressions using this variable without an explicit
/// format.
ExpressionFormat ImplicitFormat;
/// Value of numeric variable, if defined, or None otherwise.
Optional<uint64_t> Value;
@ -93,15 +188,20 @@ private:
Optional<size_t> DefLineNumber;
public:
/// Constructor for a variable \p Name defined at line \p DefLineNumber or
/// defined before input is parsed if \p DefLineNumber is None.
explicit NumericVariable(StringRef Name,
/// Constructor for a variable \p Name with implicit format \p ImplicitFormat
/// defined at line \p DefLineNumber or defined before input is parsed if
/// \p DefLineNumber is None.
explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat,
Optional<size_t> DefLineNumber = None)
: Name(Name), DefLineNumber(DefLineNumber) {}
: Name(Name), ImplicitFormat(ImplicitFormat),
DefLineNumber(DefLineNumber) {}
/// \returns name of this numeric variable.
StringRef getName() const { return Name; }
/// \returns implicit format of this numeric variable.
ExpressionFormat getImplicitFormat() const { return ImplicitFormat; }
/// \returns this variable's value.
Optional<uint64_t> getValue() const { return Value; }
@ -133,6 +233,11 @@ public:
/// \returns the value of the variable referenced by this instance.
Expected<uint64_t> eval() const override;
/// \returns implicit format of this numeric variable.
ExpressionFormat getImplicitFormat() const override {
return Variable->getImplicitFormat();
}
};
/// Type of functions evaluating a given binary operation.
@ -163,6 +268,11 @@ public:
/// \returns the expression value or an error if an undefined numeric
/// variable is used in one of the operands.
Expected<uint64_t> eval() const override;
/// \returns the implicit format of this AST, if any, a format conflict if
/// the implicit formats of the AST's components conflict, or no format if
/// the AST has no implicit format (e.g. AST is made of a single literal).
ExpressionFormat getImplicitFormat() const override;
};
class FileCheckPatternContext;
@ -218,14 +328,14 @@ class NumericSubstitution : public Substitution {
private:
/// Pointer to the class representing the expression whose value is to be
/// substituted.
std::unique_ptr<ExpressionAST> ExpressionASTPointer;
std::unique_ptr<Expression> ExpressionPointer;
public:
NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
std::unique_ptr<ExpressionAST> ExprAST, size_t InsertIdx)
: Substitution(Context, Expr, InsertIdx) {
ExpressionASTPointer = std::move(ExprAST);
}
NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr,
std::unique_ptr<Expression> ExpressionPointer,
size_t InsertIdx)
: Substitution(Context, ExpressionStr, InsertIdx),
ExpressionPointer(std::move(ExpressionPointer)) {}
/// \returns a string containing the result of evaluating the expression in
/// this substitution, or an error if evaluation failed.
@ -270,6 +380,10 @@ private:
/// automatically free them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<NumericVariable>> NumericVariables;
/// 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<Expression>> Expressions;
/// Vector holding pointers to all substitutions. Used to automatically free
/// them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<Substitution>> Substitutions;
@ -307,10 +421,9 @@ private:
/// Makes a new numeric substitution and registers it for destruction when
/// the context is destroyed.
Substitution *
makeNumericSubstitution(StringRef ExpressionStr,
std::unique_ptr<ExpressionAST> ExpressionAST,
size_t InsertIdx);
Substitution *makeNumericSubstitution(StringRef ExpressionStr,
std::unique_ptr<Expression> Expression,
size_t InsertIdx);
};
/// Class to represent an error holding a diagnostic with location information
@ -388,12 +501,12 @@ class Pattern {
std::map<StringRef, unsigned> VariableDefs;
/// Structure representing the definition of a numeric variable in a pattern.
/// It holds the pointer to the class representing the numeric variable whose
/// value is being defined and the number of the parenthesis group in
/// RegExStr to capture that value.
/// It holds the pointer to the class instance holding the value and matching
/// format of the numeric variable whose value is being defined and the
/// number of the parenthesis group in RegExStr to capture that value.
struct NumericVariableMatch {
/// Pointer to class representing the numeric variable whose value is being
/// defined.
/// Pointer to class instance holding the value and matching format of the
/// numeric variable being defined.
NumericVariable *DefinedNumericVariable;
/// Number of the parenthesis group in RegExStr that captures the value of
@ -457,12 +570,12 @@ public:
/// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
/// expression and \p Context points to the class instance holding the live
/// string and numeric variables. \returns a pointer to the class instance
/// representing the AST of the expression whose value must be substitued, 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
/// substitution block, or None if this block does not define any variable.
static Expected<std::unique_ptr<ExpressionAST>> parseNumericSubstitutionBlock(
/// representing the expression whose value must be substitued, 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 substitution
/// block, or None if this block does not define any variable.
static Expected<std::unique_ptr<Expression>> parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
@ -526,7 +639,8 @@ private:
/// should defining such a variable be invalid.
static Expected<NumericVariable *> parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
Optional<size_t> LineNumber, const SourceMgr &SM);
Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
const SourceMgr &SM);
/// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use
/// at line \p LineNumber, or before input is parsed if \p LineNumber is
/// None. Parameter \p Context points to the class instance holding the live
@ -536,7 +650,7 @@ private:
static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse(
StringRef Name, bool IsPseudo, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
enum class AllowedOperand { LineVar, Literal, Any };
enum class AllowedOperand { LineVar, LegacyLiteral, Any };
/// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
/// before input is parsed if \p LineNumber is None. Accepts both literal
/// values and numeric variables, depending on the value of \p AO. Parameter

View File

@ -62,12 +62,19 @@
62 BAD12: [[#@LINE-1]] NOT HERE
63 ERR12: note: with "@LINE-1" equal to "61"
64
65 CHECK: [[#@LINE]] CHECK
66 CHECK: [[# @LINE]] CHECK
67 CHECK: [[# @LINE ]] CHECK
; RUN: %ProtectFileCheckOutput \
; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
; RUN: | FileCheck --check-prefix ERR13 %s
68
69 CHECK: [[#@LINE-1]]
70 CHECK: [[# @LINE-1]] CHECK
71 CHECK: [[# @LINE -1]] CHECK
72 CHECK: [[# @LINE - 1]] CHECK
73 CHECK: [[# @LINE - 1 ]] CHECK
69 BAD13: [[@LINE-0xA]]
70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
71
72 CHECK: [[#@LINE]] CHECK
73 CHECK: [[# @LINE]] CHECK
74 CHECK: [[# @LINE ]] CHECK
75
76 CHECK: [[#@LINE-1]]
77 CHECK: [[# @LINE-1]] CHECK
78 CHECK: [[# @LINE -1]] CHECK
79 CHECK: [[# @LINE - 1]] CHECK
80 CHECK: [[# @LINE - 1 ]] CHECK

View File

@ -3,11 +3,11 @@
; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLINAME
NUMERRCLIFMT:Global defines:1:46: error: invalid variable name
NUMERRCLIFMT-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
NUMERRCLIFMT-NEXT: ^
NUMERRCLINAME:Global defines:1:46: error: invalid variable name
NUMERRCLINAME-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
NUMERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \
@ -26,3 +26,11 @@ RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUME
NUMERRCLITRAIL:Global defines:1:51: error: unexpected characters after numeric variable name
NUMERRCLITRAIL-NEXT:Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
NUMERRCLITRAIL-NEXT: ^
; Invalid format for variable.
RUN: not FileCheck -D#,VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
NUMERRCLIFMT:Global defines:1:45: error: invalid matching format specification in expression
NUMERRCLIFMT-NEXT:Global define #1: #,VALUE=10 (parsed as: {{\[\[#,VALUE:10\]\]}})
NUMERRCLIFMT-NEXT: ^

View File

@ -1,31 +1,33 @@
; Test functionality of -D# option: numeric variables are defined to the right
; value and CHECK directives using them match as expected given the value set.
RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s
; Tests with default format specifier.
RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' --check-prefixes CHECKNUM1,CHECKNUM2 --input-file %s %s
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix CHECKNUM1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG1
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix CHECKNUM2 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG2
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG1
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2
RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG2
RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s
Numeric value #1 = 8
Numeric value #2 = 12
CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]]
NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]]
NUMNOT1-NOT: Numeric value #1 = [[#NUMVAL1]]
NUMNOT1-NOT: Numeric value #2 = [[#NUMVAL2]]
NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
NUMERRMSG1: defines.txt:1:1: note: scanning from here
@ -37,10 +39,61 @@ NUMERRMSG2: defines.txt:1:1: note: scanning from here
NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8"
NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8"
NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here
NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12"
; Tests with explicit format specifiers.
RUN: FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix CHECKNUM3 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG3
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix CHECKNUM4 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG4
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG3
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG4
RUN: FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s
; Test with explicit and default format specifiers.
RUN: FileCheck -D#NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
; Test with explicit and implicit format specifiers.
RUN: FileCheck -D#%X,NUMVAL3=8 -D#NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
Numeric value #3 = 8
Numeric value #4 = C
CHECKNUM3: Numeric value #3 = [[#NUMVAL3]]
CHECKNUM4: Numeric value #4 = [[#NUMVAL4]]
NUMNOT2-NOT: Numeric value #3 = [[#NUMVAL3]]
NUMNOT2-NOT: Numeric value #4 = [[#NUMVAL4]]
NUMERRMSG3: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM3: expected string not found in input
NUMERRMSG3: defines.txt:1:1: note: scanning from here
NUMERRMSG3: defines.txt:1:1: note: with "NUMVAL3" equal to "7"
NUMERRMSG3: defines.txt:[[#@LINE-10]]:1: note: possible intended match here
NUMERRMSG4: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM4: expected string not found in input
NUMERRMSG4: defines.txt:1:1: note: scanning from here
NUMERRMSG4: defines.txt:1:1: note: with "NUMVAL4" equal to "8"
NUMERRMSG4: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
NOT-NUMERRMSG3: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
NOT-NUMERRMSG3: defines.txt:[[#@LINE-18]]:1: note: found here
NOT-NUMERRMSG3: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL3" equal to "8"
NOT-NUMERRMSG4: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
NOT-NUMERRMSG4: defines.txt:[[#@LINE-21]]:1: note: found here
NOT-NUMERRMSG4: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL4" equal to "C"

View File

@ -2,18 +2,19 @@ RUN: FileCheck --input-file %s %s
; We use CHECK-NEXT directives to force a match on all lines with digits.
; Numeric variable definition without spaces.
DEF NO SPC
; Numeric variable definition with default matching format without spaces.
DEF DEFAULT FMT NO SPC
10
CHECK-LABEL: DEF NO SPC
CHECK-LABEL: DEF DEFAULT FMT NO SPC
CHECK-NEXT: [[#VAR1:]]
; Numeric variable definition with different spacing.
DEF SPC
; Numeric variable definition with default matching format with different
; spacing.
DEF DEFAULT FMT SPC
11
11
11
CHECK-LABEL: DEF SPC
CHECK-LABEL: DEF DEFAULT FMT SPC
CHECK-NEXT: [[# VAR1a:]]
CHECK-NEXT: [[# VAR1b :]]
CHECK-NEXT: [[# VAR1c : ]]
@ -24,63 +25,187 @@ REDEF NO SPC
CHECK-LABEL: REDEF
CHECK-NEXT: [[#VAR1:]]
; Numeric expressions using variables defined on other lines without spaces.
USE NO SPC
; Numeric variable definition with explicit matching format.
DEF FMT
c
D
CHECK-LABEL: DEF FMT
CHECK-NEXT: [[#%x,VAR2:]]
CHECK-NEXT: [[#%X,VAR3:]]
; Numeric variable definition with explicit matching format with different
; spacing.
DEF FMT SPC
c
CHECK-LABEL: DEF FMT SPC
CHECK-NEXT: [[# %x , VAR2a : ]]
; Numeric variable definition with unsupported matching format.
RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC1 --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefix INVALID-FMT-SPEC-MSG1 --strict-whitespace %s
RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC2 --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefix INVALID-FMT-SPEC-MSG2 --strict-whitespace %s
DEF INVALID FMT
INVVAR1=a
INVVAR2=11
ERR-LABEL: DEF INVALID FMT
INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]]
INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]]
INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression
INVALID-FMT-SPEC-MSG1-NEXT: {{I}}NVALID-FMT-SPEC1-NEXT: INVVAR1={{\[\[#%c,INVVAR1:\]\]}}
INVALID-FMT-SPEC-MSG1-NEXT: {{^}} ^{{$}}
INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression
INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}}
INVALID-FMT-SPEC-MSG2-NEXT: {{^}} ^{{$}}
; Numeric expressions in explicit matching format and default matching rule using
; variables defined on other lines without spaces.
USE DEF FMT IMPL MATCH
11
12
10
c
d
b
1a
D
E
C
1B
11
11
11
CHECK-LABEL: USE
c
c
c
c
c
CHECK-LABEL: USE DEF FMT IMPL MATCH
CHECK-NEXT: [[#%u,VAR1]]
CHECK-NEXT: [[#%u,VAR1+1]]
CHECK-NEXT: [[#%u,VAR1-1]]
CHECK-NEXT: [[#%x,VAR2]]
CHECK-NEXT: [[#%x,VAR2+1]]
CHECK-NEXT: [[#%x,VAR2-1]]
CHECK-NEXT: [[#%x,VAR2+14]]
CHECK-NEXT: [[#%X,VAR3]]
CHECK-NEXT: [[#%X,VAR3+1]]
CHECK-NEXT: [[#%X,VAR3-1]]
CHECK-NEXT: [[#%X,VAR3+14]]
CHECK-NEXT: [[#%u,VAR1a]]
CHECK-NEXT: [[#%u,VAR1b]]
CHECK-NEXT: [[#%u,VAR1c]]
CHECK-NEXT: [[#%x,VAR2a]]
; Numeric expressions in explicit matching format and default matching rule using
; variables defined on other lines with different spacing.
USE EXPL FMT IMPL MATCH SPC
11
11
11
12
12
12
12
12
12
10
10
10
10
10
10
CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
CHECK-NEXT: [[#%u, VAR1]]
CHECK-NEXT: [[# %u, VAR1]]
CHECK-NEXT: [[# %u, VAR1 ]]
CHECK-NEXT: [[#%u, VAR1+1]]
CHECK-NEXT: [[# %u, VAR1+1]]
CHECK-NEXT: [[# %u , VAR1+1]]
CHECK-NEXT: [[# %u , VAR1 +1]]
CHECK-NEXT: [[# %u , VAR1 + 1]]
CHECK-NEXT: [[# %u , VAR1 + 1 ]]
CHECK-NEXT: [[#%u, VAR1-1]]
CHECK-NEXT: [[# %u, VAR1-1]]
CHECK-NEXT: [[# %u , VAR1-1]]
CHECK-NEXT: [[# %u , VAR1 -1]]
CHECK-NEXT: [[# %u , VAR1 - 1]]
CHECK-NEXT: [[# %u , VAR1 - 1 ]]
; Numeric expressions in implicit matching format and default matching rule using
; variables defined on other lines.
USE IMPL FMT IMPL MATCH
11
12
10
c
d
b
1a
D
E
C
1B
CHECK-LABEL: USE IMPL FMT IMPL MATCH
CHECK-NEXT: [[#VAR1]]
CHECK-NEXT: [[#VAR1+1]]
CHECK-NEXT: [[#VAR1-1]]
CHECK-NEXT: [[#VAR1a]]
CHECK-NEXT: [[#VAR1b]]
CHECK-NEXT: [[#VAR1c]]
; Numeric expressions using variables defined on other lines with different
; spacing.
USE SPC
11
11
12
12
12
12
10
10
10
10
CHECK-LABEL: USE SPC
CHECK-NEXT: [[# VAR1]]
CHECK-NEXT: [[# VAR1 ]]
CHECK-NEXT: [[# VAR1+1]]
CHECK-NEXT: [[# VAR1 +1]]
CHECK-NEXT: [[# VAR1 + 1]]
CHECK-NEXT: [[# VAR1 + 1 ]]
CHECK-NEXT: [[# VAR1-1]]
CHECK-NEXT: [[# VAR1 -1]]
CHECK-NEXT: [[# VAR1 - 1]]
CHECK-NEXT: [[# VAR1 - 1 ]]
CHECK-NEXT: [[#VAR2]]
CHECK-NEXT: [[#VAR2+1]]
CHECK-NEXT: [[#VAR2-1]]
CHECK-NEXT: [[#VAR2+14]]
CHECK-NEXT: [[#VAR3]]
CHECK-NEXT: [[#VAR3+1]]
CHECK-NEXT: [[#VAR3-1]]
CHECK-NEXT: [[#VAR3+14]]
; 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
USE IMPL FMT IMPL MATCH UNSIGNED IMM
9223372036854775819
CHECK-LABEL: USE UNSIGNED IMM
CHECK-NEXT: [[#VAR1+9223372036854775808]]
CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
CHECK-NEXT: [[#VAR1+0x8000000000000000]]
; Numeric expressions with matching format overriding the implicit format of
; variables defined on other lines.
USE CONV FMT IMPL MATCH
b
B
12
13
CHECK-LABEL: USE CONV FMT IMPL MATCH
CHECK-NEXT: [[# %x, VAR1]]
CHECK-NEXT: [[# %X, VAR1]]
CHECK-NEXT: [[# %u, VAR2]]
CHECK-NEXT: [[# %u, VAR3]]
; Conflicting implicit format.
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s
VAR USE IMPL FMT CONFLICT
23
FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT
FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]]
FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one
FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
FMT-CONFLICT-MSG-NEXT: {{^ \^$}}
; Explicitly specified format can override conflicting implicit formats.
VAR USE IMPL OVERRIDE FMT CONFLICT
23
CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT
CHECK-NEXT: [[# %u, VAR1 + VAR2]]
; 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]]
CHECK-NEXT: [[#VAR4:]]
CHECK-NEXT: [[#VAR1+VAR4]]
; Numeric expression using a variable defined from a numeric expression.
DEF EXPR GOOD MATCH
@ -93,8 +218,8 @@ DEF EXPR GOOD MATCH
; Empty numeric expression.
EMPTY NUM EXPR
foo 104 bar
; CHECK-LABEL: EMPTY NUM EXPR
; CHECK-NEXT: foo [[#]] bar
CHECK-LABEL: EMPTY NUM EXPR
CHECK-NEXT: foo [[#]] bar
; Numeric expression using undefined variables.
RUN: %ProtectFileCheckOutput \
@ -187,7 +312,7 @@ BIG VALUE
NUMVAR: 10000000000000000000000
BIGVAL-LABEL: BIG VALUE
BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value
BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value
BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
BIGVAL-MSG-NEXT: {{^}} ^{{$}}
@ -232,3 +357,17 @@ SAME-LINE-USE2-NEXT: [[#VAR2:VAR1+1]] [[#VAR2+1]]
SAME-LINE-USE-MSG2: numeric-expression.txt:[[#@LINE-1]]:42: error: numeric variable 'VAR2' defined earlier in the same CHECK directive
SAME-LINE-USE-MSG2-NEXT: {{S}}AME-LINE-USE2-NEXT: {{\[\[#VAR2:VAR1\+1\]\] \[\[#VAR2\+1\]\]}}
SAME-LINE-USE-MSG2-NEXT: {{^}} ^{{$}}
; Invalid change of format in variable redefinition.
RUN: not FileCheck --check-prefix REDEF-NEW-FMT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix REDEF-NEW-FMT-MSG %s
VAR REDEF FMT CHANGE
22
DC
REDEF-NEW-FMT-LABEL: VAR REDEF FMT CHANGE
REDEF-NEW-FMT-NEXT: [[#VAR1:]]
REDEF-NEW-FMT-NEXT: [[#%X,VAR1:]]
REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition
REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,VAR1:\]\]}}
REDEF-NEW-FMT-MSG-NEXT: {{^ \^$}}

View File

@ -27,11 +27,11 @@ ERRCLIVAR2: Missing variable name in command-line definition '-D='
; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLIFMT
RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLINAME
ERRCLIFMT:Global defines:1:19: error: invalid variable name
ERRCLIFMT-NEXT:Global define #1: 10VALUE=10
ERRCLIFMT-NEXT: ^
ERRCLINAME:Global defines:1:19: error: invalid variable name
ERRCLINAME-NEXT:Global define #1: 10VALUE=10
ERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \

View File

@ -8,8 +8,10 @@
#include "llvm/Support/FileCheck.h"
#include "../lib/Support/FileCheckImpl.h"
#include "llvm/Support/Regex.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <tuple>
#include <unordered_set>
using namespace llvm;
@ -18,114 +20,6 @@ namespace {
class FileCheckTest : public ::testing::Test {};
TEST_F(FileCheckTest, Literal) {
// Eval returns the literal's value.
ExpressionLiteral Ten(10);
Expected<uint64_t> Value = Ten.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(10U, *Value);
// Max value can be correctly represented.
ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
Value = Max.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
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) {
EXPECT_THAT_ERROR(
handleErrors(std::move(Err),
[&](const UndefVarError &E) {
EXPECT_EQ(ExpectedUndefVarNames.erase(E.getVarName()), 1U);
}),
Succeeded());
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
}
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
TEST_F(FileCheckTest, NumericVariable) {
// Undefined variable: getValue and eval fail, error returned by eval holds
// the name of the undefined variable.
NumericVariable FooVar("FOO", 1);
EXPECT_EQ("FOO", FooVar.getName());
NumericVariableUse FooVarUse("FOO", &FooVar);
EXPECT_FALSE(FooVar.getValue());
Expected<uint64_t> EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
FooVar.setValue(42);
// Defined variable: getValue and eval return value set.
Optional<uint64_t> Value = FooVar.getValue();
ASSERT_TRUE(Value);
EXPECT_EQ(42U, *Value);
EvalResult = FooVarUse.eval();
ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
EXPECT_EQ(42U, *EvalResult);
// Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable.
FooVar.clearValue();
EXPECT_FALSE(FooVar.getValue());
EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
}
TEST_F(FileCheckTest, Binop) {
NumericVariable FooVar("FOO", 1);
FooVar.setValue(42);
std::unique_ptr<NumericVariableUse> FooVarUse =
std::make_unique<NumericVariableUse>("FOO", &FooVar);
NumericVariable BarVar("BAR", 2);
BarVar.setValue(18);
std::unique_ptr<NumericVariableUse> BarVarUse =
std::make_unique<NumericVariableUse>("BAR", &BarVar);
BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
// Defined variable: eval returns right value.
Expected<uint64_t> Value = Binop.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(60U, *Value);
// 1 undefined variable: eval fails, error contains name of undefined
// variable.
FooVar.clearValue();
Value = Binop.eval();
expectUndefErrors({"FOO"}, Value.takeError());
// 2 undefined variables: eval fails, error contains names of all undefined
// variables.
BarVar.clearValue();
Value = Binop.eval();
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
}
TEST_F(FileCheckTest, ValidVarNameStart) {
EXPECT_TRUE(Pattern::isValidVarNameStart('a'));
EXPECT_TRUE(Pattern::isValidVarNameStart('G'));
EXPECT_TRUE(Pattern::isValidVarNameStart('_'));
EXPECT_FALSE(Pattern::isValidVarNameStart('2'));
EXPECT_FALSE(Pattern::isValidVarNameStart('$'));
EXPECT_FALSE(Pattern::isValidVarNameStart('@'));
EXPECT_FALSE(Pattern::isValidVarNameStart('+'));
EXPECT_FALSE(Pattern::isValidVarNameStart('-'));
EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
}
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
@ -152,6 +46,329 @@ static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
}
struct ExpressionFormatParameterisedFixture
: public ::testing::TestWithParam<
std::tuple<ExpressionFormat::Kind, bool, bool>> {
void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); }
ExpressionFormat::Kind Kind;
bool AllowHex;
bool AllowUpperHex;
};
TEST_P(ExpressionFormatParameterisedFixture, Format) {
SourceMgr SM;
ExpressionFormat Format(Kind);
Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
Regex WildcardRegex(*WildcardPattern);
ASSERT_TRUE(WildcardRegex.isValid());
// Does not match empty string.
EXPECT_FALSE(WildcardRegex.match(""));
// Matches all decimal digits and matches several of them.
SmallVector<StringRef, 4> Matches;
StringRef DecimalDigits = "0123456789";
ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
EXPECT_EQ(Matches[0], DecimalDigits);
// Check non digits or digits with wrong casing are not matched.
if (AllowHex) {
StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
StringRef AcceptedHexOnlyDigits =
AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0];
StringRef RefusedHexOnlyDigits =
AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1];
ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches));
EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits);
EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits));
EXPECT_FALSE(WildcardRegex.match("g"));
EXPECT_FALSE(WildcardRegex.match("G"));
} else {
EXPECT_FALSE(WildcardRegex.match("a"));
EXPECT_FALSE(WildcardRegex.match("A"));
}
Expected<std::string> MatchingString = Format.getMatchingString(0U);
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "0");
MatchingString = Format.getMatchingString(9U);
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "9");
Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
if (AllowHex) {
if (AllowUpperHex) {
ExpectedTenMatchingString = "A";
ExpectedFifteenMatchingString = "F";
} else {
ExpectedTenMatchingString = "a";
ExpectedFifteenMatchingString = "f";
}
} else {
ExpectedTenMatchingString = "10";
ExpectedFifteenMatchingString = "15";
}
EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
StringRef BufferizedValidValueStr = bufferize(SM, "0");
Expected<uint64_t> Val =
Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 0U);
BufferizedValidValueStr = bufferize(SM, "9");
Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 9U);
StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
StringRef TenStr, FifteenStr, InvalidTenStr;
if (AllowHex) {
if (AllowUpperHex) {
TenStr = "A";
FifteenStr = "F";
InvalidTenStr = "a";
} else {
TenStr = "a";
FifteenStr = "f";
InvalidTenStr = "A";
}
} else {
TenStr = "10";
FifteenStr = "15";
InvalidTenStr = "A";
}
BufferizedTenStr = bufferize(SM, TenStr);
Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 10U);
BufferizedFifteenStr = bufferize(SM, FifteenStr);
Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 15U);
// Wrong casing is not tested because valueFromStringRepr() relies on
// StringRef's getAsInteger() which does not allow to restrict casing.
BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
expectDiagnosticError(
"unable to represent numeric value",
Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
// Check boolean operator.
EXPECT_TRUE(bool(Format));
}
INSTANTIATE_TEST_CASE_P(
AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
::testing::Values(
std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
/*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
/*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
/*AllowUpperHex=*/true)), );
TEST_F(FileCheckTest, NoFormatProperties) {
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
expectError<StringError>("trying to match value with invalid format",
NoFormat.getWildcardRegex().takeError());
expectError<StringError>("trying to match value with invalid format",
NoFormat.getMatchingString(18).takeError());
EXPECT_FALSE(bool(NoFormat));
}
TEST_F(FileCheckTest, ConflictFormatProperties) {
ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
expectError<StringError>("trying to match value with invalid format",
ConflictFormat.getWildcardRegex().takeError());
expectError<StringError>("trying to match value with invalid format",
ConflictFormat.getMatchingString(18).takeError());
EXPECT_FALSE(bool(ConflictFormat));
}
TEST_F(FileCheckTest, FormatEqualityOperators) {
ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);
EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);
ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);
EXPECT_FALSE(UnsignedFormat == HexLowerFormat);
EXPECT_TRUE(UnsignedFormat != HexLowerFormat);
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
EXPECT_FALSE(NoFormat == NoFormat2);
EXPECT_TRUE(NoFormat != NoFormat2);
ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict);
EXPECT_TRUE(ConflictFormat == ConflictFormat2);
EXPECT_FALSE(ConflictFormat != ConflictFormat2);
}
TEST_F(FileCheckTest, FormatKindEqualityOperators) {
ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower);
ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict);
EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict);
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
}
TEST_F(FileCheckTest, Literal) {
// Eval returns the literal's value.
ExpressionLiteral Ten(10);
Expected<uint64_t> Value = Ten.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(10U, *Value);
EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
// Max value can be correctly represented.
ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
Value = Max.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
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;
}
TEST_F(FileCheckTest, Expression) {
std::unique_ptr<ExpressionLiteral> Ten =
std::make_unique<ExpressionLiteral>(10);
ExpressionLiteral *TenPtr = Ten.get();
Expression Expr(std::move(Ten),
ExpressionFormat(ExpressionFormat::Kind::HexLower));
EXPECT_EQ(Expr.getAST(), TenPtr);
EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);
}
static void
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
Error Err) {
EXPECT_THAT_ERROR(
handleErrors(std::move(Err),
[&](const UndefVarError &E) {
EXPECT_EQ(ExpectedUndefVarNames.erase(E.getVarName()), 1U);
}),
Succeeded());
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
}
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
TEST_F(FileCheckTest, NumericVariable) {
// Undefined variable: getValue and eval fail, error returned by eval holds
// the name of the undefined variable.
NumericVariable FooVar("FOO",
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
EXPECT_EQ("FOO", FooVar.getName());
EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
NumericVariableUse FooVarUse("FOO", &FooVar);
EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(FooVar.getValue());
Expected<uint64_t> EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
FooVar.setValue(42);
// Defined variable: getValue and eval return value set.
Optional<uint64_t> Value = FooVar.getValue();
ASSERT_TRUE(Value);
EXPECT_EQ(42U, *Value);
EvalResult = FooVarUse.eval();
ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
EXPECT_EQ(42U, *EvalResult);
// Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable.
FooVar.clearValue();
EXPECT_FALSE(FooVar.getValue());
EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
}
TEST_F(FileCheckTest, Binop) {
NumericVariable FooVar("FOO",
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
FooVar.setValue(42);
std::unique_ptr<NumericVariableUse> FooVarUse =
std::make_unique<NumericVariableUse>("FOO", &FooVar);
NumericVariable BarVar("BAR",
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
BarVar.setValue(18);
std::unique_ptr<NumericVariableUse> BarVarUse =
std::make_unique<NumericVariableUse>("BAR", &BarVar);
BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
// Defined variables: eval returns right value; implicit format is as
// expected.
Expected<uint64_t> Value = Binop.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(60U, *Value);
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
// 1 undefined variable: eval fails, error contains name of undefined
// variable.
FooVar.clearValue();
Value = Binop.eval();
expectUndefErrors({"FOO"}, Value.takeError());
// 2 undefined variables: eval fails, error contains names of all undefined
// variables.
BarVar.clearValue();
Value = Binop.eval();
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
// Literal + Variable has format of variable.
FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
std::unique_ptr<ExpressionLiteral> Eighteen =
std::make_unique<ExpressionLiteral>(18);
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
Eighteen = std::make_unique<ExpressionLiteral>(18);
Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
// Variables with different implicit format conflict.
NumericVariable BazVar("BAZ",
ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
FooVarUse = std::make_unique<NumericVariableUse>("BAZ", &FooVar);
std::unique_ptr<NumericVariableUse> BazVarUse =
std::make_unique<NumericVariableUse>("BAZ", &BazVar);
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
}
TEST_F(FileCheckTest, ValidVarNameStart) {
EXPECT_TRUE(Pattern::isValidVarNameStart('a'));
EXPECT_TRUE(Pattern::isValidVarNameStart('G'));
EXPECT_TRUE(Pattern::isValidVarNameStart('_'));
EXPECT_FALSE(Pattern::isValidVarNameStart('2'));
EXPECT_FALSE(Pattern::isValidVarNameStart('$'));
EXPECT_FALSE(Pattern::isValidVarNameStart('@'));
EXPECT_FALSE(Pattern::isValidVarNameStart('+'));
EXPECT_FALSE(Pattern::isValidVarNameStart('-'));
EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
}
TEST_F(FileCheckTest, ParseVar) {
SourceMgr SM;
StringRef OrigVarName = bufferize(SM, "GoodVar42");
@ -257,7 +474,7 @@ public:
size_t getLineNumber() const { return LineNumber; }
Expected<std::unique_ptr<ExpressionAST>>
Expected<std::unique_ptr<Expression>>
parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<NumericVariable *> DefinedNumericVariable;
@ -295,13 +512,36 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
expectDiagnosticError("unexpected characters after numeric variable name",
Tester.parseSubst("VAR GARBAGE:").takeError());
// Change of format.
expectDiagnosticError("format different from previous variable definition",
Tester.parseSubst("%X,FOO:").takeError());
// Invalid format.
expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("X,VAR1:").takeError());
expectDiagnosticError("invalid format specifier in expression",
Tester.parseSubst("%F,VAR1:").takeError());
expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("%X a,VAR1:").takeError());
// Acceptable variable definition.
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3: "), Succeeded());
// Acceptable variable definition with format specifier. Use parsePattern for
// variables whose definition needs to be visible for later checks.
EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));
EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
// Acceptable variable definition from a numeric expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
// Numeric expression.
// Numeric expression. Switch to next line to make above valid definition
// available in expressions.
Tester.initNextPattern();
// Invalid variable name.
expectDiagnosticError("invalid operand format '%VAR'",
@ -342,8 +582,17 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
// Invalid format.
expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("X,FOO:").takeError());
expectDiagnosticError("invalid format specifier in expression",
Tester.parseSubst("%F,FOO").takeError());
expectDiagnosticError("invalid matching format specification in expression",
Tester.parseSubst("%X a,FOO").takeError());
// Valid expression with 2 or more operands.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
expectDiagnosticError("unsupported operation '/'",
@ -367,6 +616,16 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
"invalid variable name",
Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());
// Invalid hex literal in legacy @LINE expression.
expectDiagnosticError(
"unexpected characters at end of expression 'xC'",
Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());
// Valid expression with format specifier.
EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
// Valid legacy @LINE expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
Succeeded());
@ -378,6 +637,18 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
expectDiagnosticError(
"unexpected characters at end of expression '+2'",
Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());
// Valid expression with several variables when their implicit formats do not
// conflict.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());
// Valid implicit format conflict in presence of explicit formats.
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());
// Implicit format conflict.
expectDiagnosticError(
"variables with conflicting format specifier: need an explicit one",
Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
}
TEST_F(FileCheckTest, ParsePattern) {
@ -407,6 +678,12 @@ TEST_F(FileCheckTest, ParsePattern) {
// Valid numeric substitution.
EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
// Valid legacy @LINE expression.
EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
// Invalid legacy @LINE expression with non decimal literal.
EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
}
TEST_F(FileCheckTest, Match) {
@ -417,12 +694,44 @@ TEST_F(FileCheckTest, Match) {
expectNotFoundError(Tester.match("FAIL").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
// Check matching a definition only matches a number.
// Check matching a definition only matches a number with the right format.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
expectNotFoundError(Tester.match("FAIL").takeError());
expectNotFoundError(Tester.match("").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
Tester.initNextPattern();
Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
expectNotFoundError(Tester.match("C").takeError());
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
Tester.initNextPattern();
Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
expectNotFoundError(Tester.match("g").takeError());
expectNotFoundError(Tester.match("C").takeError());
EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
Tester.initNextPattern();
Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
expectNotFoundError(Tester.match("H").takeError());
expectNotFoundError(Tester.match("b").takeError());
EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
// Check matching expressions with no explicit format matches the values in
// the right format.
Tester.initNextPattern();
Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
expectNotFoundError(Tester.match("f").takeError());
expectNotFoundError(Tester.match("F").takeError());
EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
Tester.initNextPattern();
Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
expectNotFoundError(Tester.match("13").takeError());
expectNotFoundError(Tester.match("D").takeError());
EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
Tester.initNextPattern();
Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
expectNotFoundError(Tester.match("12").takeError());
expectNotFoundError(Tester.match("c").takeError());
EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
// Check matching an undefined variable returns a NotFound error.
Tester.initNextPattern();
@ -441,7 +750,7 @@ TEST_F(FileCheckTest, Match) {
expectNotFoundError(Tester.match("18 21").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
// Check matching a numeric expression using @LINE after match failure uses
// Check matching a numeric expression using @LINE after a match failure uses
// the correct value for @LINE.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
@ -476,14 +785,17 @@ TEST_F(FileCheckTest, Substitution) {
// Numeric substitution blocks constituted of defined numeric variables are
// substituted for the variable's value.
NumericVariable NVar("N", 1);
NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
1);
NVar.setValue(10);
auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
NumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse),
auto ExpressionN = std::make_unique<Expression>(
std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
/*InsertIdx=*/30);
SubstValue = SubstitutionN.getResult();
ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
EXPECT_EQ("10", *SubstValue);
EXPECT_EQ("A", *SubstValue);
// Substitution of an undefined numeric variable fails, error holds name of
// undefined variable.
@ -565,7 +877,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar="));
GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
GlobalDefines.emplace_back(std::string("#%x,LocalNumVar2=LocalNumVar1+2"));
GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc"));
ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
// Create @LINE pseudo numeric variable and check it is present by matching
@ -588,12 +901,13 @@ TEST_F(FileCheckTest, FileCheckContext) {
StringRef LocalVarStr = "LocalVar";
StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
Optional<NumericVariable *> DefinedNumericVariable;
Expected<std::unique_ptr<ExpressionAST>> ExpressionASTPointer =
Expected<std::unique_ptr<Expression>> ExpressionPointer =
P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber,
&Cxt, SM);
@ -601,17 +915,25 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_EQ(*LocalVar, "FOO");
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
Expected<uint64_t> ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 18U);
ExpressionASTPointer = P.parseNumericSubstitutionBlock(
ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 20U);
ExpressionPointer =
P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 12U);
ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
EXPECT_EQ(*EmptyVar, "");
expectUndefErrors({UnknownVarStr}, UnknownVar.takeError());
@ -624,20 +946,20 @@ 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.
expectUndefErrors({"LocalNumVar2"},
(*ExpressionASTPointer)->eval().takeError());
expectUndefErrors({"LocalNumVar3"},
(*ExpressionPointer)->getAST()->eval().takeError());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock(
ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
ExpressionASTPointer = P.parseNumericSubstitutionBlock(
ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());
@ -655,11 +977,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
EXPECT_EQ(*GlobalVar, "BAR");
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock(
ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U);
@ -667,11 +989,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
Cxt.clearLocalVars();
EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock(
ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval();
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U);
}