forked from OSchip/llvm-project
Reinstate "FileCheck [5/12]: Introduce regular numeric variables"
This reinstates r360578 (gite47362c1ec
), reverted in r360653 (git004393681c
), with a fix for the list added in FileCheck.rst to build without error. 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/D60385 llvm-svn: 360665
This commit is contained in:
parent
2747ee2c83
commit
7b4ecdd3c2
|
@ -105,6 +105,11 @@ and from the command line.
|
|||
Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
|
||||
used in ``CHECK:`` lines.
|
||||
|
||||
.. option:: -D#<NUMVAR>=<VALUE>
|
||||
|
||||
Sets a filecheck numeric variable ``NUMVAR`` to ``<VALUE>`` that can be used
|
||||
in ``CHECK:`` lines.
|
||||
|
||||
.. option:: -version
|
||||
|
||||
Show the version number of this program.
|
||||
|
@ -560,8 +565,51 @@ CHECK-LABEL block. Global variables are not affected by CHECK-LABEL.
|
|||
This makes it easier to ensure that individual tests are not affected
|
||||
by variables set in preceding tests.
|
||||
|
||||
FileCheck Numeric Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
FileCheck Numeric Variables and Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:program:`FileCheck` also allows checking for numeric values that satisfy a
|
||||
numeric expression constraint based on numeric variables. This allows
|
||||
``CHECK:`` directives to verify a numeric relation between two numbers, such as
|
||||
the need for consecutive registers to be used.
|
||||
|
||||
The syntax to check a numeric expression constraint is
|
||||
``[[#<NUMVAR><op><offset>]]`` where:
|
||||
|
||||
* ``<NUMVAR>`` is the name of a numeric variable defined on the command line.
|
||||
|
||||
* ``<op>`` is an optional numeric operation to perform on the value of
|
||||
``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
|
||||
|
||||
* ``<offset>`` is the immediate value that constitutes the second operand of
|
||||
the numeric operation <op>. It must be present if ``<op>`` is present,
|
||||
absent otherwise.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]]
|
||||
|
||||
The above example would match the line:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
add r5, r5, r6
|
||||
|
||||
but would not match the line:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
add r5, r5, r7
|
||||
|
||||
due to ``7`` being unequal to ``5 + 1``.
|
||||
|
||||
The ``--enable-var-scope`` option has the same effect on numeric variables as
|
||||
on pattern variables.
|
||||
|
||||
FileCheck Pseudo Numeric Variables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes there's a need to verify output that contains line numbers of the
|
||||
match file, e.g. when testing compiler diagnostics. This introduces a certain
|
||||
|
@ -569,11 +617,9 @@ fragility of the match file structure, as "``CHECK:``" lines contain absolute
|
|||
line numbers in the same file, which have to be updated whenever line numbers
|
||||
change due to text addition or deletion.
|
||||
|
||||
To support this case, FileCheck allows using ``[[#@LINE]]``,
|
||||
``[[#@LINE+<offset>]]`` and ``[[#@LINE-<offset>]]`` numeric expressions in
|
||||
patterns, with an arbitrary number of spaces between each element of the
|
||||
expression. These expressions expand to the number of the line where a pattern
|
||||
is located (with an optional integer offset).
|
||||
To support this case, FileCheck understands the ``@LINE`` pseudo numeric
|
||||
variable which evaluates to the line number of the CHECK pattern where it is
|
||||
found.
|
||||
|
||||
This way match patterns can be put near the relevant test lines and include
|
||||
relative line number references, for example:
|
||||
|
|
|
@ -40,20 +40,67 @@ struct FileCheckRequest {
|
|||
// Numeric expression handling code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Class representing a numeric expression.
|
||||
class FileCheckNumExpr {
|
||||
/// Class representing a numeric variable with a given value in a numeric
|
||||
/// expression.
|
||||
class FileCheckNumericVariable {
|
||||
private:
|
||||
/// Value of the numeric expression.
|
||||
uint64_t Value;
|
||||
/// Name of the numeric variable.
|
||||
StringRef Name;
|
||||
|
||||
/// Value of numeric variable, if defined, or None otherwise.
|
||||
llvm::Optional<uint64_t> Value;
|
||||
|
||||
public:
|
||||
/// Constructor for a numeric expression with a known value at parse time,
|
||||
/// e.g. the implicit numeric expression defining the @LINE numeric pseudo
|
||||
/// variable.
|
||||
explicit FileCheckNumExpr(uint64_t Value) : Value(Value) {}
|
||||
/// Constructor for numeric variable \p Name with a known \p Value at parse
|
||||
/// time (e.g. the @LINE numeric variable).
|
||||
FileCheckNumericVariable(StringRef Name, uint64_t Value)
|
||||
: Name(Name), Value(Value) {}
|
||||
|
||||
/// \returns the value being matched against.
|
||||
uint64_t getValue() const { return Value; }
|
||||
/// \returns name of that numeric variable.
|
||||
StringRef getName() const { return Name; }
|
||||
|
||||
/// \returns value of this numeric variable.
|
||||
llvm::Optional<uint64_t> getValue() const { return Value; }
|
||||
|
||||
/// Sets value of this numeric variable if not defined. \returns whether the
|
||||
/// variable was already defined.
|
||||
bool setValue(uint64_t Value);
|
||||
|
||||
/// Clears value of this numeric variable. \returns whether the variable was
|
||||
/// already undefined.
|
||||
bool clearValue();
|
||||
};
|
||||
|
||||
/// Type of functions evaluating a given binary operation.
|
||||
using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
|
||||
|
||||
/// Class representing a numeric expression consisting of either a single
|
||||
/// numeric variable or a binary operation between a numeric variable and an
|
||||
/// immediate.
|
||||
class FileCheckNumExpr {
|
||||
private:
|
||||
/// Left operand.
|
||||
FileCheckNumericVariable *LeftOp;
|
||||
|
||||
/// Right operand.
|
||||
uint64_t RightOp;
|
||||
|
||||
/// Pointer to function that can evaluate this binary operation.
|
||||
binop_eval_t EvalBinop;
|
||||
|
||||
public:
|
||||
FileCheckNumExpr(binop_eval_t EvalBinop,
|
||||
FileCheckNumericVariable *OperandLeft, uint64_t OperandRight)
|
||||
: LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {}
|
||||
|
||||
/// Evaluates the value of this numeric expression, using EvalBinop to
|
||||
/// perform the binary operation it consists of. \returns None if the numeric
|
||||
/// variable used is undefined, or the expression value otherwise.
|
||||
llvm::Optional<uint64_t> eval() const;
|
||||
|
||||
/// \returns the name of the undefined variable used in this expression if
|
||||
/// any or an empty string otherwise.
|
||||
StringRef getUndefVarName() const;
|
||||
};
|
||||
|
||||
class FileCheckPatternContext;
|
||||
|
@ -105,9 +152,9 @@ public:
|
|||
size_t getIndex() const { return InsertIdx; }
|
||||
|
||||
/// \returns the result of the substitution represented by this class
|
||||
/// instance or None if substitution failed. For a numeric expression we
|
||||
/// substitute it by its value. For a pattern variable we simply replace it
|
||||
/// by the text its definition matched.
|
||||
/// instance or None if substitution failed. Numeric expressions are
|
||||
/// substituted by their values. Pattern variables are simply replaced by the
|
||||
/// text their definition matched.
|
||||
llvm::Optional<std::string> getResult() const;
|
||||
|
||||
/// \returns the name of the undefined variable used in this substitution, if
|
||||
|
@ -175,19 +222,35 @@ private:
|
|||
/// Back-references are used for uses after any the other definition.
|
||||
StringMap<StringRef> GlobalVariableTable;
|
||||
|
||||
/// Map of all pattern variables defined so far. Used at parse time to detect
|
||||
/// a name conflict between a numeric variable and a pattern variable when
|
||||
/// the former is defined on a later line than the latter.
|
||||
StringMap<bool> DefinedVariableTable;
|
||||
|
||||
/// When matching a given pattern, this holds the pointers to the classes
|
||||
/// representing the last definitions of numeric variables defined in
|
||||
/// previous patterns. Earlier definition of the variables, if any, have
|
||||
/// their own class instance not referenced by this table.
|
||||
StringMap<FileCheckNumericVariable *> GlobalNumericVariableTable;
|
||||
|
||||
/// Vector holding pointers to all parsed numeric expressions. Used to
|
||||
/// automatically free the numeric expressions once they are guaranteed to no
|
||||
/// longer be used.
|
||||
std::vector<std::unique_ptr<FileCheckNumExpr>> NumExprs;
|
||||
|
||||
/// Vector holding pointers to all parsed numeric variables. Used to
|
||||
/// automatically free them once they are guaranteed to no longer be used.
|
||||
std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
|
||||
|
||||
public:
|
||||
/// \returns the value of pattern variable \p VarName or None if no such
|
||||
/// variable has been defined.
|
||||
llvm::Optional<StringRef> getPatternVarValue(StringRef VarName);
|
||||
|
||||
/// Defines pattern variables from definitions given on the command line,
|
||||
/// passed as a vector of VAR=VAL strings in \p CmdlineDefines. Reports any
|
||||
/// error to \p SM and \returns whether an error occured.
|
||||
/// Defines pattern and numeric variables from definitions given on the
|
||||
/// command line, passed as a vector of [#]VAR=VAL strings in
|
||||
/// \p CmdlineDefines. Reports any error to \p SM and \returns whether an
|
||||
/// error occured.
|
||||
bool defineCmdlineVariables(std::vector<std::string> &CmdlineDefines,
|
||||
SourceMgr &SM);
|
||||
|
||||
|
@ -198,7 +261,13 @@ public:
|
|||
private:
|
||||
/// Makes a new numeric expression instance and registers it for destruction
|
||||
/// when the context is destroyed.
|
||||
template <class... Types> FileCheckNumExpr *makeNumExpr(Types... Args);
|
||||
FileCheckNumExpr *makeNumExpr(binop_eval_t EvalBinop,
|
||||
FileCheckNumericVariable *OperandLeft,
|
||||
uint64_t OperandRight);
|
||||
|
||||
/// Makes a new numeric variable and registers it for destruction when the
|
||||
/// context is destroyed.
|
||||
FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value);
|
||||
};
|
||||
|
||||
class FileCheckPattern {
|
||||
|
@ -214,12 +283,12 @@ class FileCheckPattern {
|
|||
|
||||
/// Entries in this vector represent uses of a pattern variable or a numeric
|
||||
/// expression in the pattern that need to be substituted in the regexp
|
||||
/// pattern at match time, e.g. "foo[[bar]]baz[[#@LINE+1]]". In this case,
|
||||
/// the RegExStr will contain "foobaz" and we'll get two entries in this
|
||||
/// vector that tells us to insert the value of pattern variable "bar" at
|
||||
/// offset 3 and the value of numeric expression "@LINE+1" at offset 6. Uses
|
||||
/// are represented by a FileCheckPatternSubstitution class to abstract
|
||||
/// whether it is a pattern variable or a numeric expression.
|
||||
/// pattern at match time, e.g. "foo[[bar]]baz[[#N+1]]". In this case, the
|
||||
/// RegExStr will contain "foobaz" and we'll get two entries in this vector
|
||||
/// that tells us to insert the value of pattern variable "bar" at offset 3
|
||||
/// and the value of numeric expression "N+1" at offset 6. Uses are
|
||||
/// represented by a FileCheckPatternSubstitution class to abstract whether
|
||||
/// it is a pattern variable or a numeric expression.
|
||||
std::vector<FileCheckPatternSubstitution> Substitutions;
|
||||
|
||||
/// Maps names of pattern variables defined in a pattern to the parenthesized
|
||||
|
@ -233,8 +302,12 @@ class FileCheckPattern {
|
|||
/// iterating over values.
|
||||
std::map<StringRef, unsigned> VariableDefs;
|
||||
|
||||
/// Pointer to the class instance shared by all patterns holding a table with
|
||||
/// the values of live variables at the start of any given CHECK line.
|
||||
/// Pointer to a class instance holding the global state shared by all
|
||||
/// patterns:
|
||||
/// - separate tables with the values of live pattern and numeric variables
|
||||
/// respectively at the start of any given CHECK line;
|
||||
/// - table holding whether a pattern variable has been defined at any given
|
||||
/// point during the parsing phase.
|
||||
FileCheckPatternContext *Context;
|
||||
|
||||
Check::FileCheckType CheckTy;
|
||||
|
@ -262,11 +335,13 @@ public:
|
|||
/// character that is part of the variable name. Otherwise, only
|
||||
/// \returns true.
|
||||
static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
|
||||
/// Parses a numeric expression involving pseudo variable \p Name with the
|
||||
/// string corresponding to the operation being performed in \p Trailer.
|
||||
/// \returns the class representing the numeric expression or nullptr if
|
||||
/// parsing fails in which case errors are reported on \p SM.
|
||||
FileCheckNumExpr *parseNumericExpression(StringRef Name, StringRef Trailer,
|
||||
/// Parses a numeric expression involving (pseudo if \p IsPseudo is true)
|
||||
/// variable \p Name with the string corresponding to the operation being
|
||||
/// performed in \p Trailer. \returns the class representing the numeric
|
||||
/// expression or nullptr if parsing fails in which case errors are reported
|
||||
/// on \p SM.
|
||||
FileCheckNumExpr *parseNumericExpression(StringRef Name, bool IsPseudo,
|
||||
StringRef Trailer,
|
||||
const SourceMgr &SM) const;
|
||||
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
|
||||
/// instance accordingly.
|
||||
|
|
|
@ -24,24 +24,54 @@
|
|||
|
||||
using namespace llvm;
|
||||
|
||||
bool FileCheckNumericVariable::setValue(uint64_t NewValue) {
|
||||
if (Value)
|
||||
return true;
|
||||
Value = NewValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileCheckNumericVariable::clearValue() {
|
||||
if (!Value)
|
||||
return true;
|
||||
Value = llvm::None;
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm::Optional<uint64_t> FileCheckNumExpr::eval() const {
|
||||
llvm::Optional<uint64_t> LeftOp = this->LeftOp->getValue();
|
||||
// Variable is undefined.
|
||||
if (!LeftOp)
|
||||
return llvm::None;
|
||||
return EvalBinop(*LeftOp, RightOp);
|
||||
}
|
||||
|
||||
StringRef FileCheckNumExpr::getUndefVarName() const {
|
||||
if (!LeftOp->getValue())
|
||||
return LeftOp->getName();
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
llvm::Optional<std::string> FileCheckPatternSubstitution::getResult() const {
|
||||
if (IsNumExpr) {
|
||||
return utostr(NumExpr->getValue());
|
||||
} else {
|
||||
// Look up the value and escape it so that we can put it into the
|
||||
// regex.
|
||||
llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
|
||||
if (!VarVal)
|
||||
llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval();
|
||||
if (!EvaluatedValue)
|
||||
return llvm::None;
|
||||
return Regex::escape(*VarVal);
|
||||
return utostr(*EvaluatedValue);
|
||||
}
|
||||
|
||||
// Look up the value and escape it so that we can put it into the regex.
|
||||
llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
|
||||
if (!VarVal)
|
||||
return llvm::None;
|
||||
return Regex::escape(*VarVal);
|
||||
}
|
||||
|
||||
StringRef FileCheckPatternSubstitution::getUndefVarName() const {
|
||||
// Parsing guarantees only @LINE is ever referenced and it is not undefined
|
||||
// by ClearLocalVars.
|
||||
if (IsNumExpr)
|
||||
return StringRef();
|
||||
// Although a use of an undefined numeric variable is detected at parse
|
||||
// time, a numeric variable can be undefined later by ClearLocalVariables.
|
||||
return NumExpr->getUndefVarName();
|
||||
|
||||
if (!Context->getPatternVarValue(FromStr))
|
||||
return FromStr;
|
||||
|
@ -91,31 +121,70 @@ static char popFront(StringRef &S) {
|
|||
return C;
|
||||
}
|
||||
|
||||
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
|
||||
return LeftOp + RightOp;
|
||||
}
|
||||
static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
|
||||
return LeftOp - RightOp;
|
||||
}
|
||||
|
||||
FileCheckNumExpr *
|
||||
FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer,
|
||||
FileCheckPattern::parseNumericExpression(StringRef Name, bool IsPseudo,
|
||||
StringRef Trailer,
|
||||
const SourceMgr &SM) const {
|
||||
if (!Name.equals("@LINE")) {
|
||||
if (IsPseudo && !Name.equals("@LINE")) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"invalid pseudo numeric variable '" + Name + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if this is a supported operation and select function to perform it.
|
||||
// This method is indirectly called from ParsePattern for all numeric
|
||||
// variable definitions and uses in the order in which they appear in the
|
||||
// CHECK pattern. For each definition, the pointer to the class instance of
|
||||
// the corresponding numeric variable definition is stored in
|
||||
// GlobalNumericVariableTable. Therefore, the pointer we get below is for the
|
||||
// class instance corresponding to the last definition of this variable use.
|
||||
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
|
||||
if (VarTableIter == Context->GlobalNumericVariableTable.end()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"using undefined numeric variable '" + Name + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileCheckNumericVariable *LeftOp = VarTableIter->second;
|
||||
|
||||
// Check if this is a supported operation and select a function to perform
|
||||
// it.
|
||||
Trailer = Trailer.ltrim(SpaceChars);
|
||||
if (Trailer.empty())
|
||||
return Context->makeNumExpr(LineNumber);
|
||||
if (Trailer.empty()) {
|
||||
return Context->makeNumExpr(add, LeftOp, 0);
|
||||
}
|
||||
SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
|
||||
char Operator = popFront(Trailer);
|
||||
binop_eval_t EvalBinop;
|
||||
switch (Operator) {
|
||||
case '+':
|
||||
EvalBinop = add;
|
||||
break;
|
||||
case '-':
|
||||
EvalBinop = sub;
|
||||
break;
|
||||
default:
|
||||
SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
|
||||
Twine("unsupported numeric operation '") + Twine(Operator) +
|
||||
"'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse right operand.
|
||||
Trailer = Trailer.ltrim(SpaceChars);
|
||||
if (Trailer.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
||||
"missing operand in numeric expression '" + Trailer + "'");
|
||||
"missing operand in numeric expression");
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t Offset;
|
||||
if (Trailer.consumeInteger(10, Offset)) {
|
||||
uint64_t RightOp;
|
||||
if (Trailer.consumeInteger(10, RightOp)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
||||
"invalid offset in numeric expression '" + Trailer + "'");
|
||||
return nullptr;
|
||||
|
@ -128,31 +197,24 @@ FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t Value;
|
||||
switch (Operator) {
|
||||
case '+':
|
||||
Value = LineNumber + Offset;
|
||||
break;
|
||||
case '-':
|
||||
Value = LineNumber - Offset;
|
||||
break;
|
||||
default:
|
||||
SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
|
||||
Twine("unsupported numeric operation '") + Twine(Operator) +
|
||||
"'");
|
||||
return nullptr;
|
||||
}
|
||||
return Context->makeNumExpr(Value);
|
||||
return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
|
||||
}
|
||||
|
||||
bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
||||
SourceMgr &SM, unsigned LineNumber,
|
||||
const FileCheckRequest &Req) {
|
||||
SourceMgr &SM, unsigned LineNumber,
|
||||
const FileCheckRequest &Req) {
|
||||
bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
|
||||
|
||||
this->LineNumber = LineNumber;
|
||||
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
||||
|
||||
// Create fake @LINE pseudo variable definition.
|
||||
StringRef LinePseudo = "@LINE";
|
||||
uint64_t LineNumber64 = LineNumber;
|
||||
FileCheckNumericVariable *LinePseudoVar =
|
||||
Context->makeNumericVariable(LinePseudo, LineNumber64);
|
||||
Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar;
|
||||
|
||||
if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
|
||||
// Ignore trailing whitespace.
|
||||
while (!PatternStr.empty() &&
|
||||
|
@ -230,10 +292,10 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
// forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other
|
||||
// regex) and assigns it to the FileCheck variable 'foo'. The latter
|
||||
// substitutes foo's value. Numeric expressions start with a '#' sign after
|
||||
// the double brackets and only have the substitution form. Pattern
|
||||
// variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*"
|
||||
// to be valid, as this helps catch some common errors. Numeric expressions
|
||||
// only support the @LINE pseudo numeric variable.
|
||||
// the double brackets and only have the substitution form. Both pattern
|
||||
// and numeric variables must satisfy the regular expression
|
||||
// "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
|
||||
// errors.
|
||||
if (PatternStr.startswith("[[")) {
|
||||
StringRef UnparsedPatternStr = PatternStr.substr(2);
|
||||
// Find the closing bracket pair ending the match. End is going to be an
|
||||
|
@ -283,21 +345,33 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
StringRef Trailer = MatchStr.substr(TrailIdx);
|
||||
bool IsVarDef = (VarEndIdx != StringRef::npos);
|
||||
|
||||
if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid name in pattern variable definition");
|
||||
return true;
|
||||
if (IsVarDef) {
|
||||
if (IsPseudo || !Trailer.consume_front(":")) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid name in pattern variable definition");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detect collisions between pattern and numeric variables when the
|
||||
// former is created later than the latter.
|
||||
if (Context->GlobalNumericVariableTable.find(Name) !=
|
||||
Context->GlobalNumericVariableTable.end()) {
|
||||
SM.PrintMessage(
|
||||
SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
|
||||
"numeric variable with name '" + Name + "' already exists");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsVarDef && IsPseudo) {
|
||||
NumExpr = parseNumericExpression(Name, Trailer, SM);
|
||||
if (IsNumExpr || (!IsVarDef && IsPseudo)) {
|
||||
NumExpr = parseNumericExpression(Name, IsPseudo, Trailer, SM);
|
||||
if (NumExpr == nullptr)
|
||||
return true;
|
||||
IsNumExpr = true;
|
||||
}
|
||||
|
||||
// Handle [[foo]].
|
||||
// Handle variable use: [[foo]] and [[#<foo expr>]].
|
||||
if (!IsVarDef) {
|
||||
// Handle use of pattern variables that were defined earlier on the
|
||||
// same line by emitting a backreference.
|
||||
|
@ -566,12 +640,22 @@ FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
|
|||
return VarIter->second;
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) {
|
||||
NumExprs.emplace_back(new FileCheckNumExpr(Args...));
|
||||
FileCheckNumExpr *
|
||||
FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
|
||||
FileCheckNumericVariable *OperandLeft,
|
||||
uint64_t OperandRight) {
|
||||
NumExprs.push_back(llvm::make_unique<FileCheckNumExpr>(EvalBinop, OperandLeft,
|
||||
OperandRight));
|
||||
return NumExprs.back().get();
|
||||
}
|
||||
|
||||
FileCheckNumericVariable *
|
||||
FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
|
||||
NumericVariables.push_back(
|
||||
llvm::make_unique<FileCheckNumericVariable>(Name, Value));
|
||||
return NumericVariables.back().get();
|
||||
}
|
||||
|
||||
size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
|
||||
// Offset keeps track of the current offset within the input Str
|
||||
size_t Offset = 0;
|
||||
|
@ -1445,7 +1529,7 @@ Regex llvm::FileCheck::buildCheckPrefixRegex() {
|
|||
|
||||
bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
|
||||
assert(GlobalVariableTable.empty() &&
|
||||
assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
|
||||
"Overriding defined variable with command-line variable definitions");
|
||||
|
||||
if (CmdlineDefines.empty())
|
||||
|
@ -1463,6 +1547,9 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
|||
CmdlineDefsDiag +=
|
||||
(Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
|
||||
|
||||
// Create a buffer with fake command line content in order to display
|
||||
// parsing diagnostic with location information and point to the
|
||||
// global definition with invalid syntax.
|
||||
std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
|
||||
MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
|
||||
StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
|
||||
|
@ -1472,27 +1559,91 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
|||
CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
|
||||
false /*KeepEmpty*/);
|
||||
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
|
||||
unsigned NameStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
|
||||
if (CmdlineDefDiag.substr(NameStart).find('=') == StringRef::npos) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefDiag.data()),
|
||||
unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
|
||||
StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
|
||||
if (CmdlineDef.find('=') == StringRef::npos) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"Missing equal sign in global definition");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
std::pair<StringRef, StringRef> CmdlineNameVal =
|
||||
CmdlineDefDiag.substr(NameStart).split('=');
|
||||
StringRef Name = CmdlineNameVal.first;
|
||||
bool IsPseudo;
|
||||
unsigned TrailIdx;
|
||||
if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || IsPseudo ||
|
||||
TrailIdx != Name.size() || Name.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"invalid name for variable definition '" + Name + "'");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
|
||||
// Numeric variable definition.
|
||||
if (CmdlineDef[0] == '#') {
|
||||
bool IsPseudo;
|
||||
unsigned TrailIdx;
|
||||
size_t EqIdx = CmdlineDef.find('=');
|
||||
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
|
||||
if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
|
||||
IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid name in numeric variable definition '" +
|
||||
CmdlineName + "'");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect collisions between pattern and numeric variables when the
|
||||
// latter is created later than the former.
|
||||
if (DefinedVariableTable.find(CmdlineName) !=
|
||||
DefinedVariableTable.end()) {
|
||||
SM.PrintMessage(
|
||||
SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error,
|
||||
"pattern variable with name '" + CmdlineName + "' already exists");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1);
|
||||
uint64_t Val;
|
||||
if (CmdlineVal.getAsInteger(10, Val)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineVal.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid value in numeric variable definition '" +
|
||||
CmdlineVal + "'");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
|
||||
|
||||
// Record this variable definition.
|
||||
GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
|
||||
} else {
|
||||
// Pattern variable definition.
|
||||
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
|
||||
StringRef Name = CmdlineNameVal.first;
|
||||
bool IsPseudo;
|
||||
unsigned TrailIdx;
|
||||
if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
|
||||
IsPseudo || TrailIdx != Name.size() || Name.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"invalid name in pattern variable definition '" + Name +
|
||||
"'");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect collisions between pattern and numeric variables when the
|
||||
// former is created later than the latter.
|
||||
if (GlobalNumericVariableTable.find(Name) !=
|
||||
GlobalNumericVariableTable.end()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"numeric variable with name '" + Name +
|
||||
"' already exists");
|
||||
ErrorFound = true;
|
||||
continue;
|
||||
}
|
||||
GlobalVariableTable.insert(CmdlineNameVal);
|
||||
// Mark the pattern variable as defined to detect collisions between
|
||||
// pattern and numeric variables in DefineCmdlineVariables when the
|
||||
// latter is created later than the former. We cannot reuse
|
||||
// GlobalVariableTable for that by populating it with an empty string
|
||||
// since we would then lose the ability to detect the use of an undefined
|
||||
// variable in Match().
|
||||
DefinedVariableTable[Name] = true;
|
||||
}
|
||||
GlobalVariableTable.insert(CmdlineNameVal);
|
||||
}
|
||||
|
||||
return ErrorFound;
|
||||
|
@ -1504,8 +1655,22 @@ void FileCheckPatternContext::clearLocalVars() {
|
|||
if (Var.first()[0] != '$')
|
||||
LocalPatternVars.push_back(Var.first());
|
||||
|
||||
// Numeric expression substitution reads the value of a variable directly,
|
||||
// not via GlobalNumericVariableTable. Therefore, we clear local variables by
|
||||
// clearing their value which will lead to a numeric expression substitution
|
||||
// failure. We also mark the variable for removal from
|
||||
// GlobalNumericVariableTable since this is what defineCmdlineVariables
|
||||
// checks to decide that no global variable has been defined.
|
||||
for (const auto &Var : GlobalNumericVariableTable)
|
||||
if (Var.first()[0] != '$') {
|
||||
Var.getValue()->clearValue();
|
||||
LocalNumericVars.push_back(Var.first());
|
||||
}
|
||||
|
||||
for (const auto &Var : LocalPatternVars)
|
||||
GlobalVariableTable.erase(Var);
|
||||
for (const auto &Var : LocalNumericVars)
|
||||
GlobalNumericVariableTable.erase(Var);
|
||||
}
|
||||
|
||||
bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
|
||||
|
@ -1538,7 +1703,10 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
|
|||
++j;
|
||||
}
|
||||
|
||||
if (Req.EnableVarScope)
|
||||
// Do not clear the first region as it's the one before the first
|
||||
// CHECK-LABEL and it would clear variables defined on the command-line
|
||||
// before they get used.
|
||||
if (i != 0 && Req.EnableVarScope)
|
||||
PatternContext.clearLocalVars();
|
||||
|
||||
for (; i != j; ++i) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
; Test incorrect syntax for -D# option is correctly diagnosed.
|
||||
|
||||
; Invalid variable name: starts with a digit.
|
||||
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
|
||||
|
||||
NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE'
|
||||
NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10
|
||||
NUMERRCLIFMT-NEXT: {{^ \^$}}
|
||||
|
||||
; Invalid definition of pseudo variable.
|
||||
RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO
|
||||
|
||||
NUMERRCLIPSEUDO: Global defines:1:20: error: invalid name in numeric variable definition '@VALUE'
|
||||
NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10
|
||||
NUMERRCLIPSEUDO-NEXT: {{^ \^$}}
|
||||
|
||||
; Invalid definition of an expression.
|
||||
RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL
|
||||
|
||||
NUMERRCLITRAIL: Global defines:1:20: error: invalid name in numeric variable definition 'VALUE+2'
|
||||
NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10
|
||||
NUMERRCLITRAIL-NEXT: {{^ \^$}}
|
||||
|
||||
; Invalid value: numeric expression instead of literal.
|
||||
RUN: not FileCheck -D#VALUE1=3 -D#VALUE2='VALUE1 + 2' --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIEXPR
|
||||
|
||||
NUMERRCLIEXPR: Global defines:2:27: error: invalid value in numeric variable definition 'VALUE1 + 2'
|
||||
NUMERRCLIEXPR-NEXT: Global define #2: #VALUE2=VALUE1 + 2
|
||||
NUMERRCLIEXPR-NEXT: {{^ \^$}}
|
|
@ -0,0 +1,22 @@
|
|||
; Test functionality of -D# option: numeric variables are defined to the right
|
||||
; value and CHECK directives using them match as expected given the value set.
|
||||
|
||||
RUN: FileCheck -D#NUMVAL=12 --check-prefix CHECKNUM --input-file %s %s
|
||||
RUN: not FileCheck -D#NUMVAL=8 --check-prefix CHECKNUM --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG
|
||||
RUN: not FileCheck -D#NUMVAL=12 --check-prefix NUMNOT --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG
|
||||
RUN: FileCheck -D#NUMVAL=8 --check-prefixes NUMNOT --input-file %s %s
|
||||
|
||||
Numeric value = 12
|
||||
CHECKNUM: Numeric value = [[#NUMVAL]]
|
||||
NUMNOT-NOT: Numeric value = [[#NUMVAL]]
|
||||
|
||||
NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input
|
||||
NUMERRMSG: defines.txt:1:1: note: scanning from here
|
||||
NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL" equal to "8"
|
||||
NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here
|
||||
|
||||
NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
|
||||
NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here
|
||||
NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with numeric expression "NUMVAL" equal to "12"
|
|
@ -0,0 +1,95 @@
|
|||
RUN: FileCheck -D#VAR1=11 --input-file %s %s
|
||||
|
||||
; We use CHECK-NEXT directives to force a match on all lines with digits.
|
||||
|
||||
; Numeric expressions using variables defined on the command-line without
|
||||
; spaces
|
||||
USE NO SPC
|
||||
11
|
||||
12
|
||||
10
|
||||
CHECK-LABEL: USE NO SPC
|
||||
CHECK-NEXT: [[#VAR1]]
|
||||
CHECK-NEXT: [[#VAR1+1]]
|
||||
CHECK-NEXT: [[#VAR1-1]]
|
||||
|
||||
; Numeric expressions using variables defined on the command-line in alternate
|
||||
; spacing
|
||||
USE ALT SPC
|
||||
11
|
||||
11
|
||||
12
|
||||
12
|
||||
12
|
||||
12
|
||||
10
|
||||
10
|
||||
10
|
||||
10
|
||||
CHECK-LABEL: USE ALT SPC
|
||||
CHECK-NEXT: [[# VAR1]]
|
||||
CHECK-NEXT: [[# VAR1 ]]
|
||||
CHECK-NEXT: [[# VAR1+1]]
|
||||
CHECK-NEXT: [[# VAR1 +1]]
|
||||
CHECK-NEXT: [[# VAR1 + 1]]
|
||||
CHECK-NEXT: [[# VAR1 + 1 ]]
|
||||
CHECK-NEXT: [[# VAR1-1]]
|
||||
CHECK-NEXT: [[# VAR1 -1]]
|
||||
CHECK-NEXT: [[# VAR1 - 1]]
|
||||
CHECK-NEXT: [[# VAR1 - 1 ]]
|
||||
|
||||
; Numeric expressions using variables defined on the command-line and an
|
||||
; immediate interpreted as an unsigned value
|
||||
; Note: 9223372036854775819 = 0x8000000000000000 + 11
|
||||
; 9223372036854775808 = 0x8000000000000000
|
||||
USE UNSIGNED IMM
|
||||
9223372036854775819
|
||||
CHECK-LABEL: USE UNSIGNED IMM
|
||||
CHECK-NEXT: [[#VAR1+9223372036854775808]]
|
||||
|
||||
; Numeric expression using undefined variable
|
||||
RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
|
||||
|
||||
UNDEF VAR USE
|
||||
UNDEFVAR: 11
|
||||
UNDEF-USE-LABEL: UNDEF VAR USE
|
||||
UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]]
|
||||
UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:30: error: using undefined numeric variable 'UNDEFVAR'
|
||||
UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
|
||||
UNDEF-USE-MSG-NEXT: {{^ \^$}}
|
||||
|
||||
; Numeric expression with unsupported operator
|
||||
RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s
|
||||
|
||||
INVALID OPERATOR
|
||||
VAR1*2: 22
|
||||
INVAL-OP-LABEL: INVALID OPERATOR
|
||||
INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]]
|
||||
INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: unsupported numeric operation '*'
|
||||
INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}}
|
||||
INVAL-OP-MSG-NEXT: {{^ \^$}}
|
||||
|
||||
; Name conflict between Numeric variable definition and pattern variable
|
||||
; definition
|
||||
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s
|
||||
RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s
|
||||
RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s
|
||||
|
||||
PATVAR NUMVAR CONFLICT
|
||||
foobar
|
||||
CONFLICT-LABEL: PATVAR NUMVAR CONFLICT
|
||||
CONFLICT1-NEXT: [[NUMVAR:foo.*]]
|
||||
CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists
|
||||
CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}}
|
||||
CLI-INPUT-PAT-CONFLICT-NEXT: {{^ \^$}}
|
||||
CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists
|
||||
CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar
|
||||
CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}}
|
||||
CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: pattern variable with name 'PATVAR' already exists
|
||||
CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42
|
||||
CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
|
|
@ -28,7 +28,7 @@ ERRCLIVAR2: Missing pattern variable name in command-line definition '-D='
|
|||
RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT
|
||||
|
||||
ERRCLIFMT: Global defines:1:19: error: invalid name for variable definition '10VALUE'
|
||||
ERRCLIFMT: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE'
|
||||
ERRCLIFMT-NEXT: Global define #1: 10VALUE=10
|
||||
ERRCLIFMT-NEXT: {{^ \^$}}
|
||||
|
||||
|
@ -36,7 +36,7 @@ ERRCLIFMT-NEXT: {{^ \^$}}
|
|||
RUN: not FileCheck -D@VALUE=10 --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIPSEUDO
|
||||
|
||||
ERRCLIPSEUDO: Global defines:1:19: error: invalid name for variable definition '@VALUE'
|
||||
ERRCLIPSEUDO: Global defines:1:19: error: invalid name in pattern variable definition '@VALUE'
|
||||
ERRCLIPSEUDO-NEXT: Global define #1: @VALUE=10
|
||||
ERRCLIPSEUDO-NEXT: {{^ \^$}}
|
||||
|
||||
|
@ -44,6 +44,6 @@ ERRCLIPSEUDO-NEXT: {{^ \^$}}
|
|||
RUN: not FileCheck -D'VALUE + 2=10' --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLITRAIL
|
||||
|
||||
ERRCLITRAIL: Global defines:1:19: error: invalid name for variable definition 'VALUE + 2'
|
||||
ERRCLITRAIL: Global defines:1:19: error: invalid name in pattern variable definition 'VALUE + 2'
|
||||
ERRCLITRAIL-NEXT: Global define #1: VALUE + 2=10
|
||||
ERRCLITRAIL-NEXT: {{^ \^$}}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// RUN: FileCheck -input-file %s %s
|
||||
// RUN: FileCheck -check-prefixes CHECK,GLOBAL -input-file %s %s
|
||||
// RUN: FileCheck -check-prefixes CHECK,LOCAL -input-file %s %s
|
||||
// RUN: FileCheck -check-prefixes CHECK,GLOBAL --enable-var-scope -input-file %s %s
|
||||
// RUN: not FileCheck -check-prefixes CHECK,LOCAL --enable-var-scope -input-file %s %s
|
||||
|
||||
local
|
||||
global
|
||||
; CHECK: [[LOCAL:loc.*]]
|
||||
; CHECK: [[$GLOBAL:glo.*]]
|
||||
|
||||
local2
|
||||
global2
|
||||
; CHECK: [[LOCAL]]2
|
||||
; CHECK: [[$GLOBAL]]2
|
||||
|
||||
barrier:
|
||||
; CHECK-LABEL: barrier
|
||||
|
||||
local3
|
||||
global3
|
||||
; LOCAL: [[LOCAL]]3
|
||||
; GLOBAL: [[$GLOBAL]]3
|
|
@ -0,0 +1,35 @@
|
|||
; Test that variables not starting with dollar sign get undefined after a
|
||||
; CHECK-LABEL directive iff --enable-var-scope is used.
|
||||
|
||||
RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s
|
||||
RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
|
||||
RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
|
||||
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: | 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: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s
|
||||
|
||||
local1
|
||||
global1
|
||||
CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]]
|
||||
CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]]
|
||||
|
||||
local2
|
||||
global2
|
||||
CHECK: [[LOCAL]][[#LOCNUM+1]]
|
||||
CHECK: [[$GLOBAL]][[#$GLOBNUM+1]]
|
||||
|
||||
barrier:
|
||||
CHECK-LABEL: barrier
|
||||
|
||||
local3
|
||||
global3
|
||||
LOCAL1: [[LOCAL]]3
|
||||
LOCAL2: local[[#LOCNUM+2]]
|
||||
LOCAL3: [[LOCAL]][[#LOCNUM+2]]
|
||||
GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]]
|
||||
|
||||
ERRUNDEF: expected string not found in input
|
||||
ERRUNDEFLOCAL: uses undefined variable "LOCAL"
|
||||
ERRUNDEFLOCNUM: uses undefined variable "LOCNUM"
|
|
@ -1,6 +1,6 @@
|
|||
; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
|
||||
; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck -check-prefix V %s
|
||||
; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck -check-prefixes V,VV %s
|
||||
; RUN: FileCheck -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s
|
||||
; RUN: FileCheck -v -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s
|
||||
; RUN: FileCheck -vv -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s
|
||||
|
||||
foo
|
||||
bar
|
||||
|
@ -29,6 +29,39 @@ VV-NEXT: verbose.txt:[[@LINE-22]]:1: note: scanning from here
|
|||
VV-NEXT: {{^}}bar{{$}}
|
||||
VV-NEXT: {{^}}^{{$}}
|
||||
|
||||
NUMVAR:42
|
||||
NUMVAR - 1:41
|
||||
CHECK: NUMVAR:[[#NUMVAR]]
|
||||
CHECK-NOT: [[#NUMVAR + 1]]
|
||||
CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]]
|
||||
|
||||
V: verbose.txt:[[#@LINE-4]]:8: remark: {{C}}HECK: expected string found in input
|
||||
V-NEXT: {{C}}HECK: {{NUMVAR:[[][[]#NUMVAR[]][]]$}}
|
||||
V-NEXT: {{^ \^$}}
|
||||
V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
|
||||
V-NEXT: {{^}}NUMVAR:42{{$}}
|
||||
V-NEXT: {{^}}^~~~~~~~~{{$}}
|
||||
V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with numeric expression "NUMVAR" equal to "42"
|
||||
V-NEXT: {{^}}NUMVAR:42{{$}}
|
||||
V-NEXT: {{^}}^~~~~~~~~{{$}}
|
||||
|
||||
V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input
|
||||
V-NEXT: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}}
|
||||
V-NEXT: {{^ \^$}}
|
||||
V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here
|
||||
V-NEXT: {{^}}NUMVAR - 1:41{{$}}
|
||||
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
|
||||
V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with numeric expression "NUMVAR - 1" equal to "41"
|
||||
V-NEXT: {{^}}NUMVAR - 1:41{{$}}
|
||||
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
|
||||
|
||||
VV-NEXT: verbose.txt:[[#@LINE-23]]:12: remark: {{C}}HECK-NOT: excluded string not found in input
|
||||
VV-NEXT: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}}
|
||||
VV-NEXT: {{^ \^$}}
|
||||
VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here
|
||||
VV-NEXT: {{^}}NUMVAR - 1:41{{$}}
|
||||
VV-NEXT: {{^}}^{{$}}
|
||||
|
||||
before empty
|
||||
|
||||
after empty
|
||||
|
@ -99,7 +132,7 @@ V-NEXT: {{^}}^~~{{$}}
|
|||
|
||||
VV-NEXT: verbose.txt:[[@LINE-9]]:19: remark: implicit EOF: expected string found in input
|
||||
VV-NEXT: {{C}}HECK-NOT: {{[{][{]z[}][}]yx$}}
|
||||
VV-NEXT: {{^ \^$}}
|
||||
VV-NEXT: {{^ \^$}}
|
||||
VV-NEXT: verbose.txt:[[@LINE+13]]:1: note: found here
|
||||
VV-NOT: {{.}}
|
||||
VV: {{^\^$}}
|
||||
|
|
|
@ -14,6 +14,57 @@ namespace {
|
|||
|
||||
class FileCheckTest : public ::testing::Test {};
|
||||
|
||||
TEST_F(FileCheckTest, NumericVariable) {
|
||||
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
|
||||
EXPECT_EQ("FOO", FooVar.getName());
|
||||
|
||||
// Defined variable: getValue returns a value, setValue fails and value
|
||||
// remains unchanged.
|
||||
llvm::Optional<uint64_t> Value = FooVar.getValue();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ(42U, *Value);
|
||||
EXPECT_TRUE(FooVar.setValue(43));
|
||||
Value = FooVar.getValue();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ(42U, *Value);
|
||||
|
||||
// Clearing variable: getValue fails, clearValue again fails.
|
||||
EXPECT_FALSE(FooVar.clearValue());
|
||||
Value = FooVar.getValue();
|
||||
EXPECT_FALSE(Value);
|
||||
EXPECT_TRUE(FooVar.clearValue());
|
||||
|
||||
// Undefined variable: setValue works, getValue returns value set.
|
||||
EXPECT_FALSE(FooVar.setValue(43));
|
||||
Value = FooVar.getValue();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ(43U, *Value);
|
||||
}
|
||||
|
||||
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
|
||||
|
||||
TEST_F(FileCheckTest, NumExpr) {
|
||||
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
|
||||
FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &FooVar, 18);
|
||||
|
||||
// Defined variable: eval returns right value, no undefined variable
|
||||
// returned.
|
||||
llvm::Optional<uint64_t> Value = NumExpr.eval();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ(60U, *Value);
|
||||
StringRef UndefVar = NumExpr.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
|
||||
// Undefined variable: eval fails, undefined variable returned. We call
|
||||
// getUndefVarName first to check that it can be called without calling
|
||||
// eval() first.
|
||||
FooVar.clearValue();
|
||||
UndefVar = NumExpr.getUndefVarName();
|
||||
EXPECT_EQ("FOO", UndefVar);
|
||||
Value = NumExpr.eval();
|
||||
EXPECT_FALSE(Value);
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, ValidVarNameStart) {
|
||||
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
|
||||
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
|
||||
|
@ -90,22 +141,38 @@ TEST_F(FileCheckTest, ParseVar) {
|
|||
EXPECT_EQ(TrailIdx, VarName.size() - 1);
|
||||
}
|
||||
|
||||
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||
std::unique_ptr<MemoryBuffer> Buffer =
|
||||
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
|
||||
StringRef StrBufferRef = Buffer->getBuffer();
|
||||
SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
|
||||
return StrBufferRef;
|
||||
}
|
||||
|
||||
class ExprTester {
|
||||
private:
|
||||
SourceMgr SM;
|
||||
FileCheckRequest Req;
|
||||
FileCheckPatternContext Context;
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
|
||||
|
||||
public:
|
||||
ExprTester() {
|
||||
std::vector<std::string> GlobalDefines;
|
||||
GlobalDefines.emplace_back(std::string("#FOO=42"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
// Call ParsePattern to have @LINE defined.
|
||||
P.ParsePattern("N/A", "CHECK", SM, 1, Req);
|
||||
}
|
||||
|
||||
bool parseExpect(std::string &VarName, std::string &Trailer) {
|
||||
bool IsPseudo = VarName[0] == '@';
|
||||
std::string NameTrailer = VarName + Trailer;
|
||||
std::unique_ptr<MemoryBuffer> Buffer =
|
||||
MemoryBuffer::getMemBufferCopy(NameTrailer, "TestBuffer");
|
||||
StringRef NameTrailerRef = Buffer->getBuffer();
|
||||
SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
|
||||
StringRef NameTrailerRef = bufferize(SM, NameTrailer);
|
||||
StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
|
||||
StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
|
||||
return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
|
||||
return P.parseNumericExpression(VarNameRef, IsPseudo, TrailerRef, SM) ==
|
||||
nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,6 +188,14 @@ TEST_F(FileCheckTest, ParseExpr) {
|
|||
Trailer = "";
|
||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Defined variable.
|
||||
VarName = "FOO";
|
||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Undefined variable.
|
||||
VarName = "UNDEF";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Wrong Pseudovar.
|
||||
VarName = "@FOO";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
@ -153,19 +228,38 @@ TEST_F(FileCheckTest, Substitution) {
|
|||
GlobalDefines.emplace_back(std::string("FOO=BAR"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
|
||||
FileCheckPatternSubstitution Substitution =
|
||||
// Substitution of undefined pattern variable fails.
|
||||
FileCheckPatternSubstitution PatternSubstitution =
|
||||
FileCheckPatternSubstitution(&Context, "VAR404", 42);
|
||||
EXPECT_FALSE(Substitution.getResult());
|
||||
EXPECT_FALSE(PatternSubstitution.getResult());
|
||||
|
||||
FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
|
||||
Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
|
||||
llvm::Optional<std::string> Value = Substitution.getResult();
|
||||
// Substitutions of defined pseudo and non-pseudo numeric variables return
|
||||
// the right value.
|
||||
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
|
||||
FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
|
||||
FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
|
||||
FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
|
||||
FileCheckPatternSubstitution SubstitutionLine =
|
||||
FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12);
|
||||
FileCheckPatternSubstitution SubstitutionN =
|
||||
FileCheckPatternSubstitution(&Context, "N", &NumExprN, 30);
|
||||
llvm::Optional<std::string> Value = SubstitutionLine.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ("42", *Value);
|
||||
Value = SubstitutionN.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ("13", *Value);
|
||||
|
||||
// Substitution of undefined numeric variable fails.
|
||||
LineVar.clearValue();
|
||||
EXPECT_FALSE(SubstitutionLine.getResult());
|
||||
NVar.clearValue();
|
||||
EXPECT_FALSE(SubstitutionN.getResult());
|
||||
|
||||
// Substitution of defined pattern variable returns the right value.
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
|
||||
Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
|
||||
Value = Substitution.getResult();
|
||||
PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
|
||||
Value = PatternSubstitution.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_EQ("BAR", *Value);
|
||||
}
|
||||
|
@ -177,19 +271,32 @@ TEST_F(FileCheckTest, UndefVars) {
|
|||
GlobalDefines.emplace_back(std::string("FOO=BAR"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
|
||||
// getUndefVarName() on a pattern variable substitution with an undefined
|
||||
// variable returns that variable.
|
||||
FileCheckPatternSubstitution Substitution =
|
||||
FileCheckPatternSubstitution(&Context, "VAR404", 42);
|
||||
StringRef UndefVar = Substitution.getUndefVarName();
|
||||
EXPECT_EQ("VAR404", UndefVar);
|
||||
|
||||
FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
|
||||
// getUndefVarName() on a pattern variable substitution with a defined
|
||||
// variable returns an empty string.
|
||||
Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
|
||||
UndefVar = Substitution.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
|
||||
// getUndefVarName() on a numeric expression substitution with a defined
|
||||
// variable returns an empty string.
|
||||
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
|
||||
FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
|
||||
Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
|
||||
UndefVar = Substitution.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
|
||||
Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
|
||||
// getUndefVarName() on a numeric expression substitution with an undefined
|
||||
// variable returns that variable.
|
||||
LineVar.clearValue();
|
||||
UndefVar = Substitution.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
EXPECT_EQ("@LINE", UndefVar);
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, FileCheckContext) {
|
||||
|
@ -197,36 +304,71 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
|||
std::vector<std::string> GlobalDefines;
|
||||
SourceMgr SM;
|
||||
|
||||
// Missing equal sign
|
||||
// Missing equal sign.
|
||||
GlobalDefines.emplace_back(std::string("LocalVar"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#LocalNumVar"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
|
||||
// Empty variable
|
||||
// Empty variable name.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
|
||||
// Invalid variable
|
||||
// Invalid variable name.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("18LocalVar=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
|
||||
// Name conflict between pattern and numeric variable.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("LocalVar=18"));
|
||||
GlobalDefines.emplace_back(std::string("#LocalVar=36"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
Cxt = FileCheckPatternContext();
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
|
||||
GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
Cxt = FileCheckPatternContext();
|
||||
|
||||
// Invalid numeric value for numeric variable.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
|
||||
// Define local variables from command-line.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
|
||||
GlobalDefines.emplace_back(std::string("EmptyVar="));
|
||||
GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
|
||||
bool GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
|
||||
EXPECT_FALSE(GotError);
|
||||
|
||||
// Check defined variables are present and undefined is absent.
|
||||
StringRef LocalVarStr = "LocalVar";
|
||||
StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar");
|
||||
StringRef EmptyVarStr = "EmptyVar";
|
||||
StringRef UnknownVarStr = "UnknownVar";
|
||||
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
||||
FileCheckNumExpr *NumExpr =
|
||||
P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
|
||||
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
||||
EXPECT_TRUE(LocalVar);
|
||||
EXPECT_EQ(*LocalVar, "FOO");
|
||||
EXPECT_TRUE(NumExpr);
|
||||
llvm::Optional<uint64_t> NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_EQ(*NumExprVal, 18U);
|
||||
EXPECT_TRUE(EmptyVar);
|
||||
EXPECT_EQ(*EmptyVar, "");
|
||||
EXPECT_FALSE(UnknownVar);
|
||||
|
@ -235,21 +377,46 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
|||
Cxt.clearLocalVars();
|
||||
LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
||||
EXPECT_FALSE(LocalVar);
|
||||
// Check a numeric expression's evaluation fails if called after clearing of
|
||||
// local variables, if it was created before. This is important because local
|
||||
// variable clearing due to --enable-var-scope happens after numeric
|
||||
// expressions are linked to the numeric variables they use.
|
||||
EXPECT_FALSE(NumExpr->eval());
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
||||
NumExpr =
|
||||
P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
|
||||
EXPECT_FALSE(NumExpr);
|
||||
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||
EXPECT_FALSE(EmptyVar);
|
||||
|
||||
// Redefine global variables and check variables are defined again.
|
||||
GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
|
||||
GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
|
||||
GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
|
||||
EXPECT_FALSE(GotError);
|
||||
StringRef GlobalVarStr = "$GlobalVar";
|
||||
StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
|
||||
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||
EXPECT_TRUE(GlobalVar);
|
||||
EXPECT_EQ(*GlobalVar, "BAR");
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
||||
NumExpr =
|
||||
P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
|
||||
EXPECT_TRUE(NumExpr);
|
||||
NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_EQ(*NumExprVal, 36U);
|
||||
|
||||
// Clear local variables and check global variables remain defined.
|
||||
Cxt.clearLocalVars();
|
||||
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||
EXPECT_TRUE(GlobalVar);
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt);
|
||||
NumExpr =
|
||||
P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
|
||||
EXPECT_TRUE(NumExpr);
|
||||
NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_EQ(*NumExprVal, 36U);
|
||||
}
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in New Issue