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
|
:program:`FileCheck` also supports numeric substitution blocks that allow
|
||||||
checking for numeric values that satisfy a numeric expression constraint based
|
defining numeric variables and checking for numeric values that satisfy a
|
||||||
on numeric variables. This allows ``CHECK:`` directives to verify a numeric
|
numeric expression constraint based on those variables via a numeric
|
||||||
relation between two numbers, such as the need for consecutive registers to be
|
substitution. This allows ``CHECK:`` directives to verify a numeric relation
|
||||||
used.
|
between two numbers, such as the need for consecutive registers to be used.
|
||||||
|
|
||||||
The syntax of a numeric substitution block is ``[[#<NUMVAR><op><offset>]]``
|
The syntax to define a numeric variable is ``[[#<NUMVAR>:]]`` where
|
||||||
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
|
* ``<op>`` is an optional numeric operation to perform on the value of
|
||||||
``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
|
``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
|
||||||
|
@ -590,31 +601,35 @@ where:
|
||||||
|
|
||||||
Spaces are accepted before, after and between any of these elements.
|
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:
|
For example:
|
||||||
|
|
||||||
.. code-block:: llvm
|
.. 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
|
.. 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
|
.. code-block:: gas
|
||||||
|
|
||||||
add r5, r5, r7
|
load r5, [r0]
|
||||||
|
load r7, [r1]
|
||||||
|
|
||||||
due to ``7`` being unequal to ``5 + 1``.
|
due to ``7`` being unequal to ``5 + 1``.
|
||||||
|
|
||||||
The ``--enable-var-scope`` option has the same effect on numeric variables as
|
The ``--enable-var-scope`` option has the same effect on numeric variables as
|
||||||
on string variables.
|
on string variables.
|
||||||
|
|
||||||
|
Important note: In its current implementation, a numeric expression cannot use
|
||||||
|
a numeric variable defined on the same line.
|
||||||
|
|
||||||
FileCheck Pseudo Numeric Variables
|
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
|
line numbers in the same file, which have to be updated whenever line numbers
|
||||||
change due to text addition or deletion.
|
change due to text addition or deletion.
|
||||||
|
|
||||||
To support this case, FileCheck understands the ``@LINE`` pseudo numeric
|
To support this case, FileCheck numeric expressions understand the ``@LINE``
|
||||||
variable which evaluates to the line number of the CHECK pattern where it is
|
pseudo numeric variable which evaluates to the line number of the CHECK pattern
|
||||||
found.
|
where it is found.
|
||||||
|
|
||||||
This way match patterns can be put near the relevant test lines and include
|
This way match patterns can be put near the relevant test lines and include
|
||||||
relative line number references, for example:
|
relative line number references, for example:
|
||||||
|
|
|
@ -41,7 +41,9 @@ struct FileCheckRequest {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
/// Class representing a numeric variable with a given value in a numeric
|
/// 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 {
|
class FileCheckNumericVariable {
|
||||||
private:
|
private:
|
||||||
/// Name of the numeric variable.
|
/// Name of the numeric variable.
|
||||||
|
@ -50,11 +52,19 @@ private:
|
||||||
/// Value of numeric variable, if defined, or None otherwise.
|
/// Value of numeric variable, if defined, or None otherwise.
|
||||||
Optional<uint64_t> Value;
|
Optional<uint64_t> Value;
|
||||||
|
|
||||||
|
/// 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:
|
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
|
/// Constructor for numeric variable \p Name with a known \p Value at parse
|
||||||
/// time (e.g. the @LINE numeric variable).
|
/// time (e.g. the @LINE numeric variable).
|
||||||
FileCheckNumericVariable(StringRef Name, uint64_t Value)
|
FileCheckNumericVariable(StringRef Name, uint64_t Value)
|
||||||
: Name(Name), Value(Value) {}
|
: Name(Name), Value(Value), DefLineNumber(0) {}
|
||||||
|
|
||||||
/// \returns name of that numeric variable.
|
/// \returns name of that numeric variable.
|
||||||
StringRef getName() const { return Name; }
|
StringRef getName() const { return Name; }
|
||||||
|
@ -69,6 +79,9 @@ public:
|
||||||
/// Clears value of this numeric variable. \returns whether the variable was
|
/// Clears value of this numeric variable. \returns whether the variable was
|
||||||
/// already undefined.
|
/// already undefined.
|
||||||
bool clearValue();
|
bool clearValue();
|
||||||
|
|
||||||
|
/// \returns the line number where this variable is defined.
|
||||||
|
size_t getDefLineNumber() { return DefLineNumber; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Type of functions evaluating a given binary operation.
|
/// 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
|
/// When matching a given pattern, this holds the pointers to the classes
|
||||||
/// representing the last definitions of numeric variables defined in
|
/// representing the last definitions of numeric variables defined in
|
||||||
/// previous patterns. Earlier definition of the variables, if any, have
|
/// previous patterns. Earlier definitions of the variables, if any, have
|
||||||
/// their own class instance not referenced by this table.
|
/// 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;
|
StringMap<FileCheckNumericVariable *> GlobalNumericVariableTable;
|
||||||
|
|
||||||
/// Vector holding pointers to all parsed numeric expressions. Used to
|
/// 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
|
/// Makes a new numeric variable and registers it for destruction when the
|
||||||
/// context is destroyed.
|
/// 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
|
/// Makes a new string substitution and registers it for destruction when the
|
||||||
/// context is destroyed.
|
/// context is destroyed.
|
||||||
|
@ -325,17 +342,39 @@ class FileCheckPattern {
|
||||||
/// and the value of numeric expression "N+1" at offset 6.
|
/// and the value of numeric expression "N+1" at offset 6.
|
||||||
std::vector<FileCheckSubstitution *> Substitutions;
|
std::vector<FileCheckSubstitution *> Substitutions;
|
||||||
|
|
||||||
/// Maps names of string variables defined in a pattern to the parenthesized
|
/// Maps names of string variables defined in a pattern to the number of
|
||||||
/// capture numbers of their last definition.
|
/// their parenthesis group in RegExStr capturing their last definition.
|
||||||
///
|
///
|
||||||
/// E.g. for the pattern "foo[[bar:.*]]baz[[bar]]quux[[bar:.*]]",
|
/// E.g. for the pattern "foo[[bar:.*]]baz([[bar]][[QUUX]][[bar:.*]])",
|
||||||
/// VariableDefs will map "bar" to 2 corresponding to the second definition
|
/// RegExStr will be "foo(.*)baz(\1<quux value>(.*))" where <quux value> is
|
||||||
/// of "bar".
|
/// 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
|
/// Note: uses std::map rather than StringMap to be able to get the key when
|
||||||
/// iterating over values.
|
/// iterating over values.
|
||||||
std::map<StringRef, unsigned> VariableDefs;
|
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
|
/// Pointer to a class instance holding the global state shared by all
|
||||||
/// patterns:
|
/// patterns:
|
||||||
/// - separate tables with the values of live string and numeric variables
|
/// - separate tables with the values of live string and numeric variables
|
||||||
|
@ -346,13 +385,14 @@ class FileCheckPattern {
|
||||||
|
|
||||||
Check::FileCheckType CheckTy;
|
Check::FileCheckType CheckTy;
|
||||||
|
|
||||||
/// Contains the number of line this pattern is in.
|
/// Line number for this CHECK pattern. Used to determine whether a variable
|
||||||
unsigned LineNumber;
|
/// definition is made on an earlier line to the one with this CHECK.
|
||||||
|
size_t LineNumber;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileCheckPattern(Check::FileCheckType Ty,
|
FileCheckPattern(Check::FileCheckType Ty, FileCheckPatternContext *Context,
|
||||||
FileCheckPatternContext *Context)
|
size_t Line)
|
||||||
: Context(Context), CheckTy(Ty) {}
|
: Context(Context), CheckTy(Ty), LineNumber(Line) {}
|
||||||
|
|
||||||
/// \returns the location in source code.
|
/// \returns the location in source code.
|
||||||
SMLoc getLoc() const { return PatternLoc; }
|
SMLoc getLoc() const { return PatternLoc; }
|
||||||
|
@ -363,31 +403,37 @@ public:
|
||||||
|
|
||||||
/// \returns whether \p C is a valid first character for a variable name.
|
/// \returns whether \p C is a valid first character for a variable name.
|
||||||
static bool isValidVarNameStart(char C);
|
static bool isValidVarNameStart(char C);
|
||||||
/// Verifies that the string at the start of \p Str is a well formed
|
/// Parses the string at the start of \p Str for a variable name and \returns
|
||||||
/// variable. \returns false if it is and sets \p IsPseudo to indicate if it
|
/// whether the variable name is ill-formed. If parsing succeeded, sets
|
||||||
/// is a pseudo variable and \p TrailIdx to the position of the last
|
/// \p IsPseudo to indicate if it is a pseudo variable, sets \p Name to the
|
||||||
/// character that is part of the variable name. Otherwise, only
|
/// parsed variable name and strips \p Str from the variable name.
|
||||||
/// \returns true.
|
static bool parseVariable(StringRef &Str, StringRef &Name, bool &IsPseudo);
|
||||||
static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
|
/// Parses \p Expr for the definition of a numeric variable, returning an
|
||||||
/// Parses a numeric substitution involving (pseudo if \p IsPseudo is true)
|
/// error if \p Context already holds a string variable with the same name.
|
||||||
/// variable \p Name with the string corresponding to the operation being
|
/// \returns whether parsing fails, in which case errors are reported on
|
||||||
/// performed in \p Trailer. \returns the class representing the numeric
|
/// \p SM. Otherwise, sets \p Name to the name of the parsed numeric
|
||||||
/// expression being substituted or nullptr if parsing fails, in which case
|
/// variable.
|
||||||
/// errors are reported on \p SM.
|
static bool parseNumericVariableDefinition(StringRef &Expr, StringRef &Name,
|
||||||
FileCheckNumExpr *parseNumericSubstitution(StringRef Name, bool IsPseudo,
|
FileCheckPatternContext *Context,
|
||||||
StringRef Trailer,
|
const SourceMgr &SM);
|
||||||
const SourceMgr &SM) const;
|
/// 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
|
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
|
||||||
/// instance accordingly.
|
/// instance accordingly.
|
||||||
///
|
///
|
||||||
/// \p Prefix provides which prefix is being matched, \p Req describes the
|
/// \p Prefix provides which prefix is being matched, \p Req describes the
|
||||||
/// global options that influence the parsing such as whitespace
|
/// global options that influence the parsing such as whitespace
|
||||||
/// canonicalization, \p SM provides the SourceMgr used for error reports,
|
/// canonicalization, \p SM provides the SourceMgr used for error reports.
|
||||||
/// and \p LineNumber is the line number in the input file from which the
|
/// \returns true in case of an error, false otherwise.
|
||||||
/// pattern string was read. \returns true in case of an error, false
|
bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
|
||||||
/// otherwise.
|
const FileCheckRequest &Req);
|
||||||
bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
|
|
||||||
unsigned LineNumber, const FileCheckRequest &Req);
|
|
||||||
/// Matches the pattern string against the input buffer \p Buffer
|
/// Matches the pattern string against the input buffer \p Buffer
|
||||||
///
|
///
|
||||||
/// \returns the position that is matched or npos if there is no match. If
|
/// \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
|
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
|
||||||
/// instance provides the current values of FileCheck string variables and
|
/// instance provides the current values of FileCheck string variables and
|
||||||
/// is updated if this match defines new values.
|
/// is updated if this match defines new values. Likewise, the
|
||||||
size_t match(StringRef Buffer, size_t &MatchLen) const;
|
/// 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
|
/// Prints the value of successful substitutions or the name of the undefined
|
||||||
/// string or numeric variable preventing a successful substitution.
|
/// string or numeric variable preventing a successful substitution.
|
||||||
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
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
|
/// \returns the offset of the closing sequence within Str, or npos if it
|
||||||
/// was not found.
|
/// was not found.
|
||||||
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
|
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> 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.
|
// Variable is undefined.
|
||||||
if (!LeftOp)
|
if (!LeftOpValue)
|
||||||
return None;
|
return None;
|
||||||
return EvalBinop(*LeftOp, RightOp);
|
return EvalBinop(*LeftOpValue, RightOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringRef FileCheckNumExpr::getUndefVarName() const {
|
StringRef FileCheckNumExpr::getUndefVarName() const {
|
||||||
|
@ -84,8 +85,8 @@ bool FileCheckPattern::isValidVarNameStart(char C) {
|
||||||
return C == '_' || isalpha(C);
|
return C == '_' || isalpha(C);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
|
bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name,
|
||||||
unsigned &TrailIdx) {
|
bool &IsPseudo) {
|
||||||
if (Str.empty())
|
if (Str.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -107,7 +108,8 @@ bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
|
||||||
ParsedOneChar = true;
|
ParsedOneChar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrailIdx = I;
|
Name = Str.take_front(I);
|
||||||
|
Str = Str.substr(I);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,24 +124,52 @@ static char popFront(StringRef &S) {
|
||||||
return C;
|
return C;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
|
bool FileCheckPattern::parseNumericVariableDefinition(
|
||||||
return LeftOp + RightOp;
|
StringRef &Expr, StringRef &Name, FileCheckPatternContext *Context,
|
||||||
}
|
const SourceMgr &SM) {
|
||||||
static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
|
bool IsPseudo;
|
||||||
return LeftOp - RightOp;
|
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 *
|
FileCheckNumericVariable *
|
||||||
FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
|
FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
|
||||||
StringRef Trailer,
|
const SourceMgr &SM) const {
|
||||||
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")) {
|
if (IsPseudo && !Name.equals("@LINE")) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||||
"invalid pseudo numeric variable '" + Name + "'");
|
"invalid pseudo numeric variable '" + Name + "'");
|
||||||
return nullptr;
|
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
|
// 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
|
// CHECK pattern. For each definition, the pointer to the class instance of
|
||||||
// the corresponding numeric variable definition is stored in
|
// the corresponding numeric variable definition is stored in
|
||||||
|
@ -152,16 +182,40 @@ FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
|
||||||
return nullptr;
|
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
|
// Check if this is a supported operation and select a function to perform
|
||||||
// it.
|
// it.
|
||||||
Trailer = Trailer.ltrim(SpaceChars);
|
Expr = Expr.ltrim(SpaceChars);
|
||||||
if (Trailer.empty()) {
|
if (Expr.empty())
|
||||||
return Context->makeNumExpr(add, LeftOp, 0);
|
return Context->makeNumExpr(add, LeftOp, 0);
|
||||||
}
|
SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
|
||||||
SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
|
char Operator = popFront(Expr);
|
||||||
char Operator = popFront(Trailer);
|
|
||||||
binop_eval_t EvalBinop;
|
binop_eval_t EvalBinop;
|
||||||
switch (Operator) {
|
switch (Operator) {
|
||||||
case '+':
|
case '+':
|
||||||
|
@ -178,35 +232,76 @@ FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse right operand.
|
// Parse right operand.
|
||||||
Trailer = Trailer.ltrim(SpaceChars);
|
Expr = Expr.ltrim(SpaceChars);
|
||||||
if (Trailer.empty()) {
|
if (Expr.empty()) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||||
"missing operand in numeric expression");
|
"missing operand in numeric expression");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
uint64_t RightOp;
|
uint64_t RightOp;
|
||||||
if (Trailer.consumeInteger(10, RightOp)) {
|
if (Expr.consumeInteger(10, RightOp)) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||||
"invalid offset in numeric expression '" + Trailer + "'");
|
"invalid offset in numeric expression '" + Expr + "'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Trailer = Trailer.ltrim(SpaceChars);
|
Expr = Expr.ltrim(SpaceChars);
|
||||||
if (!Trailer.empty()) {
|
if (!Expr.empty()) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||||
"unexpected characters at end of numeric expression '" +
|
"unexpected characters at end of numeric expression '" +
|
||||||
Trailer + "'");
|
Expr + "'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
|
return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
FileCheckNumExpr *FileCheckPattern::parseNumericSubstitutionBlock(
|
||||||
SourceMgr &SM, unsigned LineNumber,
|
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) {
|
const FileCheckRequest &Req) {
|
||||||
bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
|
bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
|
||||||
|
|
||||||
this->LineNumber = LineNumber;
|
|
||||||
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
||||||
|
|
||||||
// Create fake @LINE pseudo variable definition.
|
// 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
|
// String and numeric substitution blocks. String substitution blocks come
|
||||||
// in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
|
// in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
|
||||||
// other regex) and assigns it to the string variable 'foo'. The latter
|
// other regex) and assigns it to the string variable 'foo'. The latter
|
||||||
// substitutes foo's value. Numeric substitution blocks start with a
|
// substitutes foo's value. Numeric substitution blocks work the same way
|
||||||
// '#' sign after the double brackets and only have the substitution form.
|
// as string ones, but start with a '#' sign after the double brackets.
|
||||||
// Both string and numeric variables must satisfy the regular expression
|
// Both string and numeric variable names must satisfy the regular
|
||||||
// "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
|
// expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch
|
||||||
// errors.
|
// some common errors.
|
||||||
if (PatternStr.startswith("[[")) {
|
if (PatternStr.startswith("[[")) {
|
||||||
StringRef UnparsedPatternStr = PatternStr.substr(2);
|
StringRef UnparsedPatternStr = PatternStr.substr(2);
|
||||||
// Find the closing bracket pair ending the match. End is going to be an
|
// 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.
|
// index of the first unparsed character.
|
||||||
PatternStr = UnparsedPatternStr.substr(End + 2);
|
PatternStr = UnparsedPatternStr.substr(End + 2);
|
||||||
|
|
||||||
size_t VarEndIdx = MatchStr.find(":");
|
bool IsDefinition = false;
|
||||||
if (IsNumBlock)
|
StringRef DefName;
|
||||||
MatchStr = MatchStr.ltrim(SpaceChars);
|
StringRef SubstStr;
|
||||||
else {
|
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");
|
size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
|
||||||
if (SpacePos != StringRef::npos) {
|
if (SpacePos != StringRef::npos) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
|
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
|
||||||
SourceMgr::DK_Error, "unexpected whitespace");
|
SourceMgr::DK_Error, "unexpected whitespace");
|
||||||
return true;
|
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;
|
bool IsPseudo;
|
||||||
unsigned TrailIdx;
|
StringRef Name;
|
||||||
if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
|
StringRef OrigMatchStr = MatchStr;
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
if (parseVariable(MatchStr, Name, IsPseudo)) {
|
||||||
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()),
|
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error, "invalid variable name");
|
||||||
"invalid name in string variable definition");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect collisions between string and numeric variables when the
|
IsDefinition = (VarEndIdx != StringRef::npos);
|
||||||
// former is created later than the latter.
|
if (IsDefinition) {
|
||||||
if (Context->GlobalNumericVariableTable.find(Name) !=
|
if ((IsPseudo || !MatchStr.consume_front(":"))) {
|
||||||
Context->GlobalNumericVariableTable.end()) {
|
SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
|
||||||
SM.PrintMessage(
|
SourceMgr::DK_Error,
|
||||||
SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
|
"invalid name in string variable definition");
|
||||||
"numeric variable with name '" + Name + "' already exists");
|
return true;
|
||||||
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)) {
|
// Parse numeric substitution block.
|
||||||
NumExpr = parseNumericSubstitution(Name, IsPseudo, Trailer, SM);
|
FileCheckNumExpr *NumExpr;
|
||||||
|
FileCheckNumericVariable *DefinedNumericVariable;
|
||||||
|
if (IsNumBlock) {
|
||||||
|
NumExpr =
|
||||||
|
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
|
||||||
if (NumExpr == nullptr)
|
if (NumExpr == nullptr)
|
||||||
return true;
|
return true;
|
||||||
IsNumBlock = true;
|
if (DefinedNumericVariable) {
|
||||||
|
IsDefinition = true;
|
||||||
|
DefName = DefinedNumericVariable->getName();
|
||||||
|
MatchRegexp = StringRef("[0-9]+");
|
||||||
|
} else
|
||||||
|
SubstStr = MatchStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle substitutions: [[foo]] and [[#<foo expr>]].
|
// Handle substitutions: [[foo]] and [[#<foo expr>]].
|
||||||
if (!IsVarDef) {
|
if (!IsDefinition) {
|
||||||
// Handle substitution of string variables that were defined earlier on
|
// Handle substitution of string variables that were defined earlier on
|
||||||
// the same line by emitting a backreference.
|
// the same line by emitting a backreference. Numeric expressions do
|
||||||
if (!IsNumBlock && VariableDefs.find(Name) != VariableDefs.end()) {
|
// not support substituting a numeric variable defined on the same
|
||||||
unsigned CaptureParen = VariableDefs[Name];
|
// line.
|
||||||
if (CaptureParen < 1 || CaptureParen > 9) {
|
if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
|
unsigned CaptureParenGroup = VariableDefs[SubstStr];
|
||||||
|
if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {
|
||||||
|
SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
"Can't back-reference more than 9 variables");
|
"Can't back-reference more than 9 variables");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
AddBackrefToRegEx(CaptureParen);
|
AddBackrefToRegEx(CaptureParenGroup);
|
||||||
} else {
|
} else {
|
||||||
// Handle substitution of string variables ([[<var>]]) defined in
|
// Handle substitution of string variables ([[<var>]]) defined in
|
||||||
// previous CHECK patterns, and substitution of numeric expressions.
|
// previous CHECK patterns, and substitution of numeric expressions.
|
||||||
FileCheckSubstitution *Substitution =
|
FileCheckSubstitution *Substitution =
|
||||||
IsNumBlock
|
IsNumBlock
|
||||||
? Context->makeNumericSubstitution(MatchStr, NumExpr,
|
? Context->makeNumericSubstitution(SubstStr, NumExpr,
|
||||||
SubstInsertIdx)
|
SubstInsertIdx)
|
||||||
: Context->makeStringSubstitution(MatchStr, SubstInsertIdx);
|
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
|
||||||
Substitutions.push_back(Substitution);
|
Substitutions.push_back(Substitution);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle variable definitions: [[foo:.*]].
|
// Handle variable definitions: [[<def>:(...)]] and
|
||||||
VariableDefs[Name] = CurParen;
|
// [[#(...)<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 += '(';
|
RegExStr += '(';
|
||||||
++CurParen;
|
++CurParen;
|
||||||
|
|
||||||
if (AddRegExToRegEx(Trailer, CurParen, SM))
|
if (AddRegExToRegEx(MatchRegexp, CurParen, SM))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
RegExStr += ')';
|
RegExStr += ')';
|
||||||
|
@ -444,7 +576,8 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
|
||||||
RegExStr += Backref;
|
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 this is the EOF pattern, match it immediately.
|
||||||
if (CheckTy == Check::CheckEOF) {
|
if (CheckTy == Check::CheckEOF) {
|
||||||
MatchLen = 0;
|
MatchLen = 0;
|
||||||
|
@ -501,6 +634,25 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
|
||||||
MatchInfo[VariableDef.second];
|
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
|
// 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
|
// the required preceding newline, which is consumed by the pattern in the
|
||||||
// case of CHECK-EMPTY but not CHECK-NEXT.
|
// case of CHECK-EMPTY but not CHECK-NEXT.
|
||||||
|
@ -643,10 +795,11 @@ FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
|
||||||
return NumExprs.back().get();
|
return NumExprs.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
FileCheckNumericVariable *
|
FileCheckNumericVariable *
|
||||||
FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
|
FileCheckPatternContext::makeNumericVariable(Types... args) {
|
||||||
NumericVariables.push_back(
|
NumericVariables.push_back(
|
||||||
llvm::make_unique<FileCheckNumericVariable>(Name, Value));
|
llvm::make_unique<FileCheckNumericVariable>(args...));
|
||||||
return NumericVariables.back().get();
|
return NumericVariables.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,9 +1094,9 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
|
||||||
SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
|
SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
|
||||||
|
|
||||||
ImplicitNegativeChecks.push_back(
|
ImplicitNegativeChecks.push_back(
|
||||||
FileCheckPattern(Check::CheckNot, &PatternContext));
|
FileCheckPattern(Check::CheckNot, &PatternContext, 0));
|
||||||
ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer,
|
ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
|
||||||
"IMPLICIT-CHECK", SM, 0, Req);
|
"IMPLICIT-CHECK", SM, Req);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks;
|
std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks;
|
||||||
|
@ -1004,8 +1157,8 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
|
||||||
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
|
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
|
||||||
|
|
||||||
// Parse the pattern.
|
// Parse the pattern.
|
||||||
FileCheckPattern P(CheckTy, &PatternContext);
|
FileCheckPattern P(CheckTy, &PatternContext, LineNumber);
|
||||||
if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req))
|
if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Verify that CHECK-LABEL lines do not define or use variables
|
// 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.
|
// prefix as a filler for the error message.
|
||||||
if (!DagNotMatches.empty()) {
|
if (!DagNotMatches.empty()) {
|
||||||
CheckStrings.emplace_back(
|
CheckStrings.emplace_back(
|
||||||
FileCheckPattern(Check::CheckEOF, &PatternContext),
|
FileCheckPattern(Check::CheckEOF, &PatternContext, LineNumber + 1),
|
||||||
*Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
|
*Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
|
||||||
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
|
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
|
||||||
}
|
}
|
||||||
|
@ -1223,7 +1376,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
|
||||||
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
|
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
|
||||||
size_t CurrentMatchLen;
|
size_t CurrentMatchLen;
|
||||||
// get a match at current start point
|
// 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)
|
if (i == 1)
|
||||||
FirstMatchPos = LastPos + MatchPos;
|
FirstMatchPos = LastPos + MatchPos;
|
||||||
|
|
||||||
|
@ -1344,7 +1497,7 @@ bool FileCheckString::CheckNot(
|
||||||
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
|
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
|
||||||
|
|
||||||
size_t MatchLen = 0;
|
size_t MatchLen = 0;
|
||||||
size_t Pos = Pat->match(Buffer, MatchLen);
|
size_t Pos = Pat->match(Buffer, MatchLen, SM);
|
||||||
|
|
||||||
if (Pos == StringRef::npos) {
|
if (Pos == StringRef::npos) {
|
||||||
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
|
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
|
||||||
|
@ -1404,7 +1557,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
||||||
// CHECK-DAG group.
|
// CHECK-DAG group.
|
||||||
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
|
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
|
||||||
StringRef MatchBuffer = Buffer.substr(MatchPos);
|
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
|
// With a group of CHECK-DAGs, a single mismatching means the match on
|
||||||
// that group of CHECK-DAGs fails immediately.
|
// that group of CHECK-DAGs fails immediately.
|
||||||
if (MatchPosBuf == StringRef::npos) {
|
if (MatchPosBuf == StringRef::npos) {
|
||||||
|
@ -1568,7 +1721,8 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||||
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
|
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
|
||||||
unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
|
unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
|
||||||
StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
|
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()),
|
SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
"Missing equal sign in global definition");
|
"Missing equal sign in global definition");
|
||||||
|
@ -1578,27 +1732,28 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||||
|
|
||||||
// Numeric variable definition.
|
// Numeric variable definition.
|
||||||
if (CmdlineDef[0] == '#') {
|
if (CmdlineDef[0] == '#') {
|
||||||
bool IsPseudo;
|
|
||||||
unsigned TrailIdx;
|
|
||||||
size_t EqIdx = CmdlineDef.find('=');
|
|
||||||
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
|
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
|
||||||
if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
|
StringRef VarName;
|
||||||
IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
|
SMLoc CmdlineNameLoc = SMLoc::getFromPointer(CmdlineName.data());
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
|
bool ParseError = FileCheckPattern::parseNumericVariableDefinition(
|
||||||
SourceMgr::DK_Error,
|
CmdlineName, VarName, this, SM);
|
||||||
"invalid name in numeric variable definition '" +
|
// Check that CmdlineName starts with a valid numeric variable to be
|
||||||
CmdlineName + "'");
|
// 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;
|
ErrorFound = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect collisions between string and numeric variables when the latter
|
// Detect collisions between string and numeric variables when the latter
|
||||||
// is created later than the former.
|
// is created later than the former.
|
||||||
if (DefinedVariableTable.find(CmdlineName) !=
|
if (DefinedVariableTable.find(VarName) != DefinedVariableTable.end()) {
|
||||||
DefinedVariableTable.end()) {
|
|
||||||
SM.PrintMessage(
|
SM.PrintMessage(
|
||||||
SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error,
|
SMLoc::getFromPointer(VarName.data()), SourceMgr::DK_Error,
|
||||||
"string variable with name '" + CmdlineName + "' already exists");
|
"string variable with name '" + VarName + "' already exists");
|
||||||
ErrorFound = true;
|
ErrorFound = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1613,21 +1768,25 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||||
ErrorFound = true;
|
ErrorFound = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
|
auto DefinedNumericVariable = makeNumericVariable(0, VarName);
|
||||||
|
DefinedNumericVariable->setValue(Val);
|
||||||
|
|
||||||
// Record this variable definition.
|
// Record this variable definition.
|
||||||
GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
|
GlobalNumericVariableTable[DefinedNumericVariable->getName()] =
|
||||||
|
DefinedNumericVariable;
|
||||||
} else {
|
} else {
|
||||||
// String variable definition.
|
// String variable definition.
|
||||||
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
|
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
|
||||||
StringRef Name = CmdlineNameVal.first;
|
StringRef CmdlineName = CmdlineNameVal.first;
|
||||||
|
StringRef OrigCmdlineName = CmdlineName;
|
||||||
|
StringRef Name;
|
||||||
bool IsPseudo;
|
bool IsPseudo;
|
||||||
unsigned TrailIdx;
|
if (FileCheckPattern::parseVariable(CmdlineName, Name, IsPseudo) ||
|
||||||
if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
|
IsPseudo || !CmdlineName.empty()) {
|
||||||
IsPseudo || TrailIdx != Name.size() || Name.empty()) {
|
SM.PrintMessage(SMLoc::getFromPointer(OrigCmdlineName.data()),
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
"invalid name in string variable definition '" + Name +
|
"invalid name in string variable definition '" +
|
||||||
"'");
|
OrigCmdlineName + "'");
|
||||||
ErrorFound = true;
|
ErrorFound = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1646,7 +1805,7 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||||
// Mark the string variable as defined to detect collisions between
|
// Mark the string variable as defined to detect collisions between
|
||||||
// string and numeric variables in DefineCmdlineVariables when the latter
|
// string and numeric variables in DefineCmdlineVariables when the latter
|
||||||
// is created later than the former. We cannot reuse GlobalVariableTable
|
// 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
|
// lose the ability to detect the use of an undefined variable in
|
||||||
// match().
|
// match().
|
||||||
DefinedVariableTable[Name] = true;
|
DefinedVariableTable[Name] = true;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
|
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
|
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: Global define #1: #10VALUE=10
|
||||||
NUMERRCLIFMT-NEXT: {{^ \^$}}
|
NUMERRCLIFMT-NEXT: {{^ \^$}}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ NUMERRCLIFMT-NEXT: {{^ \^$}}
|
||||||
RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
|
RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO
|
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: Global define #1: #@VALUE=10
|
||||||
NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
|
NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
|
||||||
RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
|
RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL
|
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: Global define #1: #VALUE+2=10
|
||||||
NUMERRCLITRAIL-NEXT: {{^ \^$}}
|
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.
|
; We use CHECK-NEXT directives to force a match on all lines with digits.
|
||||||
|
|
||||||
; Numeric expressions using variables defined on the command-line without
|
; Numeric variable definition without spaces.
|
||||||
; 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
|
USE NO SPC
|
||||||
11
|
11
|
||||||
12
|
12
|
||||||
10
|
10
|
||||||
CHECK-LABEL: USE NO SPC
|
11
|
||||||
|
11
|
||||||
|
11
|
||||||
|
CHECK-LABEL: USE
|
||||||
CHECK-NEXT: [[#VAR1]]
|
CHECK-NEXT: [[#VAR1]]
|
||||||
CHECK-NEXT: [[#VAR1+1]]
|
CHECK-NEXT: [[#VAR1+1]]
|
||||||
CHECK-NEXT: [[#VAR1-1]]
|
CHECK-NEXT: [[#VAR1-1]]
|
||||||
|
CHECK-NEXT: [[#VAR1a]]
|
||||||
|
CHECK-NEXT: [[#VAR1b]]
|
||||||
|
CHECK-NEXT: [[#VAR1c]]
|
||||||
|
|
||||||
; Numeric expressions using variables defined on the command-line in alternate
|
; Numeric expressions using variables defined on other lines with different
|
||||||
; spacing
|
; spacing.
|
||||||
USE ALT SPC
|
USE SPC
|
||||||
11
|
11
|
||||||
11
|
11
|
||||||
12
|
12
|
||||||
|
@ -26,7 +47,7 @@ USE ALT SPC
|
||||||
10
|
10
|
||||||
10
|
10
|
||||||
10
|
10
|
||||||
CHECK-LABEL: USE ALT SPC
|
CHECK-LABEL: USE SPC
|
||||||
CHECK-NEXT: [[# VAR1]]
|
CHECK-NEXT: [[# VAR1]]
|
||||||
CHECK-NEXT: [[# VAR1 ]]
|
CHECK-NEXT: [[# VAR1 ]]
|
||||||
CHECK-NEXT: [[# VAR1+1]]
|
CHECK-NEXT: [[# VAR1+1]]
|
||||||
|
@ -39,7 +60,7 @@ CHECK-NEXT: [[# VAR1 - 1]]
|
||||||
CHECK-NEXT: [[# VAR1 - 1 ]]
|
CHECK-NEXT: [[# VAR1 - 1 ]]
|
||||||
|
|
||||||
; Numeric expressions using variables defined on the command-line and an
|
; 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
|
; Note: 9223372036854775819 = 0x8000000000000000 + 11
|
||||||
; 9223372036854775808 = 0x8000000000000000
|
; 9223372036854775808 = 0x8000000000000000
|
||||||
USE UNSIGNED IMM
|
USE UNSIGNED IMM
|
||||||
|
@ -47,7 +68,7 @@ USE UNSIGNED IMM
|
||||||
CHECK-LABEL: USE UNSIGNED IMM
|
CHECK-LABEL: USE UNSIGNED IMM
|
||||||
CHECK-NEXT: [[#VAR1+9223372036854775808]]
|
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: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
|
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: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
|
||||||
UNDEF-USE-MSG-NEXT: {{^ \^$}}
|
UNDEF-USE-MSG-NEXT: {{^ \^$}}
|
||||||
|
|
||||||
; Numeric expression with unsupported operator
|
; Numeric expression with unsupported operator.
|
||||||
RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \
|
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
|
RUN: | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s
|
||||||
|
|
||||||
INVALID OPERATOR
|
INVALID OPERATOR
|
||||||
VAR1*2: 22
|
NUMVAR*2: 22
|
||||||
INVAL-OP-LABEL: INVALID OPERATOR
|
INVAL-OP-LABEL: INVALID OPERATOR
|
||||||
INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]]
|
INVAL-OP-NEXT: NUMVAR*2: [[#NUMVAR*2]]
|
||||||
INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: unsupported numeric operation '*'
|
INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:35: error: unsupported numeric operation '*'
|
||||||
INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}}
|
INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: NUMVAR*2: {{\[\[#NUMVAR\*2\]\]}}
|
||||||
INVAL-OP-MSG-NEXT: {{^ \^$}}
|
INVAL-OP-MSG-NEXT: {{^ \^$}}
|
||||||
|
|
||||||
; Name conflict between Numeric variable definition and string variable
|
; Name conflict between Numeric variable definition and string variable
|
||||||
; definition
|
; definition whether from the command-line or input text.
|
||||||
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \
|
RUN: not FileCheck --check-prefixes CONFLICT,CONFLICT1,CONFLICT2 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s
|
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-STR-CONFLICT %s
|
||||||
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
|
RUN: not FileCheck -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT2 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s
|
RUN: | FileCheck --strict-whitespace --check-prefix INPUT-STR-CONFLICT %s
|
||||||
RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
|
RUN: not FileCheck -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s
|
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
|
foobar
|
||||||
CONFLICT-LABEL: PATVAR NUMVAR CONFLICT
|
redef2 42
|
||||||
CONFLICT1-NEXT: [[NUMVAR:foo.*]]
|
CONFLICT-LABEL: STRVAR NUMVAR CONFLICT
|
||||||
CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists
|
CONFLICT1-NEXT: redef1 [[#NUMVAR:]]
|
||||||
CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}}
|
CONFLICT2: [[NUMVAR:foo.*]]
|
||||||
CLI-INPUT-PAT-CONFLICT-NEXT: {{^ \^$}}
|
CONFLICT3: [[STRVAR:foo.*]]
|
||||||
CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists
|
CONFLICT4: redef2 [[#STRVAR:]]
|
||||||
CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar
|
INPUT-STR-CONFLICT: numeric-expression.txt:[[#@LINE-3]]:14: error: numeric variable with name 'NUMVAR' already exists
|
||||||
CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}}
|
INPUT-STR-CONFLICT-NEXT: {{C}}ONFLICT2: {{\[\[NUMVAR:foo\.\*\]\]}}
|
||||||
CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: string variable with name 'PATVAR' already exists
|
INPUT-STR-CONFLICT-NEXT: {{^ \^$}}
|
||||||
CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42
|
CLI-STR-CONFLICT: Global defines:2:19: error: numeric variable with name 'NUMVAR' already exists
|
||||||
CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
|
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
|
; Test that variables not starting with dollar sign get undefined after a
|
||||||
; CHECK-LABEL directive iff --enable-var-scope is used.
|
; 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
|
; Reference run: variables remain defined at all time when not using
|
||||||
RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
|
; --enable-var-scope option.
|
||||||
RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
|
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: | 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: | 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
|
RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s
|
||||||
|
|
||||||
local1
|
local1
|
||||||
global1
|
global1
|
||||||
CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]]
|
CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM:]]
|
||||||
CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]]
|
CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM:]]
|
||||||
|
|
||||||
local2
|
local2
|
||||||
global2
|
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 -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 -v -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 -vv -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s
|
||||||
|
|
||||||
foo
|
foo
|
||||||
bar
|
bar
|
||||||
|
@ -29,36 +29,33 @@ VV-NEXT: verbose.txt:[[@LINE-22]]:1: note: scanning from here
|
||||||
VV-NEXT: {{^}}bar{{$}}
|
VV-NEXT: {{^}}bar{{$}}
|
||||||
VV-NEXT: {{^}}^{{$}}
|
VV-NEXT: {{^}}^{{$}}
|
||||||
|
|
||||||
NUMVAR:42
|
NUMVAR=42
|
||||||
NUMVAR - 1:41
|
NUMVAR - 1:41
|
||||||
CHECK: NUMVAR:[[#NUMVAR]]
|
CHECK: NUMVAR=[[#NUMVAR:]]
|
||||||
CHECK-NOT: [[#NUMVAR + 1]]
|
CHECK-NOT: [[#NUMVAR + 1]]
|
||||||
CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]]
|
CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]]
|
||||||
|
|
||||||
V: verbose.txt:[[#@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
|
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: {{^ \^$}}
|
||||||
V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
|
V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
|
||||||
V-NEXT: {{^}}NUMVAR:42{{$}}
|
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: {{^}}^~~~~~~~~{{$}}
|
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: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}}
|
||||||
V-NEXT: {{^ \^$}}
|
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: {{^}}NUMVAR - 1:41{{$}}
|
||||||
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
|
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: {{^}}NUMVAR - 1:41{{$}}
|
||||||
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
|
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: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}}
|
||||||
VV-NEXT: {{^ \^$}}
|
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: {{^}}NUMVAR - 1:41{{$}}
|
||||||
VV-NEXT: {{^}}^{{$}}
|
VV-NEXT: {{^}}^{{$}}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,16 @@ namespace {
|
||||||
class FileCheckTest : public ::testing::Test {};
|
class FileCheckTest : public ::testing::Test {};
|
||||||
|
|
||||||
TEST_F(FileCheckTest, NumericVariable) {
|
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());
|
EXPECT_EQ("FOO", FooVar.getName());
|
||||||
|
|
||||||
// Defined variable: getValue returns a value, setValue fails and value
|
|
||||||
// remains unchanged.
|
|
||||||
llvm::Optional<uint64_t> Value = FooVar.getValue();
|
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_TRUE(Value);
|
||||||
EXPECT_EQ(42U, *Value);
|
EXPECT_EQ(42U, *Value);
|
||||||
EXPECT_TRUE(FooVar.setValue(43));
|
EXPECT_TRUE(FooVar.setValue(43));
|
||||||
|
@ -33,12 +37,6 @@ TEST_F(FileCheckTest, NumericVariable) {
|
||||||
Value = FooVar.getValue();
|
Value = FooVar.getValue();
|
||||||
EXPECT_FALSE(Value);
|
EXPECT_FALSE(Value);
|
||||||
EXPECT_TRUE(FooVar.clearValue());
|
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; }
|
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
|
||||||
|
@ -78,67 +76,69 @@ TEST_F(FileCheckTest, ValidVarNameStart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FileCheckTest, ParseVar) {
|
TEST_F(FileCheckTest, ParseVar) {
|
||||||
StringRef VarName = "GoodVar42";
|
StringRef OrigVarName = "GoodVar42";
|
||||||
|
StringRef VarName = OrigVarName;
|
||||||
|
StringRef ParsedName;
|
||||||
bool IsPseudo = true;
|
bool IsPseudo = true;
|
||||||
unsigned TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(ParsedName, OrigVarName);
|
||||||
|
EXPECT_TRUE(VarName.empty());
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size());
|
|
||||||
|
|
||||||
VarName = "$GoodGlobalVar";
|
VarName = OrigVarName = "$GoodGlobalVar";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(ParsedName, OrigVarName);
|
||||||
|
EXPECT_TRUE(VarName.empty());
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size());
|
|
||||||
|
|
||||||
VarName = "@GoodPseudoVar";
|
VarName = OrigVarName = "@GoodPseudoVar";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(ParsedName, OrigVarName);
|
||||||
|
EXPECT_TRUE(VarName.empty());
|
||||||
EXPECT_TRUE(IsPseudo);
|
EXPECT_TRUE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size());
|
|
||||||
|
|
||||||
VarName = "42BadVar";
|
VarName = "42BadVar";
|
||||||
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
|
|
||||||
VarName = "$@";
|
VarName = "$@";
|
||||||
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
|
|
||||||
VarName = "B@dVar";
|
VarName = OrigVarName = "B@dVar";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||||
|
EXPECT_EQ(ParsedName, "B");
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, 1U);
|
|
||||||
|
|
||||||
VarName = "B$dVar";
|
VarName = OrigVarName = "B$dVar";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||||
|
EXPECT_EQ(ParsedName, "B");
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, 1U);
|
|
||||||
|
|
||||||
VarName = "BadVar+";
|
VarName = "BadVar+";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(VarName, "+");
|
||||||
|
EXPECT_EQ(ParsedName, "BadVar");
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size() - 1);
|
|
||||||
|
|
||||||
VarName = "BadVar-";
|
VarName = "BadVar-";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(VarName, "-");
|
||||||
|
EXPECT_EQ(ParsedName, "BadVar");
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size() - 1);
|
|
||||||
|
|
||||||
VarName = "BadVar:";
|
VarName = "BadVar:";
|
||||||
IsPseudo = true;
|
IsPseudo = true;
|
||||||
TrailIdx = 0;
|
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx));
|
EXPECT_EQ(VarName, ":");
|
||||||
|
EXPECT_EQ(ParsedName, "BadVar");
|
||||||
EXPECT_FALSE(IsPseudo);
|
EXPECT_FALSE(IsPseudo);
|
||||||
EXPECT_EQ(TrailIdx, VarName.size() - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||||
|
@ -149,76 +149,175 @@ static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||||
return StrBufferRef;
|
return StrBufferRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExprTester {
|
class PatternTester {
|
||||||
private:
|
private:
|
||||||
|
size_t LineNumber = 1;
|
||||||
SourceMgr SM;
|
SourceMgr SM;
|
||||||
FileCheckRequest Req;
|
FileCheckRequest Req;
|
||||||
FileCheckPatternContext Context;
|
FileCheckPatternContext Context;
|
||||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
|
FileCheckPattern P =
|
||||||
|
FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ExprTester() {
|
PatternTester() {
|
||||||
std::vector<std::string> GlobalDefines;
|
std::vector<std::string> GlobalDefines;
|
||||||
GlobalDefines.emplace_back(std::string("#FOO=42"));
|
GlobalDefines.emplace_back(std::string("#FOO=42"));
|
||||||
|
GlobalDefines.emplace_back(std::string("BAR=BAZ"));
|
||||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||||
// Call ParsePattern to have @LINE defined.
|
// Call parsePattern to have @LINE defined.
|
||||||
P.ParsePattern("N/A", "CHECK", SM, 1, Req);
|
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) {
|
void initNextPattern() {
|
||||||
bool IsPseudo = VarName[0] == '@';
|
P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
|
||||||
std::string NameTrailer = VarName + Trailer;
|
}
|
||||||
StringRef NameTrailerRef = bufferize(SM, NameTrailer);
|
|
||||||
StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
|
bool parseNumVarDefExpect(StringRef Expr) {
|
||||||
StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
|
StringRef ExprBufferRef = bufferize(SM, Expr);
|
||||||
return P.parseNumericSubstitution(VarNameRef, IsPseudo, TrailerRef, SM) ==
|
StringRef Name;
|
||||||
nullptr;
|
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) {
|
TEST_F(FileCheckTest, ParseNumericVariableDefinition) {
|
||||||
ExprTester Tester;
|
PatternTester Tester;
|
||||||
|
|
||||||
// @LINE with offset.
|
// Invalid definition of pseudo.
|
||||||
std::string VarName = "@LINE";
|
EXPECT_TRUE(Tester.parseNumVarDefExpect("@LINE"));
|
||||||
std::string Trailer = "+3";
|
|
||||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// @LINE only.
|
// Conflict with pattern variable.
|
||||||
Trailer = "";
|
EXPECT_TRUE(Tester.parseNumVarDefExpect("BAR"));
|
||||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// Defined variable.
|
// Defined variable.
|
||||||
VarName = "FOO";
|
EXPECT_FALSE(Tester.parseNumVarDefExpect("FOO"));
|
||||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
}
|
||||||
|
|
||||||
// Undefined variable.
|
TEST_F(FileCheckTest, ParseExpr) {
|
||||||
VarName = "UNDEF";
|
PatternTester Tester;
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// Wrong Pseudovar.
|
// Variable definition.
|
||||||
VarName = "@FOO";
|
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
// 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.
|
// Unsupported operator.
|
||||||
VarName = "@LINE";
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));
|
||||||
Trailer = "/2";
|
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// Missing offset operand.
|
// Missing offset operand.
|
||||||
VarName = "@LINE";
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));
|
||||||
Trailer = "+";
|
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// Cannot parse offset operand.
|
// Cannot parse offset operand.
|
||||||
VarName = "@LINE";
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x"));
|
||||||
Trailer = "+x";
|
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
|
||||||
|
|
||||||
// Unexpected string at end of numeric expression.
|
// Unexpected string at end of numeric expression.
|
||||||
VarName = "@LINE";
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x"));
|
||||||
Trailer = "+5x";
|
|
||||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
// 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) {
|
TEST_F(FileCheckTest, Substitution) {
|
||||||
|
@ -236,7 +335,7 @@ TEST_F(FileCheckTest, Substitution) {
|
||||||
// Substitutions of defined pseudo and non-pseudo numeric variables return
|
// Substitutions of defined pseudo and non-pseudo numeric variables return
|
||||||
// the right value.
|
// the right value.
|
||||||
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
|
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
|
||||||
FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
|
FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10);
|
||||||
FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
|
FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
|
||||||
FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
|
FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
|
||||||
FileCheckNumericSubstitution SubstitutionLine =
|
FileCheckNumericSubstitution SubstitutionLine =
|
||||||
|
@ -257,7 +356,7 @@ TEST_F(FileCheckTest, Substitution) {
|
||||||
EXPECT_FALSE(SubstitutionN.getResult());
|
EXPECT_FALSE(SubstitutionN.getResult());
|
||||||
|
|
||||||
// Substitution of a defined string variable returns the right value.
|
// 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);
|
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
|
||||||
Value = StringSubstitution.getResult();
|
Value = StringSubstitution.getResult();
|
||||||
EXPECT_TRUE(Value);
|
EXPECT_TRUE(Value);
|
||||||
|
@ -359,9 +458,10 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
||||||
StringRef EmptyVarStr = "EmptyVar";
|
StringRef EmptyVarStr = "EmptyVar";
|
||||||
StringRef UnknownVarStr = "UnknownVar";
|
StringRef UnknownVarStr = "UnknownVar";
|
||||||
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
||||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
|
||||||
FileCheckNumExpr *NumExpr =
|
FileCheckNumericVariable *DefinedNumericVariable;
|
||||||
P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM);
|
FileCheckNumExpr *NumExpr = P.parseNumericSubstitutionBlock(
|
||||||
|
LocalNumVarRef, DefinedNumericVariable, SM);
|
||||||
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||||
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
||||||
EXPECT_TRUE(LocalVar);
|
EXPECT_TRUE(LocalVar);
|
||||||
|
@ -383,9 +483,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
||||||
// variable clearing due to --enable-var-scope happens after numeric
|
// variable clearing due to --enable-var-scope happens after numeric
|
||||||
// expressions are linked to the numeric variables they use.
|
// expressions are linked to the numeric variables they use.
|
||||||
EXPECT_FALSE(NumExpr->eval());
|
EXPECT_FALSE(NumExpr->eval());
|
||||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
|
||||||
NumExpr =
|
NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef,
|
||||||
P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM);
|
DefinedNumericVariable, SM);
|
||||||
EXPECT_FALSE(NumExpr);
|
EXPECT_FALSE(NumExpr);
|
||||||
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||||
EXPECT_FALSE(EmptyVar);
|
EXPECT_FALSE(EmptyVar);
|
||||||
|
@ -400,9 +500,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
||||||
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||||
EXPECT_TRUE(GlobalVar);
|
EXPECT_TRUE(GlobalVar);
|
||||||
EXPECT_EQ(*GlobalVar, "BAR");
|
EXPECT_EQ(*GlobalVar, "BAR");
|
||||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
|
||||||
NumExpr =
|
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
|
||||||
P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
|
DefinedNumericVariable, SM);
|
||||||
EXPECT_TRUE(NumExpr);
|
EXPECT_TRUE(NumExpr);
|
||||||
NumExprVal = NumExpr->eval();
|
NumExprVal = NumExpr->eval();
|
||||||
EXPECT_TRUE(NumExprVal);
|
EXPECT_TRUE(NumExprVal);
|
||||||
|
@ -412,9 +512,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
||||||
Cxt.clearLocalVars();
|
Cxt.clearLocalVars();
|
||||||
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||||
EXPECT_TRUE(GlobalVar);
|
EXPECT_TRUE(GlobalVar);
|
||||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
|
||||||
NumExpr =
|
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
|
||||||
P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
|
DefinedNumericVariable, SM);
|
||||||
EXPECT_TRUE(NumExpr);
|
EXPECT_TRUE(NumExpr);
|
||||||
NumExprVal = NumExpr->eval();
|
NumExprVal = NumExpr->eval();
|
||||||
EXPECT_TRUE(NumExprVal);
|
EXPECT_TRUE(NumExprVal);
|
||||||
|
|
Loading…
Reference in New Issue