FileCheck [6/12]: Introduce numeric variable definition

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch introduces support for defining
numeric variable in a CHECK directive.

This commit introduces support for defining numeric variable from a
litteral value in the input text. Numeric expressions can then use the
variable provided it is on a later line.

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

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

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

Tags: #llvm

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

llvm-svn: 362705
This commit is contained in:
Thomas Preud'homme 2019-06-06 13:21:06 +00:00
parent bf5bca5bea
commit 71d3f227a7
8 changed files with 706 additions and 337 deletions

View File

@ -571,15 +571,26 @@ FileCheck Numeric Substitution Blocks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:program:`FileCheck` also supports numeric substitution blocks that allow
checking for numeric values that satisfy a numeric expression constraint based
on numeric variables. This allows ``CHECK:`` directives to verify a numeric
relation between two numbers, such as the need for consecutive registers to be
used.
defining numeric variables and checking for numeric values that satisfy a
numeric expression constraint based on those variables via a numeric
substitution. This allows ``CHECK:`` directives to verify a numeric relation
between two numbers, such as the need for consecutive registers to be used.
The syntax of a numeric substitution block is ``[[#<NUMVAR><op><offset>]]``
where:
The syntax to define a numeric variable is ``[[#<NUMVAR>:]]`` where
``<NUMVAR>`` is the name of the numeric variable to define to the matching
value.
* ``<NUMVAR>`` is the name of a numeric variable defined on the command line.
For example:
.. code-block:: llvm
; CHECK: mov r[[#REG:]], 42
would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
The syntax of a numeric substitution is ``[[#<NUMVAR><op><offset>]]`` where:
* ``<NUMVAR>`` is the name of a defined numeric variable.
* ``<op>`` is an optional numeric operation to perform on the value of
``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
@ -590,31 +601,35 @@ where:
Spaces are accepted before, after and between any of these elements.
Unlike string substitution blocks, numeric substitution blocks only introduce
numeric substitutions which substitute a numeric expression for its value.
For example:
.. code-block:: llvm
; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]]
; CHECK: load r[[#REG:]], [r0]
; CHECK: load r[[#REG+1]], [r1]
The above example would match the line:
The above example would match the text:
.. code-block:: gas
add r5, r5, r6
load r5, [r0]
load r6, [r1]
but would not match the line:
but would not match the text:
.. code-block:: gas
add r5, r5, r7
load r5, [r0]
load r7, [r1]
due to ``7`` being unequal to ``5 + 1``.
The ``--enable-var-scope`` option has the same effect on numeric variables as
on string variables.
Important note: In its current implementation, a numeric expression cannot use
a numeric variable defined on the same line.
FileCheck Pseudo Numeric Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -624,9 +639,9 @@ fragility of the match file structure, as "``CHECK:``" lines contain absolute
line numbers in the same file, which have to be updated whenever line numbers
change due to text addition or deletion.
To support this case, FileCheck understands the ``@LINE`` pseudo numeric
variable which evaluates to the line number of the CHECK pattern where it is
found.
To support this case, FileCheck numeric expressions understand the ``@LINE``
pseudo numeric variable which evaluates to the line number of the CHECK pattern
where it is found.
This way match patterns can be put near the relevant test lines and include
relative line number references, for example:

View File

@ -41,7 +41,9 @@ struct FileCheckRequest {
//===----------------------------------------------------------------------===//
/// Class representing a numeric variable with a given value in a numeric
/// expression.
/// expression. Each definition of a variable gets its own instance of this
/// class. Variable uses share the same instance as their respective
/// definition.
class FileCheckNumericVariable {
private:
/// Name of the numeric variable.
@ -50,11 +52,19 @@ private:
/// Value of numeric variable, if defined, or None otherwise.
Optional<uint64_t> Value;
/// Line number where this variable is defined. Used to determine whether a
/// variable is defined on the same line as a given use.
size_t DefLineNumber;
public:
/// Constructor for a variable \p Name defined at line \p DefLineNumber.
FileCheckNumericVariable(size_t DefLineNumber, StringRef Name)
: Name(Name), DefLineNumber(DefLineNumber) {}
/// Constructor for numeric variable \p Name with a known \p Value at parse
/// time (e.g. the @LINE numeric variable).
FileCheckNumericVariable(StringRef Name, uint64_t Value)
: Name(Name), Value(Value) {}
: Name(Name), Value(Value), DefLineNumber(0) {}
/// \returns name of that numeric variable.
StringRef getName() const { return Name; }
@ -69,6 +79,9 @@ public:
/// Clears value of this numeric variable. \returns whether the variable was
/// already undefined.
bool clearValue();
/// \returns the line number where this variable is defined.
size_t getDefLineNumber() { return DefLineNumber; }
};
/// Type of functions evaluating a given binary operation.
@ -248,8 +261,11 @@ private:
/// When matching a given pattern, this holds the pointers to the classes
/// representing the last definitions of numeric variables defined in
/// previous patterns. Earlier definition of the variables, if any, have
/// their own class instance not referenced by this table.
/// previous patterns. Earlier definitions of the variables, if any, have
/// their own class instance not referenced by this table. When matching a
/// pattern all definitions for that pattern are recorded in the
/// NumericVariableDefs table in the FileCheckPattern instance of that
/// pattern.
StringMap<FileCheckNumericVariable *> GlobalNumericVariableTable;
/// Vector holding pointers to all parsed numeric expressions. Used to
@ -292,7 +308,8 @@ private:
/// Makes a new numeric variable and registers it for destruction when the
/// context is destroyed.
FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value);
template <class... Types>
FileCheckNumericVariable *makeNumericVariable(Types... args);
/// Makes a new string substitution and registers it for destruction when the
/// context is destroyed.
@ -325,17 +342,39 @@ class FileCheckPattern {
/// and the value of numeric expression "N+1" at offset 6.
std::vector<FileCheckSubstitution *> Substitutions;
/// Maps names of string variables defined in a pattern to the parenthesized
/// capture numbers of their last definition.
/// Maps names of string variables defined in a pattern to the number of
/// their parenthesis group in RegExStr capturing their last definition.
///
/// E.g. for the pattern "foo[[bar:.*]]baz[[bar]]quux[[bar:.*]]",
/// VariableDefs will map "bar" to 2 corresponding to the second definition
/// of "bar".
/// E.g. for the pattern "foo[[bar:.*]]baz([[bar]][[QUUX]][[bar:.*]])",
/// RegExStr will be "foo(.*)baz(\1<quux value>(.*))" where <quux value> is
/// the value captured for QUUX on the earlier line where it was defined, and
/// VariableDefs will map "bar" to the third parenthesis group which captures
/// the second definition of "bar".
///
/// Note: uses std::map rather than StringMap to be able to get the key when
/// iterating over values.
std::map<StringRef, unsigned> VariableDefs;
/// Structure representing the definition of a numeric variable in a pattern.
/// It holds the pointer to the class representing the numeric variable whose
/// value is being defined and the number of the parenthesis group in
/// RegExStr to capture that value.
struct FileCheckNumExprMatch {
/// Pointer to class representing the numeric variable whose value is being
/// defined.
FileCheckNumericVariable *DefinedNumericVariable;
/// Number of the parenthesis group in RegExStr that captures the value of
/// this numeric variable definition.
unsigned CaptureParenGroup;
};
/// Holds the number of the parenthesis group in RegExStr and pointer to the
/// corresponding FileCheckNumericVariable class instance of all numeric
/// variable definitions. Used to set the matched value of all those
/// variables.
StringMap<FileCheckNumExprMatch> NumericVariableDefs;
/// Pointer to a class instance holding the global state shared by all
/// patterns:
/// - separate tables with the values of live string and numeric variables
@ -346,13 +385,14 @@ class FileCheckPattern {
Check::FileCheckType CheckTy;
/// Contains the number of line this pattern is in.
unsigned LineNumber;
/// Line number for this CHECK pattern. Used to determine whether a variable
/// definition is made on an earlier line to the one with this CHECK.
size_t LineNumber;
public:
explicit FileCheckPattern(Check::FileCheckType Ty,
FileCheckPatternContext *Context)
: Context(Context), CheckTy(Ty) {}
FileCheckPattern(Check::FileCheckType Ty, FileCheckPatternContext *Context,
size_t Line)
: Context(Context), CheckTy(Ty), LineNumber(Line) {}
/// \returns the location in source code.
SMLoc getLoc() const { return PatternLoc; }
@ -363,31 +403,37 @@ public:
/// \returns whether \p C is a valid first character for a variable name.
static bool isValidVarNameStart(char C);
/// Verifies that the string at the start of \p Str is a well formed
/// variable. \returns false if it is and sets \p IsPseudo to indicate if it
/// is a pseudo variable and \p TrailIdx to the position of the last
/// character that is part of the variable name. Otherwise, only
/// \returns true.
static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
/// Parses a numeric substitution involving (pseudo if \p IsPseudo is true)
/// variable \p Name with the string corresponding to the operation being
/// performed in \p Trailer. \returns the class representing the numeric
/// expression being substituted or nullptr if parsing fails, in which case
/// errors are reported on \p SM.
FileCheckNumExpr *parseNumericSubstitution(StringRef Name, bool IsPseudo,
StringRef Trailer,
/// Parses the string at the start of \p Str for a variable name and \returns
/// whether the variable name is ill-formed. If parsing succeeded, sets
/// \p IsPseudo to indicate if it is a pseudo variable, sets \p Name to the
/// parsed variable name and strips \p Str from the variable name.
static bool parseVariable(StringRef &Str, StringRef &Name, bool &IsPseudo);
/// Parses \p Expr for the definition of a numeric variable, returning an
/// error if \p Context already holds a string variable with the same name.
/// \returns whether parsing fails, in which case errors are reported on
/// \p SM. Otherwise, sets \p Name to the name of the parsed numeric
/// variable.
static bool parseNumericVariableDefinition(StringRef &Expr, StringRef &Name,
FileCheckPatternContext *Context,
const SourceMgr &SM);
/// Parses \p Expr for a numeric substitution block. \returns the class
/// representing the AST of the numeric expression whose value must be
/// substituted, or nullptr if parsing fails, in which case errors are
/// reported on \p SM. Sets \p DefinedNumericVariable to point to the class
/// representing the numeric variable defined in this numeric substitution
/// block, or nullptr if this block does not define any variable.
FileCheckNumExpr *parseNumericSubstitutionBlock(
StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable,
const SourceMgr &SM) const;
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
/// instance accordingly.
///
/// \p Prefix provides which prefix is being matched, \p Req describes the
/// global options that influence the parsing such as whitespace
/// canonicalization, \p SM provides the SourceMgr used for error reports,
/// and \p LineNumber is the line number in the input file from which the
/// pattern string was read. \returns true in case of an error, false
/// otherwise.
bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
unsigned LineNumber, const FileCheckRequest &Req);
/// canonicalization, \p SM provides the SourceMgr used for error reports.
/// \returns true in case of an error, false otherwise.
bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
const FileCheckRequest &Req);
/// Matches the pattern string against the input buffer \p Buffer
///
/// \returns the position that is matched or npos if there is no match. If
@ -396,8 +442,11 @@ public:
///
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
/// instance provides the current values of FileCheck string variables and
/// is updated if this match defines new values.
size_t match(StringRef Buffer, size_t &MatchLen) const;
/// is updated if this match defines new values. Likewise, the
/// GlobalNumericVariableTable StringMap in the same class provides the
/// current values of FileCheck numeric variables and is updated if this
/// match defines new numeric values.
size_t match(StringRef Buffer, size_t &MatchLen, const SourceMgr &SM) const;
/// Prints the value of successful substitutions or the name of the undefined
/// string or numeric variable preventing a successful substitution.
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
@ -427,6 +476,17 @@ private:
/// \returns the offset of the closing sequence within Str, or npos if it
/// was not found.
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
/// Parses \p Expr for the use of a numeric variable. \returns the pointer to
/// the class instance representing that variable if successful, or nullptr
/// otherwise, in which case errors are reported on \p SM.
FileCheckNumericVariable *parseNumericVariableUse(StringRef &Expr,
const SourceMgr &SM) const;
/// Parses \p Expr for a binary operation.
/// \returns the class representing the binary operation of the numeric
/// expression, or nullptr if parsing fails, in which case errors are
/// reported on \p SM.
FileCheckNumExpr *parseBinop(StringRef &Expr, const SourceMgr &SM) const;
};
//===----------------------------------------------------------------------===//

View File

@ -39,11 +39,12 @@ bool FileCheckNumericVariable::clearValue() {
}
Optional<uint64_t> FileCheckNumExpr::eval() const {
Optional<uint64_t> LeftOp = this->LeftOp->getValue();
assert(LeftOp && "Evaluating an empty numeric expression");
Optional<uint64_t> LeftOpValue = LeftOp->getValue();
// Variable is undefined.
if (!LeftOp)
if (!LeftOpValue)
return None;
return EvalBinop(*LeftOp, RightOp);
return EvalBinop(*LeftOpValue, RightOp);
}
StringRef FileCheckNumExpr::getUndefVarName() const {
@ -84,8 +85,8 @@ bool FileCheckPattern::isValidVarNameStart(char C) {
return C == '_' || isalpha(C);
}
bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
unsigned &TrailIdx) {
bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name,
bool &IsPseudo) {
if (Str.empty())
return true;
@ -107,7 +108,8 @@ bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
ParsedOneChar = true;
}
TrailIdx = I;
Name = Str.take_front(I);
Str = Str.substr(I);
return false;
}
@ -122,24 +124,52 @@ static char popFront(StringRef &S) {
return C;
}
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp + RightOp;
}
static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp - RightOp;
bool FileCheckPattern::parseNumericVariableDefinition(
StringRef &Expr, StringRef &Name, FileCheckPatternContext *Context,
const SourceMgr &SM) {
bool IsPseudo;
if (parseVariable(Expr, Name, IsPseudo)) {
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
"invalid variable name");
return true;
}
FileCheckNumExpr *
FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
StringRef Trailer,
if (IsPseudo) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"definition of pseudo numeric variable unsupported");
return true;
}
// Detect collisions between string and numeric variables when the latter
// is created later than the former.
if (Context->DefinedVariableTable.find(Name) !=
Context->DefinedVariableTable.end()) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"string variable with name '" + Name + "' already exists");
return true;
}
return false;
}
FileCheckNumericVariable *
FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
const SourceMgr &SM) const {
bool IsPseudo;
StringRef Name;
if (parseVariable(Expr, Name, IsPseudo)) {
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
"invalid variable name");
return nullptr;
}
if (IsPseudo && !Name.equals("@LINE")) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"invalid pseudo numeric variable '" + Name + "'");
return nullptr;
}
// This method is indirectly called from ParsePattern for all numeric
// This method is indirectly called from parsePattern for all numeric
// variable definitions and uses in the order in which they appear in the
// CHECK pattern. For each definition, the pointer to the class instance of
// the corresponding numeric variable definition is stored in
@ -152,16 +182,40 @@ FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
return nullptr;
}
FileCheckNumericVariable *LeftOp = VarTableIter->second;
FileCheckNumericVariable *NumericVariable = VarTableIter->second;
if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"numeric variable '" + Name +
"' defined on the same line as used");
return nullptr;
}
return NumericVariable;
}
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp + RightOp;
}
static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp - RightOp;
}
FileCheckNumExpr *FileCheckPattern::parseBinop(StringRef &Expr,
const SourceMgr &SM) const {
FileCheckNumericVariable *LeftOp = parseNumericVariableUse(Expr, SM);
if (!LeftOp) {
// Error reporting done in parseNumericVariableUse().
return nullptr;
}
// Check if this is a supported operation and select a function to perform
// it.
Trailer = Trailer.ltrim(SpaceChars);
if (Trailer.empty()) {
Expr = Expr.ltrim(SpaceChars);
if (Expr.empty())
return Context->makeNumExpr(add, LeftOp, 0);
}
SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
char Operator = popFront(Trailer);
SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
char Operator = popFront(Expr);
binop_eval_t EvalBinop;
switch (Operator) {
case '+':
@ -178,35 +232,76 @@ FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
}
// Parse right operand.
Trailer = Trailer.ltrim(SpaceChars);
if (Trailer.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
Expr = Expr.ltrim(SpaceChars);
if (Expr.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
"missing operand in numeric expression");
return nullptr;
}
uint64_t RightOp;
if (Trailer.consumeInteger(10, RightOp)) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
"invalid offset in numeric expression '" + Trailer + "'");
if (Expr.consumeInteger(10, RightOp)) {
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
"invalid offset in numeric expression '" + Expr + "'");
return nullptr;
}
Trailer = Trailer.ltrim(SpaceChars);
if (!Trailer.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
Expr = Expr.ltrim(SpaceChars);
if (!Expr.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
"unexpected characters at end of numeric expression '" +
Trailer + "'");
Expr + "'");
return nullptr;
}
return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
}
bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
SourceMgr &SM, unsigned LineNumber,
FileCheckNumExpr *FileCheckPattern::parseNumericSubstitutionBlock(
StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable,
const SourceMgr &SM) const {
// Parse the numeric variable definition.
DefinedNumericVariable = nullptr;
size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) {
StringRef DefExpr = Expr.substr(0, DefEnd);
StringRef UseExpr = Expr = Expr.substr(DefEnd + 1);
DefExpr = DefExpr.ltrim(SpaceChars);
StringRef Name;
if (parseNumericVariableDefinition(DefExpr, Name, Context, SM)) {
// Invalid variable definition. Error reporting done in parsing function.
return nullptr;
}
DefinedNumericVariable =
Context->makeNumericVariable(this->LineNumber, Name);
DefExpr = DefExpr.ltrim(SpaceChars);
if (!DefExpr.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()),
SourceMgr::DK_Error,
"invalid numeric variable definition");
return nullptr;
}
UseExpr = UseExpr.ltrim(SpaceChars);
if (!UseExpr.empty()) {
SM.PrintMessage(
SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error,
"unexpected string after variable definition: '" + UseExpr + "'");
return nullptr;
}
return Context->makeNumExpr(add, nullptr, 0);
}
// Parse the numeric expression itself.
Expr = Expr.ltrim(SpaceChars);
return parseBinop(Expr, SM);
}
bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
SourceMgr &SM,
const FileCheckRequest &Req) {
bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
this->LineNumber = LineNumber;
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
// Create fake @LINE pseudo variable definition.
@ -292,11 +387,11 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
// String and numeric substitution blocks. String substitution blocks come
// in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
// other regex) and assigns it to the string variable 'foo'. The latter
// substitutes foo's value. Numeric substitution blocks start with a
// '#' sign after the double brackets and only have the substitution form.
// Both string and numeric variables must satisfy the regular expression
// "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
// errors.
// substitutes foo's value. Numeric substitution blocks work the same way
// as string ones, but start with a '#' sign after the double brackets.
// Both string and numeric variable names must satisfy the regular
// expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch
// some common errors.
if (PatternStr.startswith("[[")) {
StringRef UnparsedPatternStr = PatternStr.substr(2);
// Find the closing bracket pair ending the match. End is going to be an
@ -316,37 +411,36 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
// index of the first unparsed character.
PatternStr = UnparsedPatternStr.substr(End + 2);
bool IsDefinition = false;
StringRef DefName;
StringRef SubstStr;
StringRef MatchRegexp;
size_t SubstInsertIdx = RegExStr.size();
// Parse string variable or legacy numeric expression.
if (!IsNumBlock) {
size_t VarEndIdx = MatchStr.find(":");
if (IsNumBlock)
MatchStr = MatchStr.ltrim(SpaceChars);
else {
size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
if (SpacePos != StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
SourceMgr::DK_Error, "unexpected whitespace");
return true;
}
}
// Get the variable name (e.g. "foo") and verify it is well formed.
// Get the name (e.g. "foo") and verify it is well formed.
bool IsPseudo;
unsigned TrailIdx;
if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
StringRef Name;
StringRef OrigMatchStr = MatchStr;
if (parseVariable(MatchStr, Name, IsPseudo)) {
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
SourceMgr::DK_Error, "invalid variable name");
return true;
}
size_t SubstInsertIdx = RegExStr.size();
FileCheckNumExpr *NumExpr;
StringRef Name = MatchStr.substr(0, TrailIdx);
StringRef Trailer = MatchStr.substr(TrailIdx);
bool IsVarDef = (VarEndIdx != StringRef::npos);
if (IsVarDef) {
if (IsPseudo || !Trailer.consume_front(":")) {
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
IsDefinition = (VarEndIdx != StringRef::npos);
if (IsDefinition) {
if ((IsPseudo || !MatchStr.consume_front(":"))) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
SourceMgr::DK_Error,
"invalid name in string variable definition");
return true;
@ -357,51 +451,89 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
if (Context->GlobalNumericVariableTable.find(Name) !=
Context->GlobalNumericVariableTable.end()) {
SM.PrintMessage(
SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"numeric variable with name '" + Name + "' already exists");
return true;
}
DefName = Name;
MatchRegexp = MatchStr;
} else {
if (IsPseudo) {
MatchStr = OrigMatchStr;
IsNumBlock = true;
} else
SubstStr = Name;
}
}
if (IsNumBlock || (!IsVarDef && IsPseudo)) {
NumExpr = parseNumericSubstitution(Name, IsPseudo, Trailer, SM);
// Parse numeric substitution block.
FileCheckNumExpr *NumExpr;
FileCheckNumericVariable *DefinedNumericVariable;
if (IsNumBlock) {
NumExpr =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
if (NumExpr == nullptr)
return true;
IsNumBlock = true;
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = DefinedNumericVariable->getName();
MatchRegexp = StringRef("[0-9]+");
} else
SubstStr = MatchStr;
}
// Handle substitutions: [[foo]] and [[#<foo expr>]].
if (!IsVarDef) {
if (!IsDefinition) {
// Handle substitution of string variables that were defined earlier on
// the same line by emitting a backreference.
if (!IsNumBlock && VariableDefs.find(Name) != VariableDefs.end()) {
unsigned CaptureParen = VariableDefs[Name];
if (CaptureParen < 1 || CaptureParen > 9) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
// the same line by emitting a backreference. Numeric expressions do
// not support substituting a numeric variable defined on the same
// line.
if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {
unsigned CaptureParenGroup = VariableDefs[SubstStr];
if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {
SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),
SourceMgr::DK_Error,
"Can't back-reference more than 9 variables");
return true;
}
AddBackrefToRegEx(CaptureParen);
AddBackrefToRegEx(CaptureParenGroup);
} else {
// Handle substitution of string variables ([[<var>]]) defined in
// previous CHECK patterns, and substitution of numeric expressions.
FileCheckSubstitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(MatchStr, NumExpr,
? Context->makeNumericSubstitution(SubstStr, NumExpr,
SubstInsertIdx)
: Context->makeStringSubstitution(MatchStr, SubstInsertIdx);
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
continue;
}
// Handle variable definitions: [[foo:.*]].
VariableDefs[Name] = CurParen;
// Handle variable definitions: [[<def>:(...)]] and
// [[#(...)<def>:(...)]].
if (IsNumBlock) {
FileCheckNumExprMatch NumExprDef = {DefinedNumericVariable, CurParen};
NumericVariableDefs[DefName] = NumExprDef;
// This store is done here rather than in match() to allow
// parseNumericVariableUse() to get the pointer to the class instance
// of the right variable definition corresponding to a given numeric
// variable use.
Context->GlobalNumericVariableTable[DefName] = DefinedNumericVariable;
} else {
VariableDefs[DefName] = CurParen;
// Mark the string variable as defined to detect collisions between
// string and numeric variables in parseNumericVariableUse() and
// DefineCmdlineVariables() when the latter is created later than the
// former. We cannot reuse GlobalVariableTable for this by populating
// it with an empty string since we would then lose the ability to
// detect the use of an undefined variable in match().
Context->DefinedVariableTable[DefName] = true;
}
RegExStr += '(';
++CurParen;
if (AddRegExToRegEx(Trailer, CurParen, SM))
if (AddRegExToRegEx(MatchRegexp, CurParen, SM))
return true;
RegExStr += ')';
@ -444,7 +576,8 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
RegExStr += Backref;
}
size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
const SourceMgr &SM) const {
// If this is the EOF pattern, match it immediately.
if (CheckTy == Check::CheckEOF) {
MatchLen = 0;
@ -501,6 +634,25 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
MatchInfo[VariableDef.second];
}
// If this defines any numeric variables, remember their values.
for (const auto &NumericVariableDef : NumericVariableDefs) {
const FileCheckNumExprMatch &NumericVariableMatch =
NumericVariableDef.getValue();
unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup;
assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error");
FileCheckNumericVariable *DefinedNumericVariable =
NumericVariableMatch.DefinedNumericVariable;
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
uint64_t Val;
if (MatchedValue.getAsInteger(10, Val)) {
SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()),
SourceMgr::DK_Error, "Unable to represent numeric value");
}
if (DefinedNumericVariable->setValue(Val))
assert(false && "Numeric variable redefined");
}
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
// the required preceding newline, which is consumed by the pattern in the
// case of CHECK-EMPTY but not CHECK-NEXT.
@ -643,10 +795,11 @@ FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
return NumExprs.back().get();
}
template <class... Types>
FileCheckNumericVariable *
FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
FileCheckPatternContext::makeNumericVariable(Types... args) {
NumericVariables.push_back(
llvm::make_unique<FileCheckNumericVariable>(Name, Value));
llvm::make_unique<FileCheckNumericVariable>(args...));
return NumericVariables.back().get();
}
@ -941,9 +1094,9 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
ImplicitNegativeChecks.push_back(
FileCheckPattern(Check::CheckNot, &PatternContext));
ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer,
"IMPLICIT-CHECK", SM, 0, Req);
FileCheckPattern(Check::CheckNot, &PatternContext, 0));
ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
"IMPLICIT-CHECK", SM, Req);
}
std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks;
@ -1004,8 +1157,8 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
// Parse the pattern.
FileCheckPattern P(CheckTy, &PatternContext);
if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req))
FileCheckPattern P(CheckTy, &PatternContext, LineNumber);
if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req))
return true;
// Verify that CHECK-LABEL lines do not define or use variables
@ -1049,7 +1202,7 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
// prefix as a filler for the error message.
if (!DagNotMatches.empty()) {
CheckStrings.emplace_back(
FileCheckPattern(Check::CheckEOF, &PatternContext),
FileCheckPattern(Check::CheckEOF, &PatternContext, LineNumber + 1),
*Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
@ -1223,7 +1376,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
size_t CurrentMatchLen;
// get a match at current start point
size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen);
size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen, SM);
if (i == 1)
FirstMatchPos = LastPos + MatchPos;
@ -1344,7 +1497,7 @@ bool FileCheckString::CheckNot(
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
size_t MatchLen = 0;
size_t Pos = Pat->match(Buffer, MatchLen);
size_t Pos = Pat->match(Buffer, MatchLen, SM);
if (Pos == StringRef::npos) {
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
@ -1404,7 +1557,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// CHECK-DAG group.
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
StringRef MatchBuffer = Buffer.substr(MatchPos);
size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen);
size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen, SM);
// With a group of CHECK-DAGs, a single mismatching means the match on
// that group of CHECK-DAGs fails immediately.
if (MatchPosBuf == StringRef::npos) {
@ -1568,7 +1721,8 @@ bool FileCheckPatternContext::defineCmdlineVariables(
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
if (CmdlineDef.find('=') == StringRef::npos) {
size_t EqIdx = CmdlineDef.find('=');
if (EqIdx == StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
SourceMgr::DK_Error,
"Missing equal sign in global definition");
@ -1578,27 +1732,28 @@ bool FileCheckPatternContext::defineCmdlineVariables(
// Numeric variable definition.
if (CmdlineDef[0] == '#') {
bool IsPseudo;
unsigned TrailIdx;
size_t EqIdx = CmdlineDef.find('=');
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
SourceMgr::DK_Error,
"invalid name in numeric variable definition '" +
CmdlineName + "'");
StringRef VarName;
SMLoc CmdlineNameLoc = SMLoc::getFromPointer(CmdlineName.data());
bool ParseError = FileCheckPattern::parseNumericVariableDefinition(
CmdlineName, VarName, this, SM);
// Check that CmdlineName starts with a valid numeric variable to be
// defined and that it is not followed that anything. That second check
// detects cases like "FOO+2" in a "FOO+2=10" definition.
if (ParseError || !CmdlineName.empty()) {
if (!ParseError)
SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error,
"invalid variable name");
ErrorFound = true;
continue;
}
// Detect collisions between string and numeric variables when the latter
// is created later than the former.
if (DefinedVariableTable.find(CmdlineName) !=
DefinedVariableTable.end()) {
if (DefinedVariableTable.find(VarName) != DefinedVariableTable.end()) {
SM.PrintMessage(
SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error,
"string variable with name '" + CmdlineName + "' already exists");
SMLoc::getFromPointer(VarName.data()), SourceMgr::DK_Error,
"string variable with name '" + VarName + "' already exists");
ErrorFound = true;
continue;
}
@ -1613,21 +1768,25 @@ bool FileCheckPatternContext::defineCmdlineVariables(
ErrorFound = true;
continue;
}
auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
auto DefinedNumericVariable = makeNumericVariable(0, VarName);
DefinedNumericVariable->setValue(Val);
// Record this variable definition.
GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
GlobalNumericVariableTable[DefinedNumericVariable->getName()] =
DefinedNumericVariable;
} else {
// String variable definition.
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
StringRef Name = CmdlineNameVal.first;
StringRef CmdlineName = CmdlineNameVal.first;
StringRef OrigCmdlineName = CmdlineName;
StringRef Name;
bool IsPseudo;
unsigned TrailIdx;
if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
IsPseudo || TrailIdx != Name.size() || Name.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"invalid name in string variable definition '" + Name +
"'");
if (FileCheckPattern::parseVariable(CmdlineName, Name, IsPseudo) ||
IsPseudo || !CmdlineName.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(OrigCmdlineName.data()),
SourceMgr::DK_Error,
"invalid name in string variable definition '" +
OrigCmdlineName + "'");
ErrorFound = true;
continue;
}
@ -1646,7 +1805,7 @@ bool FileCheckPatternContext::defineCmdlineVariables(
// Mark the string variable as defined to detect collisions between
// string and numeric variables in DefineCmdlineVariables when the latter
// is created later than the former. We cannot reuse GlobalVariableTable
// for that by populating it with an empty string since we would then
// for this by populating it with an empty string since we would then
// lose the ability to detect the use of an undefined variable in
// match().
DefinedVariableTable[Name] = true;

View File

@ -4,7 +4,7 @@
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE'
NUMERRCLIFMT: Global defines:1:20: error: invalid variable name
NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10
NUMERRCLIFMT-NEXT: {{^ \^$}}
@ -12,7 +12,7 @@ NUMERRCLIFMT-NEXT: {{^ \^$}}
RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO
NUMERRCLIPSEUDO: Global defines:1:20: error: invalid name in numeric variable definition '@VALUE'
NUMERRCLIPSEUDO: Global defines:1:20: error: definition of pseudo numeric variable unsupported
NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10
NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
@ -20,7 +20,7 @@ NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL
NUMERRCLITRAIL: Global defines:1:20: error: invalid name in numeric variable definition 'VALUE+2'
NUMERRCLITRAIL: Global defines:1:20: error: invalid variable name
NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10
NUMERRCLITRAIL-NEXT: {{^ \^$}}

View File

@ -1,21 +1,42 @@
RUN: FileCheck -D#VAR1=11 --input-file %s %s
RUN: FileCheck --input-file %s %s
; We use CHECK-NEXT directives to force a match on all lines with digits.
; Numeric expressions using variables defined on the command-line without
; spaces
; Numeric variable definition without spaces.
DEF NO SPC
11
CHECK-LABEL: DEF NO SPC
CHECK-NEXT: [[#VAR1:]]
; Numeric variable definition with different spacing.
DEF SPC
11
11
11
CHECK-LABEL: DEF SPC
CHECK-NEXT: [[# VAR1a:]]
CHECK-NEXT: [[# VAR1b :]]
CHECK-NEXT: [[# VAR1c : ]]
; Numeric expressions using variables defined on other lines without spaces.
USE NO SPC
11
12
10
CHECK-LABEL: USE NO SPC
11
11
11
CHECK-LABEL: USE
CHECK-NEXT: [[#VAR1]]
CHECK-NEXT: [[#VAR1+1]]
CHECK-NEXT: [[#VAR1-1]]
CHECK-NEXT: [[#VAR1a]]
CHECK-NEXT: [[#VAR1b]]
CHECK-NEXT: [[#VAR1c]]
; Numeric expressions using variables defined on the command-line in alternate
; spacing
USE ALT SPC
; Numeric expressions using variables defined on other lines with different
; spacing.
USE SPC
11
11
12
@ -26,7 +47,7 @@ USE ALT SPC
10
10
10
CHECK-LABEL: USE ALT SPC
CHECK-LABEL: USE SPC
CHECK-NEXT: [[# VAR1]]
CHECK-NEXT: [[# VAR1 ]]
CHECK-NEXT: [[# VAR1+1]]
@ -39,7 +60,7 @@ CHECK-NEXT: [[# VAR1 - 1]]
CHECK-NEXT: [[# VAR1 - 1 ]]
; Numeric expressions using variables defined on the command-line and an
; immediate interpreted as an unsigned value
; immediate interpreted as an unsigned value.
; Note: 9223372036854775819 = 0x8000000000000000 + 11
; 9223372036854775808 = 0x8000000000000000
USE UNSIGNED IMM
@ -47,7 +68,7 @@ USE UNSIGNED IMM
CHECK-LABEL: USE UNSIGNED IMM
CHECK-NEXT: [[#VAR1+9223372036854775808]]
; Numeric expression using undefined variable
; Numeric expression using undefined variable.
RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
@ -59,37 +80,51 @@ UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:30: error: using undefined nu
UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
UNDEF-USE-MSG-NEXT: {{^ \^$}}
; Numeric expression with unsupported operator
RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \
; Numeric expression with unsupported operator.
RUN: not FileCheck -D#NUMVAR=10 --check-prefix INVAL-OP --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s
INVALID OPERATOR
VAR1*2: 22
NUMVAR*2: 22
INVAL-OP-LABEL: INVALID OPERATOR
INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]]
INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: unsupported numeric operation '*'
INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}}
INVAL-OP-NEXT: NUMVAR*2: [[#NUMVAR*2]]
INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:35: error: unsupported numeric operation '*'
INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: NUMVAR*2: {{\[\[#NUMVAR\*2\]\]}}
INVAL-OP-MSG-NEXT: {{^ \^$}}
; Name conflict between Numeric variable definition and string variable
; definition
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s
RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s
; definition whether from the command-line or input text.
RUN: not FileCheck --check-prefixes CONFLICT,CONFLICT1,CONFLICT2 --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-STR-CONFLICT %s
RUN: not FileCheck -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT2 --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-STR-CONFLICT %s
RUN: not FileCheck -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CLI-STR-CONFLICT %s
RUN: not FileCheck --check-prefixes CONFLICT,CONFLICT3,CONFLICT4 --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-NUM-CONFLICT %s
RUN: not FileCheck -DSTRVAR=foobar --check-prefixes CONFLICT,CONFLICT4 --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-NUM-CONFLICT %s
RUN: not FileCheck -DSTRVAR=foobar -D#STRVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CLI-NUM-CONFLICT %s
PATVAR NUMVAR CONFLICT
STRVAR NUMVAR CONFLICT
redef1 42
foobar
CONFLICT-LABEL: PATVAR NUMVAR CONFLICT
CONFLICT1-NEXT: [[NUMVAR:foo.*]]
CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists
CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}}
CLI-INPUT-PAT-CONFLICT-NEXT: {{^ \^$}}
CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists
CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar
CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}}
CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: string variable with name 'PATVAR' already exists
CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42
CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
redef2 42
CONFLICT-LABEL: STRVAR NUMVAR CONFLICT
CONFLICT1-NEXT: redef1 [[#NUMVAR:]]
CONFLICT2: [[NUMVAR:foo.*]]
CONFLICT3: [[STRVAR:foo.*]]
CONFLICT4: redef2 [[#STRVAR:]]
INPUT-STR-CONFLICT: numeric-expression.txt:[[#@LINE-3]]:14: error: numeric variable with name 'NUMVAR' already exists
INPUT-STR-CONFLICT-NEXT: {{C}}ONFLICT2: {{\[\[NUMVAR:foo\.\*\]\]}}
INPUT-STR-CONFLICT-NEXT: {{^ \^$}}
CLI-STR-CONFLICT: Global defines:2:19: error: numeric variable with name 'NUMVAR' already exists
CLI-STR-CONFLICT-NEXT: Global define #2: NUMVAR=foobar
CLI-STR-CONFLICT-NEXT: {{^ \^$}}
INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists
INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#STRVAR:\]\]}}
INPUT-NUM-CONFLICT-NEXT: {{^ \^$}}
CLI-NUM-CONFLICT: Global defines:2:20: error: string variable with name 'STRVAR' already exists
CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42
CLI-NUM-CONFLICT-NEXT: {{^ \^$}}

View File

@ -1,19 +1,22 @@
; Test that variables not starting with dollar sign get undefined after a
; CHECK-LABEL directive iff --enable-var-scope is used.
RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s
RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
; Reference run: variables remain defined at all time when not using
; --enable-var-scope option.
RUN: FileCheck --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s
RUN: FileCheck --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
RUN: not FileCheck --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s
RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \
RUN: not FileCheck --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s
RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \
RUN: not FileCheck --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \
RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s
local1
global1
CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]]
CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]]
CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM:]]
CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM:]]
local2
global2

View File

@ -1,6 +1,6 @@
; RUN: FileCheck -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
; RUN: FileCheck -v -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s
; RUN: FileCheck -vv -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s
; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s
; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s
foo
bar
@ -29,36 +29,33 @@ VV-NEXT: verbose.txt:[[@LINE-22]]:1: note: scanning from here
VV-NEXT: {{^}}bar{{$}}
VV-NEXT: {{^}}^{{$}}
NUMVAR:42
NUMVAR=42
NUMVAR - 1:41
CHECK: NUMVAR:[[#NUMVAR]]
CHECK: NUMVAR=[[#NUMVAR:]]
CHECK-NOT: [[#NUMVAR + 1]]
CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]]
V: verbose.txt:[[#@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
V-NEXT: {{C}}HECK: {{NUMVAR:[[][[]#NUMVAR[]][]]$}}
V-NEXT: {{C}}HECK: {{NUMVAR=[[][[]#NUMVAR:[]][]]$}}
V-NEXT: {{^ \^$}}
V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
V-NEXT: {{^}}NUMVAR:42{{$}}
V-NEXT: {{^}}^~~~~~~~~{{$}}
V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with "NUMVAR" equal to "42"
V-NEXT: {{^}}NUMVAR:42{{$}}
V-NEXT: {{^}}NUMVAR=42{{$}}
V-NEXT: {{^}}^~~~~~~~~{{$}}
V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input
V-NEXT: verbose.txt:[[#@LINE-9]]:13: remark: {{C}}HECK-NEXT: expected string found in input
V-NEXT: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}}
V-NEXT: {{^ \^$}}
V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here
V-NEXT: verbose.txt:[[#@LINE-15]]:1: note: found here
V-NEXT: {{^}}NUMVAR - 1:41{{$}}
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with "NUMVAR - 1" equal to "41"
V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: with "NUMVAR - 1" equal to "41"
V-NEXT: {{^}}NUMVAR - 1:41{{$}}
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
VV-NEXT: verbose.txt:[[#@LINE-23]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
VV-NEXT: verbose.txt:[[#@LINE-20]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
VV-NEXT: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}}
VV-NEXT: {{^ \^$}}
VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here
VV-NEXT: verbose.txt:[[#@LINE-25]]:1: note: scanning from here
VV-NEXT: {{^}}NUMVAR - 1:41{{$}}
VV-NEXT: {{^}}^{{$}}

View File

@ -15,12 +15,16 @@ namespace {
class FileCheckTest : public ::testing::Test {};
TEST_F(FileCheckTest, NumericVariable) {
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
// Undefined variable: getValue and clearValue fails, setValue works.
FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO");
EXPECT_EQ("FOO", FooVar.getName());
// Defined variable: getValue returns a value, setValue fails and value
// remains unchanged.
llvm::Optional<uint64_t> Value = FooVar.getValue();
EXPECT_FALSE(Value);
EXPECT_TRUE(FooVar.clearValue());
EXPECT_FALSE(FooVar.setValue(42));
// Defined variable: getValue returns value set, setValue fails.
Value = FooVar.getValue();
EXPECT_TRUE(Value);
EXPECT_EQ(42U, *Value);
EXPECT_TRUE(FooVar.setValue(43));
@ -33,12 +37,6 @@ TEST_F(FileCheckTest, NumericVariable) {
Value = FooVar.getValue();
EXPECT_FALSE(Value);
EXPECT_TRUE(FooVar.clearValue());
// Undefined variable: setValue works, getValue returns value set.
EXPECT_FALSE(FooVar.setValue(43));
Value = FooVar.getValue();
EXPECT_TRUE(Value);
EXPECT_EQ(43U, *Value);
}
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
@ -78,67 +76,69 @@ TEST_F(FileCheckTest, ValidVarNameStart) {
}
TEST_F(FileCheckTest, ParseVar) {
StringRef VarName = "GoodVar42";
StringRef OrigVarName = "GoodVar42";
StringRef VarName = OrigVarName;
StringRef ParsedName;
bool IsPseudo = true;
unsigned TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(ParsedName, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size());
VarName = "$GoodGlobalVar";
VarName = OrigVarName = "$GoodGlobalVar";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(ParsedName, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size());
VarName = "@GoodPseudoVar";
VarName = OrigVarName = "@GoodPseudoVar";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(ParsedName, OrigVarName);
EXPECT_TRUE(VarName.empty());
EXPECT_TRUE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size());
VarName = "42BadVar";
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
VarName = "$@";
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
VarName = "B@dVar";
VarName = OrigVarName = "B@dVar";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(VarName, OrigVarName.substr(1));
EXPECT_EQ(ParsedName, "B");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, 1U);
VarName = "B$dVar";
VarName = OrigVarName = "B$dVar";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(VarName, OrigVarName.substr(1));
EXPECT_EQ(ParsedName, "B");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, 1U);
VarName = "BadVar+";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(VarName, "+");
EXPECT_EQ(ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size() - 1);
VarName = "BadVar-";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(VarName, "-");
EXPECT_EQ(ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size() - 1);
VarName = "BadVar:";
IsPseudo = true;
TrailIdx = 0;
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
EXPECT_EQ(VarName, ":");
EXPECT_EQ(ParsedName, "BadVar");
EXPECT_FALSE(IsPseudo);
EXPECT_EQ(TrailIdx, VarName.size() - 1);
}
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
@ -149,76 +149,175 @@ static StringRef bufferize(SourceMgr &SM, StringRef Str) {
return StrBufferRef;
}
class ExprTester {
class PatternTester {
private:
size_t LineNumber = 1;
SourceMgr SM;
FileCheckRequest Req;
FileCheckPatternContext Context;
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
FileCheckPattern P =
FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
public:
ExprTester() {
PatternTester() {
std::vector<std::string> GlobalDefines;
GlobalDefines.emplace_back(std::string("#FOO=42"));
GlobalDefines.emplace_back(std::string("BAR=BAZ"));
Context.defineCmdlineVariables(GlobalDefines, SM);
// Call ParsePattern to have @LINE defined.
P.ParsePattern("N/A", "CHECK", SM, 1, Req);
// Call parsePattern to have @LINE defined.
P.parsePattern("N/A", "CHECK", SM, Req);
// parsePattern does not expect to be called twice for the same line and
// will set FixedStr and RegExStr incorrectly if it is. Therefore prepare
// a pattern for a different line.
initNextPattern();
}
bool parseExpect(std::string &VarName, std::string &Trailer) {
bool IsPseudo = VarName[0] == '@';
std::string NameTrailer = VarName + Trailer;
StringRef NameTrailerRef = bufferize(SM, NameTrailer);
StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
return P.parseNumericSubstitution(VarNameRef, IsPseudo, TrailerRef, SM) ==
nullptr;
void initNextPattern() {
P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
}
bool parseNumVarDefExpect(StringRef Expr) {
StringRef ExprBufferRef = bufferize(SM, Expr);
StringRef Name;
return FileCheckPattern::parseNumericVariableDefinition(ExprBufferRef, Name,
&Context, SM);
}
bool parseSubstExpect(StringRef Expr) {
StringRef ExprBufferRef = bufferize(SM, Expr);
FileCheckNumericVariable *DefinedNumericVariable;
return P.parseNumericSubstitutionBlock(
ExprBufferRef, DefinedNumericVariable, SM) == nullptr;
}
bool parsePatternExpect(StringRef Pattern) {
StringRef PatBufferRef = bufferize(SM, Pattern);
return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
}
bool matchExpect(StringRef Buffer) {
StringRef BufferRef = bufferize(SM, Buffer);
size_t MatchLen;
return P.match(BufferRef, MatchLen, SM);
}
};
TEST_F(FileCheckTest, ParseExpr) {
ExprTester Tester;
TEST_F(FileCheckTest, ParseNumericVariableDefinition) {
PatternTester Tester;
// @LINE with offset.
std::string VarName = "@LINE";
std::string Trailer = "+3";
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
// Invalid definition of pseudo.
EXPECT_TRUE(Tester.parseNumVarDefExpect("@LINE"));
// @LINE only.
Trailer = "";
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
// Conflict with pattern variable.
EXPECT_TRUE(Tester.parseNumVarDefExpect("BAR"));
// Defined variable.
VarName = "FOO";
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
EXPECT_FALSE(Tester.parseNumVarDefExpect("FOO"));
}
// Undefined variable.
VarName = "UNDEF";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
TEST_F(FileCheckTest, ParseExpr) {
PatternTester Tester;
// Wrong Pseudovar.
VarName = "@FOO";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
// Variable definition.
// Definition of invalid variable.
EXPECT_TRUE(Tester.parseSubstExpect("10VAR:"));
EXPECT_TRUE(Tester.parseSubstExpect("@FOO:"));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE:"));
// Garbage after name of variable being defined.
EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:"));
// Variable defined to numeric expression.
EXPECT_TRUE(Tester.parseSubstExpect("VAR1: FOO"));
// Acceptable variable definition.
EXPECT_FALSE(Tester.parseSubstExpect("VAR1:"));
EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:"));
EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :"));
EXPECT_FALSE(Tester.parseSubstExpect("VAR3: "));
// Numeric expression.
// Unacceptable variable.
EXPECT_TRUE(Tester.parseSubstExpect("10VAR"));
EXPECT_TRUE(Tester.parseSubstExpect("@FOO"));
EXPECT_TRUE(Tester.parseSubstExpect("UNDEF"));
// Only valid variable.
EXPECT_FALSE(Tester.parseSubstExpect("@LINE"));
EXPECT_FALSE(Tester.parseSubstExpect("FOO"));
// Use variable defined on same line.
EXPECT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:]]"));
EXPECT_TRUE(Tester.parseSubstExpect("LINE1VAR"));
// Unsupported operator.
VarName = "@LINE";
Trailer = "/2";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));
// Missing offset operand.
VarName = "@LINE";
Trailer = "+";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));
// Cannot parse offset operand.
VarName = "@LINE";
Trailer = "+x";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x"));
// Unexpected string at end of numeric expression.
VarName = "@LINE";
Trailer = "+5x";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x"));
// Valid expression.
EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
}
TEST_F(FileCheckTest, ParsePattern) {
PatternTester Tester;
// Space in pattern variable expression.
EXPECT_TRUE(Tester.parsePatternExpect("[[ BAR]]"));
// Invalid variable name.
EXPECT_TRUE(Tester.parsePatternExpect("[[42INVALID]]"));
// Invalid pattern variable definition.
EXPECT_TRUE(Tester.parsePatternExpect("[[@PAT:]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[PAT+2:]]"));
// Collision with numeric variable.
EXPECT_TRUE(Tester.parsePatternExpect("[[FOO:]]"));
// Valid use of pattern variable.
EXPECT_FALSE(Tester.parsePatternExpect("[[BAR]]"));
// Valid pattern variable definition.
EXPECT_FALSE(Tester.parsePatternExpect("[[PAT:[0-9]+]]"));
// Invalid numeric expressions.
EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#2+@LINE]]"));
EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]"));
// Valid numeric expressions and numeric variable definition.
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]"));
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE+2]]"));
EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR:]]"));
}
TEST_F(FileCheckTest, Match) {
PatternTester Tester;
// Check matching a definition only matches a number.
Tester.parsePatternExpect("[[#NUMVAR:]]");
EXPECT_TRUE(Tester.matchExpect("FAIL"));
EXPECT_FALSE(Tester.matchExpect("18"));
// Check matching the variable defined matches the correct number only
Tester.initNextPattern();
Tester.parsePatternExpect("[[#NUMVAR]] [[#NUMVAR+2]]");
EXPECT_TRUE(Tester.matchExpect("19 21"));
EXPECT_TRUE(Tester.matchExpect("18 21"));
EXPECT_FALSE(Tester.matchExpect("18 20"));
}
TEST_F(FileCheckTest, Substitution) {
@ -236,7 +335,7 @@ TEST_F(FileCheckTest, Substitution) {
// Substitutions of defined pseudo and non-pseudo numeric variables return
// the right value.
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10);
FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
FileCheckNumericSubstitution SubstitutionLine =
@ -257,7 +356,7 @@ TEST_F(FileCheckTest, Substitution) {
EXPECT_FALSE(SubstitutionN.getResult());
// Substitution of a defined string variable returns the right value.
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
Value = StringSubstitution.getResult();
EXPECT_TRUE(Value);
@ -359,9 +458,10 @@ TEST_F(FileCheckTest, FileCheckContext) {
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
FileCheckNumExpr *NumExpr =
P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM);
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
FileCheckNumericVariable *DefinedNumericVariable;
FileCheckNumExpr *NumExpr = P.parseNumericSubstitutionBlock(
LocalNumVarRef, DefinedNumericVariable, SM);
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
EXPECT_TRUE(LocalVar);
@ -383,9 +483,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
// variable clearing due to --enable-var-scope happens after numeric
// expressions are linked to the numeric variables they use.
EXPECT_FALSE(NumExpr->eval());
P = FileCheckPattern(Check::CheckPlain, &Cxt);
NumExpr =
P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM);
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_FALSE(NumExpr);
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
EXPECT_FALSE(EmptyVar);
@ -400,9 +500,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
EXPECT_EQ(*GlobalVar, "BAR");
P = FileCheckPattern(Check::CheckPlain, &Cxt);
NumExpr =
P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_TRUE(NumExpr);
NumExprVal = NumExpr->eval();
EXPECT_TRUE(NumExprVal);
@ -412,9 +512,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
Cxt.clearLocalVars();
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
P = FileCheckPattern(Check::CheckPlain, &Cxt);
NumExpr =
P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
DefinedNumericVariable, SM);
EXPECT_TRUE(NumExpr);
NumExprVal = NumExpr->eval();
EXPECT_TRUE(NumExprVal);