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 Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines. 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 Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to
``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section the result of evaluating ``<NUMERIC EXPRESSION>`` that can be used in
``CHECK:`` lines. See section
``FileCheck Numeric Variables and Expressions`` for details on supported ``FileCheck Numeric Variables and Expressions`` for details on supported
numeric expressions. 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 substitution. This allows ``CHECK:`` directives to verify a numeric relation
between two numbers, such as the need for consecutive registers to be used. between two numbers, such as the need for consecutive registers to be used.
The syntax to define a numeric variable is ``[[#<NUMVAR>:]]`` where The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
``<NUMVAR>`` is the name of the numeric variable to define to the matching
value. * ``%<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: For example:
.. code-block:: llvm .. 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 The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
expression. An expression is recursively defined as:
* a numeric operand, or * ``%<fmtspec>`` is the same matching format specifier as for defining numeric
* an expression followed by an operator and a numeric operand. 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 * ``<expr>`` is an expression. An expression is in turn recursively defined
literal. The supported operators are ``+`` and ``-``. Spaces are accepted as:
before, after and between any of these elements.
* 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: For example:
@ -616,6 +634,7 @@ For example:
; CHECK: load r[[#REG:]], [r0] ; CHECK: load r[[#REG:]], [r0]
; CHECK: load r[[#REG+1]], [r1] ; CHECK: load r[[#REG+1]], [r1]
; CHECK: Loading from 0x[[#%x,ADDR:] to 0x[[#ADDR + 7]]
The above example would match the text: The above example would match the text:
@ -623,6 +642,7 @@ The above example would match the text:
load r5, [r0] load r5, [r0]
load r6, [r1] load r6, [r1]
Loading from 0xa0463440 to 0xa0463447
but would not match the text: but would not match the text:
@ -630,8 +650,10 @@ but would not match the text:
load r5, [r0] load r5, [r0]
load r7, [r1] 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]+}}, 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 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, 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 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 assigned to the value. The unified syntax for both defining numeric variables
and checking a numeric expression is thus ``[[#<NUMVAR>: <expr>]]`` with each and checking a numeric expression is thus ``[[#%<fmtspec>,<NUMVAR>: <expr>]]``
element as described previously. 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 The ``--enable-var-scope`` option has the same effect on numeric variables as
on string variables. on string variables.

View File

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

View File

@ -30,6 +30,68 @@ namespace llvm {
// Numeric substitution handling code. // 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. /// Base class representing the AST of a given expression.
class ExpressionAST { class ExpressionAST {
public: public:
@ -38,6 +100,13 @@ public:
/// Evaluates and \returns the value of the expression represented by this /// Evaluates and \returns the value of the expression represented by this
/// AST or an error if evaluation fails. /// AST or an error if evaluation fails.
virtual Expected<uint64_t> eval() const = 0; 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. /// 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 representing a numeric variable and its associated current value.
class NumericVariable { class NumericVariable {
private: private:
/// Name of the numeric variable. /// Name of the numeric variable.
StringRef Name; 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. /// Value of numeric variable, if defined, or None otherwise.
Optional<uint64_t> Value; Optional<uint64_t> Value;
@ -93,15 +188,20 @@ private:
Optional<size_t> DefLineNumber; Optional<size_t> DefLineNumber;
public: public:
/// Constructor for a variable \p Name defined at line \p DefLineNumber or /// Constructor for a variable \p Name with implicit format \p ImplicitFormat
/// defined before input is parsed if \p DefLineNumber is None. /// defined at line \p DefLineNumber or defined before input is parsed if
explicit NumericVariable(StringRef Name, /// \p DefLineNumber is None.
explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat,
Optional<size_t> DefLineNumber = None) Optional<size_t> DefLineNumber = None)
: Name(Name), DefLineNumber(DefLineNumber) {} : Name(Name), ImplicitFormat(ImplicitFormat),
DefLineNumber(DefLineNumber) {}
/// \returns name of this numeric variable. /// \returns name of this numeric variable.
StringRef getName() const { return Name; } StringRef getName() const { return Name; }
/// \returns implicit format of this numeric variable.
ExpressionFormat getImplicitFormat() const { return ImplicitFormat; }
/// \returns this variable's value. /// \returns this variable's value.
Optional<uint64_t> getValue() const { return Value; } Optional<uint64_t> getValue() const { return Value; }
@ -133,6 +233,11 @@ public:
/// \returns the value of the variable referenced by this instance. /// \returns the value of the variable referenced by this instance.
Expected<uint64_t> eval() const override; 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. /// Type of functions evaluating a given binary operation.
@ -163,6 +268,11 @@ public:
/// \returns the expression value or an error if an undefined numeric /// \returns the expression value or an error if an undefined numeric
/// variable is used in one of the operands. /// variable is used in one of the operands.
Expected<uint64_t> eval() const override; 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; class FileCheckPatternContext;
@ -218,14 +328,14 @@ class NumericSubstitution : public Substitution {
private: private:
/// Pointer to the class representing the expression whose value is to be /// Pointer to the class representing the expression whose value is to be
/// substituted. /// substituted.
std::unique_ptr<ExpressionAST> ExpressionASTPointer; std::unique_ptr<Expression> ExpressionPointer;
public: public:
NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr,
std::unique_ptr<ExpressionAST> ExprAST, size_t InsertIdx) std::unique_ptr<Expression> ExpressionPointer,
: Substitution(Context, Expr, InsertIdx) { size_t InsertIdx)
ExpressionASTPointer = std::move(ExprAST); : Substitution(Context, ExpressionStr, InsertIdx),
} ExpressionPointer(std::move(ExpressionPointer)) {}
/// \returns a string containing the result of evaluating the expression in /// \returns a string containing the result of evaluating the expression in
/// this substitution, or an error if evaluation failed. /// 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. /// automatically free them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<NumericVariable>> NumericVariables; 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 /// Vector holding pointers to all substitutions. Used to automatically free
/// them once they are guaranteed to no longer be used. /// them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<Substitution>> Substitutions; std::vector<std::unique_ptr<Substitution>> Substitutions;
@ -307,10 +421,9 @@ private:
/// Makes a new numeric substitution and registers it for destruction when /// Makes a new numeric substitution and registers it for destruction when
/// the context is destroyed. /// the context is destroyed.
Substitution * Substitution *makeNumericSubstitution(StringRef ExpressionStr,
makeNumericSubstitution(StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
std::unique_ptr<ExpressionAST> ExpressionAST, size_t InsertIdx);
size_t InsertIdx);
}; };
/// Class to represent an error holding a diagnostic with location information /// Class to represent an error holding a diagnostic with location information
@ -388,12 +501,12 @@ class Pattern {
std::map<StringRef, unsigned> VariableDefs; std::map<StringRef, unsigned> VariableDefs;
/// Structure representing the definition of a numeric variable in a pattern. /// Structure representing the definition of a numeric variable in a pattern.
/// It holds the pointer to the class representing the numeric variable whose /// It holds the pointer to the class instance holding the value and matching
/// value is being defined and the number of the parenthesis group in /// format of the numeric variable whose value is being defined and the
/// RegExStr to capture that value. /// number of the parenthesis group in RegExStr to capture that value.
struct NumericVariableMatch { struct NumericVariableMatch {
/// Pointer to class representing the numeric variable whose value is being /// Pointer to class instance holding the value and matching format of the
/// defined. /// numeric variable being defined.
NumericVariable *DefinedNumericVariable; NumericVariable *DefinedNumericVariable;
/// Number of the parenthesis group in RegExStr that captures the value of /// 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 /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
/// expression and \p Context points to the class instance holding the live /// expression and \p Context points to the class instance holding the live
/// string and numeric variables. \returns a pointer to the class instance /// string and numeric variables. \returns a pointer to the class instance
/// representing the AST of the expression whose value must be substitued, or /// representing the expression whose value must be substitued, or an error
/// an error holding a diagnostic against \p SM if parsing fails. If /// holding a diagnostic against \p SM if parsing fails. If substitution was
/// substitution was successful, sets \p DefinedNumericVariable to point to /// successful, sets \p DefinedNumericVariable to point to the class
/// the class representing the numeric variable defined in this numeric /// representing the numeric variable defined in this numeric substitution
/// substitution block, or None if this block does not define any variable. /// block, or None if this block does not define any variable.
static Expected<std::unique_ptr<ExpressionAST>> parseNumericSubstitutionBlock( static Expected<std::unique_ptr<Expression>> parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable, StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber, bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM); FileCheckPatternContext *Context, const SourceMgr &SM);
@ -526,7 +639,8 @@ private:
/// should defining such a variable be invalid. /// should defining such a variable be invalid.
static Expected<NumericVariable *> parseNumericVariableDefinition( static Expected<NumericVariable *> parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context, 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 /// 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 /// 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 /// None. Parameter \p Context points to the class instance holding the live
@ -536,7 +650,7 @@ private:
static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse( static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse(
StringRef Name, bool IsPseudo, Optional<size_t> LineNumber, StringRef Name, bool IsPseudo, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM); 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 /// 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 /// before input is parsed if \p LineNumber is None. Accepts both literal
/// values and numeric variables, depending on the value of \p AO. Parameter /// values and numeric variables, depending on the value of \p AO. Parameter

View File

@ -62,12 +62,19 @@
62 BAD12: [[#@LINE-1]] NOT HERE 62 BAD12: [[#@LINE-1]] NOT HERE
63 ERR12: note: with "@LINE-1" equal to "61" 63 ERR12: note: with "@LINE-1" equal to "61"
64 64
65 CHECK: [[#@LINE]] CHECK ; RUN: %ProtectFileCheckOutput \
66 CHECK: [[# @LINE]] CHECK ; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
67 CHECK: [[# @LINE ]] CHECK ; RUN: | FileCheck --check-prefix ERR13 %s
68 68
69 CHECK: [[#@LINE-1]] 69 BAD13: [[@LINE-0xA]]
70 CHECK: [[# @LINE-1]] CHECK 70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
71 CHECK: [[# @LINE -1]] CHECK 71
72 CHECK: [[# @LINE - 1]] CHECK 72 CHECK: [[#@LINE]] CHECK
73 CHECK: [[# @LINE - 1 ]] 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. ; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --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 NUMERRCLINAME:Global defines:1:46: error: invalid variable name
NUMERRCLIFMT-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) NUMERRCLINAME-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
NUMERRCLIFMT-NEXT: ^ NUMERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable. ; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \ 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: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:Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
NUMERRCLITRAIL-NEXT: ^ 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 ; Test functionality of -D# option: numeric variables are defined to the right
; value and CHECK directives using them match as expected given the value set. ; value and CHECK directives using them match as expected given the value set.
RUN: FileCheck -D#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: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix CHECKNUM1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1 RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG1
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix CHECKNUM2 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2 RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG2
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1 RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG1
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \ RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2 RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG2
RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s
Numeric value #1 = 8 Numeric value #1 = 8
Numeric value #2 = 12 Numeric value #2 = 12
CHECKNUM1: Numeric value #1 = [[#NUMVAL1]] CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
CHECKNUM2: Numeric value #2 = [[#NUMVAL2]] CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]] NUMNOT1-NOT: Numeric value #1 = [[#NUMVAL1]]
NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]] NUMNOT1-NOT: Numeric value #2 = [[#NUMVAL2]]
NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
NUMERRMSG1: defines.txt:1:1: note: scanning from here 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:1:1: note: with "NUMVAL2" equal to "8"
NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here 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-18]]:1: note: found here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8" 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-21]]:1: note: found here
NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12" 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. ; We use CHECK-NEXT directives to force a match on all lines with digits.
; Numeric variable definition without spaces. ; Numeric variable definition with default matching format without spaces.
DEF NO SPC DEF DEFAULT FMT NO SPC
10 10
CHECK-LABEL: DEF NO SPC CHECK-LABEL: DEF DEFAULT FMT NO SPC
CHECK-NEXT: [[#VAR1:]] CHECK-NEXT: [[#VAR1:]]
; Numeric variable definition with different spacing. ; Numeric variable definition with default matching format with different
DEF SPC ; spacing.
DEF DEFAULT FMT SPC
11 11
11 11
11 11
CHECK-LABEL: DEF SPC CHECK-LABEL: DEF DEFAULT FMT SPC
CHECK-NEXT: [[# VAR1a:]] CHECK-NEXT: [[# VAR1a:]]
CHECK-NEXT: [[# VAR1b :]] CHECK-NEXT: [[# VAR1b :]]
CHECK-NEXT: [[# VAR1c : ]] CHECK-NEXT: [[# VAR1c : ]]
@ -24,63 +25,187 @@ REDEF NO SPC
CHECK-LABEL: REDEF CHECK-LABEL: REDEF
CHECK-NEXT: [[#VAR1:]] CHECK-NEXT: [[#VAR1:]]
; Numeric expressions using variables defined on other lines without spaces. ; Numeric variable definition with explicit matching format.
USE NO SPC 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 11
12 12
10 10
c
d
b
1a
D
E
C
1B
11 11
11 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]]
CHECK-NEXT: [[#VAR1+1]] CHECK-NEXT: [[#VAR1+1]]
CHECK-NEXT: [[#VAR1-1]] CHECK-NEXT: [[#VAR1-1]]
CHECK-NEXT: [[#VAR1a]] CHECK-NEXT: [[#VAR2]]
CHECK-NEXT: [[#VAR1b]] CHECK-NEXT: [[#VAR2+1]]
CHECK-NEXT: [[#VAR1c]] CHECK-NEXT: [[#VAR2-1]]
CHECK-NEXT: [[#VAR2+14]]
; Numeric expressions using variables defined on other lines with different CHECK-NEXT: [[#VAR3]]
; spacing. CHECK-NEXT: [[#VAR3+1]]
USE SPC CHECK-NEXT: [[#VAR3-1]]
11 CHECK-NEXT: [[#VAR3+14]]
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 ]]
; Numeric expressions using variables defined on other lines and an immediate ; Numeric expressions using variables defined on other lines and an immediate
; interpreted as an unsigned value. ; interpreted as an unsigned value.
; Note: 9223372036854775819 = 0x8000000000000000 + 11 ; Note: 9223372036854775819 = 0x8000000000000000 + 11
; 9223372036854775808 = 0x8000000000000000 USE IMPL FMT IMPL MATCH UNSIGNED IMM
USE UNSIGNED IMM
9223372036854775819 9223372036854775819
CHECK-LABEL: USE UNSIGNED IMM CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
CHECK-NEXT: [[#VAR1+9223372036854775808]] 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. ; Numeric expressions using more than one variable defined on other lines.
USE MULTI VAR USE MULTI VAR
31 31
42 42
CHECK-LABEL: USE MULTI VAR CHECK-LABEL: USE MULTI VAR
CHECK-NEXT: [[#VAR2:]] CHECK-NEXT: [[#VAR4:]]
CHECK-NEXT: [[#VAR1+VAR2]] CHECK-NEXT: [[#VAR1+VAR4]]
; Numeric expression using a variable defined from a numeric expression. ; Numeric expression using a variable defined from a numeric expression.
DEF EXPR GOOD MATCH DEF EXPR GOOD MATCH
@ -93,8 +218,8 @@ DEF EXPR GOOD MATCH
; Empty numeric expression. ; Empty numeric expression.
EMPTY NUM EXPR EMPTY NUM EXPR
foo 104 bar foo 104 bar
; CHECK-LABEL: EMPTY NUM EXPR CHECK-LABEL: EMPTY NUM EXPR
; CHECK-NEXT: foo [[#]] bar CHECK-NEXT: foo [[#]] bar
; Numeric expression using undefined variables. ; Numeric expression using undefined variables.
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
@ -187,7 +312,7 @@ BIG VALUE
NUMVAR: 10000000000000000000000 NUMVAR: 10000000000000000000000
BIGVAL-LABEL: BIG VALUE BIGVAL-LABEL: BIG VALUE
BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]] BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value
BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
BIGVAL-MSG-NEXT: {{^}} ^{{$}} BIGVAL-MSG-NEXT: {{^}} ^{{$}}
@ -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: 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: {{S}}AME-LINE-USE2-NEXT: {{\[\[#VAR2:VAR1\+1\]\] \[\[#VAR2\+1\]\]}}
SAME-LINE-USE-MSG2-NEXT: {{^}} ^{{$}} 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. ; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \ 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 ERRCLINAME:Global defines:1:19: error: invalid variable name
ERRCLIFMT-NEXT:Global define #1: 10VALUE=10 ERRCLINAME-NEXT:Global define #1: 10VALUE=10
ERRCLIFMT-NEXT: ^ ERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable. ; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \ RUN: %ProtectFileCheckOutput \

View File

@ -8,8 +8,10 @@
#include "llvm/Support/FileCheck.h" #include "llvm/Support/FileCheck.h"
#include "../lib/Support/FileCheckImpl.h" #include "../lib/Support/FileCheckImpl.h"
#include "llvm/Support/Regex.h"
#include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include <tuple>
#include <unordered_set> #include <unordered_set>
using namespace llvm; using namespace llvm;
@ -18,114 +20,6 @@ namespace {
class FileCheckTest : public ::testing::Test {}; 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) { static StringRef bufferize(SourceMgr &SM, StringRef Str) {
std::unique_ptr<MemoryBuffer> Buffer = std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
@ -152,6 +46,329 @@ static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
expectError<ErrorDiagnostic>(ExpectedMsg, std::move(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) { TEST_F(FileCheckTest, ParseVar) {
SourceMgr SM; SourceMgr SM;
StringRef OrigVarName = bufferize(SM, "GoodVar42"); StringRef OrigVarName = bufferize(SM, "GoodVar42");
@ -257,7 +474,7 @@ public:
size_t getLineNumber() const { return LineNumber; } size_t getLineNumber() const { return LineNumber; }
Expected<std::unique_ptr<ExpressionAST>> Expected<std::unique_ptr<Expression>>
parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) { parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
StringRef ExprBufferRef = bufferize(SM, Expr); StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<NumericVariable *> DefinedNumericVariable; Optional<NumericVariable *> DefinedNumericVariable;
@ -295,13 +512,36 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
expectDiagnosticError("unexpected characters after numeric variable name", expectDiagnosticError("unexpected characters after numeric variable name",
Tester.parseSubst("VAR GARBAGE:").takeError()); 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("VAR1:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), 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()); 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. // Invalid variable name.
expectDiagnosticError("invalid operand format '%VAR'", expectDiagnosticError("invalid operand format '%VAR'",
@ -342,8 +582,17 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid single operand expression. // Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded()); 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. // Valid expression with 2 or more operands.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded()); 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()); EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
expectDiagnosticError("unsupported operation '/'", expectDiagnosticError("unsupported operation '/'",
@ -367,6 +616,16 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
"invalid variable name", "invalid variable name",
Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError()); 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. // Valid legacy @LINE expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true), EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
Succeeded()); Succeeded());
@ -378,6 +637,18 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
expectDiagnosticError( expectDiagnosticError(
"unexpected characters at end of expression '+2'", "unexpected characters at end of expression '+2'",
Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError()); 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) { TEST_F(FileCheckTest, ParsePattern) {
@ -407,6 +678,12 @@ TEST_F(FileCheckTest, ParsePattern) {
// Valid numeric substitution. // Valid numeric substitution.
EXPECT_FALSE(Tester.parsePattern("[[#FOO]]")); 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) { TEST_F(FileCheckTest, Match) {
@ -417,12 +694,44 @@ TEST_F(FileCheckTest, Match) {
expectNotFoundError(Tester.match("FAIL").takeError()); expectNotFoundError(Tester.match("FAIL").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded()); 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(); Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]")); ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
expectNotFoundError(Tester.match("FAIL").takeError()); expectNotFoundError(Tester.match("FAIL").takeError());
expectNotFoundError(Tester.match("").takeError()); expectNotFoundError(Tester.match("").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded()); 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. // Check matching an undefined variable returns a NotFound error.
Tester.initNextPattern(); Tester.initNextPattern();
@ -441,7 +750,7 @@ TEST_F(FileCheckTest, Match) {
expectNotFoundError(Tester.match("18 21").takeError()); expectNotFoundError(Tester.match("18 21").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded()); 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. // the correct value for @LINE.
Tester.initNextPattern(); Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]")); ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
@ -476,14 +785,17 @@ TEST_F(FileCheckTest, Substitution) {
// Numeric substitution blocks constituted of defined numeric variables are // Numeric substitution blocks constituted of defined numeric variables are
// substituted for the variable's value. // substituted for the variable's value.
NumericVariable NVar("N", 1); NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
1);
NVar.setValue(10); NVar.setValue(10);
auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar); 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); /*InsertIdx=*/30);
SubstValue = SubstitutionN.getResult(); SubstValue = SubstitutionN.getResult();
ASSERT_THAT_EXPECTED(SubstValue, Succeeded()); ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
EXPECT_EQ("10", *SubstValue); EXPECT_EQ("A", *SubstValue);
// Substitution of an undefined numeric variable fails, error holds name of // Substitution of an undefined numeric variable fails, error holds name of
// undefined variable. // undefined variable.
@ -565,7 +877,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
GlobalDefines.emplace_back(std::string("LocalVar=FOO")); GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar=")); GlobalDefines.emplace_back(std::string("EmptyVar="));
GlobalDefines.emplace_back(std::string("#LocalNumVar1=18")); 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()); ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
// Create @LINE pseudo numeric variable and check it is present by matching // Create @LINE pseudo numeric variable and check it is present by matching
@ -588,12 +901,13 @@ TEST_F(FileCheckTest, FileCheckContext) {
StringRef LocalVarStr = "LocalVar"; StringRef LocalVarStr = "LocalVar";
StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1"); StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2"); StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
StringRef EmptyVarStr = "EmptyVar"; StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar"; StringRef UnknownVarStr = "UnknownVar";
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr); Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
Optional<NumericVariable *> DefinedNumericVariable; Optional<NumericVariable *> DefinedNumericVariable;
Expected<std::unique_ptr<ExpressionAST>> ExpressionASTPointer = Expected<std::unique_ptr<Expression>> ExpressionPointer =
P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber, /*IsLegacyLineExpr=*/false, LineNumber,
&Cxt, SM); &Cxt, SM);
@ -601,17 +915,25 @@ TEST_F(FileCheckTest, FileCheckContext) {
EXPECT_EQ(*LocalVar, "FOO"); EXPECT_EQ(*LocalVar, "FOO");
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
Expected<uint64_t> ExpressionVal = (*ExpressionASTPointer)->eval(); Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 18U); EXPECT_EQ(*ExpressionVal, 18U);
ExpressionASTPointer = P.parseNumericSubstitutionBlock( ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable, LocalNumVar2Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval(); ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 20U); 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()); ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
EXPECT_EQ(*EmptyVar, ""); EXPECT_EQ(*EmptyVar, "");
expectUndefErrors({UnknownVarStr}, UnknownVar.takeError()); expectUndefErrors({UnknownVarStr}, UnknownVar.takeError());
@ -624,20 +946,20 @@ TEST_F(FileCheckTest, FileCheckContext) {
// local variables, if it was created before. This is important because local // local variables, if it was created before. This is important because local
// variable clearing due to --enable-var-scope happens after numeric // variable clearing due to --enable-var-scope happens after numeric
// expressions are linked to the numeric variables they use. // expressions are linked to the numeric variables they use.
expectUndefErrors({"LocalNumVar2"}, expectUndefErrors({"LocalNumVar3"},
(*ExpressionASTPointer)->eval().takeError()); (*ExpressionPointer)->getAST()->eval().takeError());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock( ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM); LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval(); ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError()); expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
ExpressionASTPointer = P.parseNumericSubstitutionBlock( ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM); LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval(); ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError()); expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
expectUndefErrors({"EmptyVar"}, EmptyVar.takeError()); expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());
@ -655,11 +977,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
ASSERT_THAT_EXPECTED(GlobalVar, Succeeded()); ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
EXPECT_EQ(*GlobalVar, "BAR"); EXPECT_EQ(*GlobalVar, "BAR");
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock( ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM); LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval(); ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U); EXPECT_EQ(*ExpressionVal, 36U);
@ -667,11 +989,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
Cxt.clearLocalVars(); Cxt.clearLocalVars();
EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded()); EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
ExpressionASTPointer = P.parseNumericSubstitutionBlock( ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM); LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionASTPointer)->eval(); ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U); EXPECT_EQ(*ExpressionVal, 36U);
} }