forked from OSchip/llvm-project
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:
parent
bf5bca5bea
commit
71d3f227a7
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
const SourceMgr &SM) const;
|
||||
/// 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;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
FileCheckNumExpr *
|
||||
FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
|
||||
StringRef Trailer,
|
||||
const SourceMgr &SM) const {
|
||||
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,92 +411,129 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
// index of the first unparsed character.
|
||||
PatternStr = UnparsedPatternStr.substr(End + 2);
|
||||
|
||||
size_t VarEndIdx = MatchStr.find(":");
|
||||
if (IsNumBlock)
|
||||
MatchStr = MatchStr.ltrim(SpaceChars);
|
||||
else {
|
||||
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(":");
|
||||
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.
|
||||
bool IsPseudo;
|
||||
unsigned TrailIdx;
|
||||
if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
|
||||
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(":")) {
|
||||
// Get the name (e.g. "foo") and verify it is well formed.
|
||||
bool IsPseudo;
|
||||
StringRef Name;
|
||||
StringRef OrigMatchStr = MatchStr;
|
||||
if (parseVariable(MatchStr, Name, IsPseudo)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid name in string variable definition");
|
||||
SourceMgr::DK_Error, "invalid variable name");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detect collisions between string and numeric variables when the
|
||||
// former is created later than the latter.
|
||||
if (Context->GlobalNumericVariableTable.find(Name) !=
|
||||
Context->GlobalNumericVariableTable.end()) {
|
||||
SM.PrintMessage(
|
||||
SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
|
||||
"numeric variable with name '" + Name + "' already exists");
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
// Detect collisions between string and numeric variables when the
|
||||
// former is created later than the latter.
|
||||
if (Context->GlobalNumericVariableTable.find(Name) !=
|
||||
Context->GlobalNumericVariableTable.end()) {
|
||||
SM.PrintMessage(
|
||||
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;
|
||||
|
|
|
@ -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: {{^ \^$}}
|
||||
|
||||
|
|
|
@ -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-MSG-NEXT: {{^ \^$}}
|
||||
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: {{^ \^$}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: {{^ \^$}}
|
||||
VV-NEXT: verbose.txt:[[#@LINE-25]]:1: note: scanning from here
|
||||
VV-NEXT: {{^}}NUMVAR - 1:41{{$}}
|
||||
VV-NEXT: {{^}}^{{$}}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue