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