diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -571,15 +571,26 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :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 ``[[#]]`` -where: +The syntax to define a numeric variable is ``[[#:]]`` where +```` is the name of the numeric variable to define to the matching +value. -* ```` 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 ``[[#]]`` where: + +* ```` is the name of a defined numeric variable. * ```` is an optional numeric operation to perform on the value of ````. Currently supported numeric operations are ``+`` and ``-``. @@ -590,31 +601,35 @@ Spaces are accepted before, after and between any of these elements. -Unlike string substitution blocks, numeric substitution blocks only introduce -numeric substitutions which substitute a numeric expression for its value. For example: .. code-block:: llvm - ; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]] + ; CHECK: load r[[#REG:]], [r0] + ; CHECK: load r[[#REG+1]], [r1] -The above example would match the line: +The above example would match the text: .. code-block:: gas - add r5, r5, r6 + load r5, [r0] + load r6, [r1] -but would not match the line: +but would not match the text: .. code-block:: gas - add r5, r5, r7 + load r5, [r0] + load r7, [r1] due to ``7`` being unequal to ``5 + 1``. The ``--enable-var-scope`` option has the same effect on numeric variables as on string variables. +Important note: In its current implementation, a numeric expression cannot use +a numeric variable defined on the same line. + FileCheck Pseudo Numeric Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -624,9 +639,9 @@ 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: diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -41,7 +41,9 @@ //===----------------------------------------------------------------------===// /// Class representing a numeric variable with a given value in a numeric -/// expression. +/// expression. Each definition of a variable gets its own instance of this +/// class. Variable uses share the same instance as their respective +/// definition. class FileCheckNumericVariable { private: /// Name of the numeric variable. @@ -50,11 +52,19 @@ /// Value of numeric variable, if defined, or None otherwise. Optional Value; + /// Line number where this variable is defined. Used to determine whether a + /// variable is defined on the same line as a given use. + size_t DefLineNumber; + public: + /// Constructor for a variable \p Name defined at line \p DefLineNumber. + FileCheckNumericVariable(size_t DefLineNumber, StringRef Name) + : Name(Name), DefLineNumber(DefLineNumber) {} + /// Constructor for numeric variable \p Name with a known \p Value at parse /// time (e.g. the @LINE numeric variable). FileCheckNumericVariable(StringRef Name, uint64_t Value) - : Name(Name), Value(Value) {} + : Name(Name), Value(Value), DefLineNumber(0) {} /// \returns name of that numeric variable. StringRef getName() const { return Name; } @@ -69,6 +79,9 @@ /// Clears value of this numeric variable. \returns whether the variable was /// already undefined. bool clearValue(); + + /// \returns the line number where this variable is defined. + size_t getDefLineNumber() { return DefLineNumber; } }; /// Type of functions evaluating a given binary operation. @@ -248,8 +261,11 @@ /// 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 GlobalNumericVariableTable; /// Vector holding pointers to all parsed numeric expressions. Used to @@ -292,7 +308,8 @@ /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. - FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value); + template + FileCheckNumericVariable *makeNumericVariable(Types... args); /// Makes a new string substitution and registers it for destruction when the /// context is destroyed. @@ -325,8 +342,8 @@ /// and the value of numeric expression "N+1" at offset 6. std::vector 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 @@ -336,6 +353,26 @@ /// iterating over values. std::map 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 NumericVariableDefs; + /// Pointer to a class instance holding the global state shared by all /// patterns: /// - separate tables with the values of live string and numeric variables @@ -346,13 +383,14 @@ Check::FileCheckType CheckTy; - /// Contains the number of line this pattern is in. - unsigned LineNumber; + /// Line number for this CHECK pattern. Used to determine whether a variable + /// definition is made on an earlier line to the one with this CHECK. + size_t LineNumber; public: - explicit FileCheckPattern(Check::FileCheckType Ty, - FileCheckPatternContext *Context) - : Context(Context), CheckTy(Ty) {} + FileCheckPattern(Check::FileCheckType Ty, FileCheckPatternContext *Context, + size_t Line) + : Context(Context), CheckTy(Ty), LineNumber(Line) {} /// \returns the location in source code. SMLoc getLoc() const { return PatternLoc; } @@ -363,31 +401,37 @@ /// \returns whether \p C is a valid first character for a variable name. static bool isValidVarNameStart(char C); - /// Verifies that the string at the start of \p Str is a well formed - /// variable. \returns false if it is and sets \p IsPseudo to indicate if it - /// is a pseudo variable and \p TrailIdx to the position of the last - /// character that is part of the variable name. Otherwise, only - /// \returns true. - static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); - /// Parses a numeric substitution involving (pseudo if \p IsPseudo is true) - /// variable \p Name with the string corresponding to the operation being - /// performed in \p Trailer. \returns the class representing the numeric - /// expression being substituted or nullptr if parsing fails, in which case - /// errors are reported on \p SM. - FileCheckNumExpr *parseNumericSubstitution(StringRef Name, bool IsPseudo, - StringRef Trailer, - const SourceMgr &SM) const; + /// Parses the string at the start of \p Str for a variable name and \returns + /// whether the variable name is ill-formed. If parsing succeeded, sets + /// \p IsPseudo to indicate if it is a pseudo variable, sets \p Name to the + /// parsed variable name and strips \p Str from the variable name. + static bool parseVariable(StringRef &Str, StringRef &Name, bool &IsPseudo); + /// Parses \p Expr for the definition of a numeric variable, returning an + /// error if \p Context already holds a string variable with the same name. + /// \returns whether parsing fails, in which case errors are reported on + /// \p SM. Otherwise, sets \p Name to the name of the parsed numeric + /// variable. + static bool parseNumericVariableDefinition(StringRef &Expr, StringRef &Name, + FileCheckPatternContext *Context, + const SourceMgr &SM); + /// Parses \p Expr for a numeric substitution block. \returns the class + /// representing the AST of the numeric expression whose value must be + /// substituted, or nullptr if parsing fails, in which case errors are + /// reported on \p SM. Sets \p DefinedNumericVariable to point to the class + /// representing the numeric variable defined in this numeric substitution + /// block, or nullptr if this block does not define any variable. + FileCheckNumExpr *parseNumericSubstitutionBlock( + StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, + const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// /// \p Prefix provides which prefix is being matched, \p Req describes the /// global options that influence the parsing such as whitespace - /// canonicalization, \p SM provides the SourceMgr used for error reports, - /// and \p LineNumber is the line number in the input file from which the - /// pattern string was read. \returns true in case of an error, false - /// otherwise. - bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, - unsigned LineNumber, const FileCheckRequest &Req); + /// canonicalization, \p SM provides the SourceMgr used for error reports. + /// \returns true in case of an error, false otherwise. + bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, + const FileCheckRequest &Req); /// Matches the pattern string against the input buffer \p Buffer /// /// \returns the position that is matched or npos if there is no match. If @@ -396,8 +440,11 @@ /// /// The GlobalVariableTable StringMap in the FileCheckPatternContext class /// instance provides the current values of FileCheck string variables and - /// is updated if this match defines new values. - size_t match(StringRef Buffer, size_t &MatchLen) const; + /// is updated if this match defines new values. Likewise, the + /// GlobalNumericVariableTable StringMap in the same class provides the + /// current values of FileCheck numeric variables and is updated if this + /// match defines new numeric values. + size_t match(StringRef Buffer, size_t &MatchLen, const SourceMgr &SM) const; /// Prints the value of successful substitutions or the name of the undefined /// string or numeric variable preventing a successful substitution. void printSubstitutions(const SourceMgr &SM, StringRef Buffer, @@ -427,6 +474,17 @@ /// \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; }; //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -39,11 +39,12 @@ } Optional FileCheckNumExpr::eval() const { - Optional LeftOp = this->LeftOp->getValue(); + assert(LeftOp && "Evaluating an empty numeric expression"); + Optional LeftOpValue = LeftOp->getValue(); // Variable is undefined. - if (!LeftOp) + if (!LeftOpValue) return None; - return EvalBinop(*LeftOp, RightOp); + return EvalBinop(*LeftOpValue, RightOp); } StringRef FileCheckNumExpr::getUndefVarName() const { @@ -84,8 +85,8 @@ return C == '_' || isalpha(C); } -bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo, - unsigned &TrailIdx) { +bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name, + bool &IsPseudo) { if (Str.empty()) return true; @@ -107,7 +108,8 @@ ParsedOneChar = true; } - TrailIdx = I; + Name = Str.take_front(I); + Str = Str.substr(I); return false; } @@ -122,24 +124,52 @@ return C; } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp + RightOp; -} -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp - RightOp; +bool FileCheckPattern::parseNumericVariableDefinition( + StringRef &Expr, StringRef &Name, FileCheckPatternContext *Context, + const SourceMgr &SM) { + bool IsPseudo; + if (parseVariable(Expr, Name, IsPseudo)) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid variable name"); + return true; + } + + if (IsPseudo) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "definition of pseudo numeric variable unsupported"); + return true; + } + + // Detect collisions between string and numeric variables when the latter + // is created later than the former. + if (Context->DefinedVariableTable.find(Name) != + Context->DefinedVariableTable.end()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "string variable with name '" + Name + "' already exists"); + return true; + } + + return false; } -FileCheckNumExpr * -FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo, - StringRef Trailer, - const SourceMgr &SM) const { +FileCheckNumericVariable * +FileCheckPattern::parseNumericVariableUse(StringRef &Expr, + const SourceMgr &SM) const { + bool IsPseudo; + StringRef Name; + if (parseVariable(Expr, Name, IsPseudo)) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid variable name"); + return nullptr; + } + if (IsPseudo && !Name.equals("@LINE")) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "invalid pseudo numeric variable '" + Name + "'"); return nullptr; } - // This method is indirectly called from ParsePattern for all numeric + // This method is indirectly called from parsePattern for all numeric // variable definitions and uses in the order in which they appear in the // CHECK pattern. For each definition, the pointer to the class instance of // the corresponding numeric variable definition is stored in @@ -152,16 +182,40 @@ return nullptr; } - FileCheckNumericVariable *LeftOp = VarTableIter->second; + FileCheckNumericVariable *NumericVariable = VarTableIter->second; + if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "numeric variable '" + Name + + "' defined on the same line as used"); + return nullptr; + } + + return NumericVariable; +} + +static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { + return LeftOp + RightOp; +} + +static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { + return LeftOp - RightOp; +} + +FileCheckNumExpr *FileCheckPattern::parseBinop(StringRef &Expr, + const SourceMgr &SM) const { + FileCheckNumericVariable *LeftOp = parseNumericVariableUse(Expr, SM); + if (!LeftOp) { + // Error reporting done in parseNumericVariableUse(). + return nullptr; + } // Check if this is a supported operation and select a function to perform // it. - Trailer = Trailer.ltrim(SpaceChars); - if (Trailer.empty()) { + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) return Context->makeNumExpr(add, LeftOp, 0); - } - SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data()); - char Operator = popFront(Trailer); + SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); + char Operator = popFront(Expr); binop_eval_t EvalBinop; switch (Operator) { case '+': @@ -178,35 +232,76 @@ } // Parse right operand. - Trailer = Trailer.ltrim(SpaceChars); - if (Trailer.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, "missing operand in numeric expression"); return nullptr; } uint64_t RightOp; - if (Trailer.consumeInteger(10, RightOp)) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, - "invalid offset in numeric expression '" + Trailer + "'"); + if (Expr.consumeInteger(10, RightOp)) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid offset in numeric expression '" + Expr + "'"); return nullptr; } - Trailer = Trailer.ltrim(SpaceChars); - if (!Trailer.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, + Expr = Expr.ltrim(SpaceChars); + if (!Expr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, "unexpected characters at end of numeric expression '" + - Trailer + "'"); + Expr + "'"); return nullptr; } return Context->makeNumExpr(EvalBinop, LeftOp, RightOp); } -bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, - SourceMgr &SM, unsigned LineNumber, +FileCheckNumExpr *FileCheckPattern::parseNumericSubstitutionBlock( + StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, + const SourceMgr &SM) const { + // Parse the numeric variable definition. + DefinedNumericVariable = nullptr; + size_t DefEnd = Expr.find(':'); + if (DefEnd != StringRef::npos) { + StringRef DefExpr = Expr.substr(0, DefEnd); + StringRef UseExpr = Expr = Expr.substr(DefEnd + 1); + + DefExpr = DefExpr.ltrim(SpaceChars); + StringRef Name; + if (parseNumericVariableDefinition(DefExpr, Name, Context, SM)) { + // Invalid variable definition. Error reporting done in parsing function. + return nullptr; + } + + DefinedNumericVariable = + Context->makeNumericVariable(this->LineNumber, Name); + + DefExpr = DefExpr.ltrim(SpaceChars); + if (!DefExpr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), + SourceMgr::DK_Error, + "invalid numeric variable definition"); + return nullptr; + } + UseExpr = UseExpr.ltrim(SpaceChars); + if (!UseExpr.empty()) { + SM.PrintMessage( + SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, + "unexpected string after variable definition: '" + UseExpr + "'"); + return nullptr; + } + return Context->makeNumExpr(add, nullptr, 0); + } + + // Parse the numeric expression itself. + Expr = Expr.ltrim(SpaceChars); + return parseBinop(Expr, SM); +} + +bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, + SourceMgr &SM, const FileCheckRequest &Req) { bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot; - this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); // Create fake @LINE pseudo variable definition. @@ -292,11 +387,11 @@ // String and numeric substitution blocks. String substitution blocks come // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some // other regex) and assigns it to the string variable 'foo'. The latter - // substitutes foo's value. Numeric substitution blocks start with a - // '#' sign after the double brackets and only have the substitution form. - // Both string and numeric variables must satisfy the regular expression - // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common - // errors. + // substitutes foo's value. Numeric substitution blocks work the same way + // as string ones, but start with a '#' sign after the double brackets. + // Both string and numeric variable names must satisfy the regular + // expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch + // some common errors. if (PatternStr.startswith("[[")) { StringRef UnparsedPatternStr = PatternStr.substr(2); // Find the closing bracket pair ending the match. End is going to be an @@ -316,92 +411,129 @@ // 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 [[#]]. - 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 ([[]]) 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: [[:(...)]] and + // [[#(...):(...)]]. + if (IsNumBlock) { + FileCheckNumExprMatch NumExprDef = {DefinedNumericVariable, CurParen}; + NumericVariableDefs[DefName] = NumExprDef; + // This store is done here rather than in match() to allow + // parseNumericVariableUse() to get the pointer to the class instance + // of the right variable definition corresponding to a given numeric + // variable use. + Context->GlobalNumericVariableTable[DefName] = DefinedNumericVariable; + } else { + VariableDefs[DefName] = CurParen; + // Mark the string variable as defined to detect collisions between + // string and numeric variables in parseNumericVariableUse() and + // DefineCmdlineVariables() when the latter is created later than the + // former. We cannot reuse GlobalVariableTable for this by populating + // it with an empty string since we would then lose the ability to + // detect the use of an undefined variable in match(). + Context->DefinedVariableTable[DefName] = true; + } RegExStr += '('; ++CurParen; - if (AddRegExToRegEx(Trailer, CurParen, SM)) + if (AddRegExToRegEx(MatchRegexp, CurParen, SM)) return true; RegExStr += ')'; @@ -444,7 +576,8 @@ RegExStr += Backref; } -size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { +size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, + const SourceMgr &SM) const { // If this is the EOF pattern, match it immediately. if (CheckTy == Check::CheckEOF) { MatchLen = 0; @@ -501,6 +634,25 @@ MatchInfo[VariableDef.second]; } + // If this defines any numeric variables, remember their values. + for (const auto &NumericVariableDef : NumericVariableDefs) { + const FileCheckNumExprMatch &NumericVariableMatch = + NumericVariableDef.getValue(); + unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup; + assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error"); + FileCheckNumericVariable *DefinedNumericVariable = + NumericVariableMatch.DefinedNumericVariable; + + StringRef MatchedValue = MatchInfo[CaptureParenGroup]; + uint64_t Val; + if (MatchedValue.getAsInteger(10, Val)) { + SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()), + SourceMgr::DK_Error, "Unable to represent numeric value"); + } + if (DefinedNumericVariable->setValue(Val)) + assert(false && "Numeric variable redefined"); + } + // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after // the required preceding newline, which is consumed by the pattern in the // case of CHECK-EMPTY but not CHECK-NEXT. @@ -643,10 +795,11 @@ return NumExprs.back().get(); } +template FileCheckNumericVariable * -FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) { +FileCheckPatternContext::makeNumericVariable(Types... args) { NumericVariables.push_back( - llvm::make_unique(Name, Value)); + llvm::make_unique(args...)); return NumericVariables.back().get(); } @@ -941,9 +1094,9 @@ 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 DagNotMatches = ImplicitNegativeChecks; @@ -1004,8 +1157,8 @@ SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - FileCheckPattern P(CheckTy, &PatternContext); - if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req)) + FileCheckPattern P(CheckTy, &PatternContext, LineNumber); + if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req)) return true; // Verify that CHECK-LABEL lines do not define or use variables @@ -1049,7 +1202,7 @@ // prefix as a filler for the error message. if (!DagNotMatches.empty()) { CheckStrings.emplace_back( - FileCheckPattern(Check::CheckEOF, &PatternContext), + FileCheckPattern(Check::CheckEOF, &PatternContext, LineNumber + 1), *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -1223,7 +1376,7 @@ StringRef MatchBuffer = Buffer.substr(LastMatchEnd); size_t CurrentMatchLen; // get a match at current start point - size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen); + size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen, SM); if (i == 1) FirstMatchPos = LastPos + MatchPos; @@ -1344,7 +1497,7 @@ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->match(Buffer, MatchLen); + size_t Pos = Pat->match(Buffer, MatchLen, SM); if (Pos == StringRef::npos) { PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, @@ -1404,7 +1557,7 @@ // CHECK-DAG group. for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) { StringRef MatchBuffer = Buffer.substr(MatchPos); - size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen); + size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen, SM); // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (MatchPosBuf == StringRef::npos) { @@ -1568,7 +1721,8 @@ for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) { unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size(); StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart); - if (CmdlineDef.find('=') == StringRef::npos) { + size_t EqIdx = CmdlineDef.find('='); + if (EqIdx == StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()), SourceMgr::DK_Error, "Missing equal sign in global definition"); @@ -1578,27 +1732,28 @@ // Numeric variable definition. if (CmdlineDef[0] == '#') { - bool IsPseudo; - unsigned TrailIdx; - size_t EqIdx = CmdlineDef.find('='); StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1); - if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) || - IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()), - SourceMgr::DK_Error, - "invalid name in numeric variable definition '" + - CmdlineName + "'"); + StringRef VarName; + SMLoc CmdlineNameLoc = SMLoc::getFromPointer(CmdlineName.data()); + bool ParseError = FileCheckPattern::parseNumericVariableDefinition( + CmdlineName, VarName, this, SM); + // Check that CmdlineName starts with a valid numeric variable to be + // defined and that it is not followed that anything. That second check + // detects cases like "FOO+2" in a "FOO+2=10" definition. + if (ParseError || !CmdlineName.empty()) { + if (!ParseError) + SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error, + "invalid variable name"); ErrorFound = true; continue; } // Detect collisions between string and numeric variables when the latter // is created later than the former. - if (DefinedVariableTable.find(CmdlineName) != - DefinedVariableTable.end()) { + if (DefinedVariableTable.find(VarName) != DefinedVariableTable.end()) { SM.PrintMessage( - SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error, - "string variable with name '" + CmdlineName + "' already exists"); + SMLoc::getFromPointer(VarName.data()), SourceMgr::DK_Error, + "string variable with name '" + VarName + "' already exists"); ErrorFound = true; continue; } @@ -1613,21 +1768,25 @@ 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 CmdlineNameVal = CmdlineDef.split('='); - StringRef Name = CmdlineNameVal.first; + StringRef CmdlineName = CmdlineNameVal.first; + StringRef OrigCmdlineName = CmdlineName; + StringRef Name; bool IsPseudo; - unsigned TrailIdx; - if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || - IsPseudo || TrailIdx != Name.size() || Name.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, - "invalid name in string variable definition '" + Name + - "'"); + if (FileCheckPattern::parseVariable(CmdlineName, Name, IsPseudo) || + IsPseudo || !CmdlineName.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(OrigCmdlineName.data()), + SourceMgr::DK_Error, + "invalid name in string variable definition '" + + OrigCmdlineName + "'"); ErrorFound = true; continue; } @@ -1646,7 +1805,7 @@ // 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; diff --git a/llvm/test/FileCheck/numeric-defines-diagnostics.txt b/llvm/test/FileCheck/numeric-defines-diagnostics.txt --- a/llvm/test/FileCheck/numeric-defines-diagnostics.txt +++ b/llvm/test/FileCheck/numeric-defines-diagnostics.txt @@ -4,7 +4,7 @@ RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT -NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE' +NUMERRCLIFMT: Global defines:1:20: error: invalid variable name NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 NUMERRCLIFMT-NEXT: {{^ \^$}} @@ -12,7 +12,7 @@ RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO -NUMERRCLIPSEUDO: Global defines:1:20: error: invalid name in numeric variable definition '@VALUE' +NUMERRCLIPSEUDO: Global defines:1:20: error: definition of pseudo numeric variable unsupported NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 NUMERRCLIPSEUDO-NEXT: {{^ \^$}} @@ -20,7 +20,7 @@ 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: {{^ \^$}} diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -1,21 +1,42 @@ -RUN: FileCheck -D#VAR1=11 --input-file %s %s +RUN: FileCheck --input-file %s %s ; We use CHECK-NEXT directives to force a match on all lines with digits. -; Numeric expressions using variables defined on the command-line without -; spaces +; Numeric variable definition without spaces. +DEF NO SPC +11 +CHECK-LABEL: DEF NO SPC +CHECK-NEXT: [[#VAR1:]] + +; Numeric variable definition with different spacing. +DEF SPC +11 +11 +11 +CHECK-LABEL: DEF SPC +CHECK-NEXT: [[# VAR1a:]] +CHECK-NEXT: [[# VAR1b :]] +CHECK-NEXT: [[# VAR1c : ]] + +; Numeric expressions using variables defined on other lines without spaces. USE NO SPC 11 12 10 -CHECK-LABEL: USE NO SPC +11 +11 +11 +CHECK-LABEL: USE CHECK-NEXT: [[#VAR1]] CHECK-NEXT: [[#VAR1+1]] CHECK-NEXT: [[#VAR1-1]] +CHECK-NEXT: [[#VAR1a]] +CHECK-NEXT: [[#VAR1b]] +CHECK-NEXT: [[#VAR1c]] -; Numeric expressions using variables defined on the command-line in alternate -; spacing -USE ALT SPC +; Numeric expressions using variables defined on other lines with different +; spacing. +USE SPC 11 11 12 @@ -26,7 +47,7 @@ 10 10 10 -CHECK-LABEL: USE ALT SPC +CHECK-LABEL: USE SPC CHECK-NEXT: [[# VAR1]] CHECK-NEXT: [[# VAR1 ]] CHECK-NEXT: [[# VAR1+1]] @@ -39,7 +60,7 @@ CHECK-NEXT: [[# VAR1 - 1 ]] ; Numeric expressions using variables defined on the command-line and an -; immediate interpreted as an unsigned value +; immediate interpreted as an unsigned value. ; Note: 9223372036854775819 = 0x8000000000000000 + 11 ; 9223372036854775808 = 0x8000000000000000 USE UNSIGNED IMM @@ -47,7 +68,7 @@ CHECK-LABEL: USE UNSIGNED IMM CHECK-NEXT: [[#VAR1+9223372036854775808]] -; Numeric expression using undefined variable +; Numeric expression using undefined variable. RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s @@ -59,37 +80,51 @@ UNDEF-USE-MSG-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: {{^ \^$}} diff --git a/llvm/test/FileCheck/var-scope.txt b/llvm/test/FileCheck/var-scope.txt --- a/llvm/test/FileCheck/var-scope.txt +++ b/llvm/test/FileCheck/var-scope.txt @@ -1,19 +1,22 @@ ; Test that variables not starting with dollar sign get undefined after a ; CHECK-LABEL directive iff --enable-var-scope is used. -RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s -RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \ +; Reference run: variables remain defined at all time when not using +; --enable-var-scope option. +RUN: FileCheck --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s + +RUN: FileCheck --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s +RUN: not FileCheck --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \ +RUN: not FileCheck --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \ +RUN: not FileCheck --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s local1 global1 -CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]] -CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]] +CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM:]] +CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM:]] local2 global2 diff --git a/llvm/test/FileCheck/verbose.txt b/llvm/test/FileCheck/verbose.txt --- a/llvm/test/FileCheck/verbose.txt +++ b/llvm/test/FileCheck/verbose.txt @@ -1,6 +1,6 @@ -; RUN: FileCheck -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s -; RUN: FileCheck -v -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s -; RUN: FileCheck -vv -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s +; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s +; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s +; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s foo bar @@ -29,36 +29,33 @@ VV-NEXT: {{^}}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: {{^}}^{{$}} diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -15,12 +15,16 @@ 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 Value = FooVar.getValue(); + EXPECT_FALSE(Value); + EXPECT_TRUE(FooVar.clearValue()); + EXPECT_FALSE(FooVar.setValue(42)); + + // Defined variable: getValue returns value set, setValue fails. + Value = FooVar.getValue(); EXPECT_TRUE(Value); EXPECT_EQ(42U, *Value); EXPECT_TRUE(FooVar.setValue(43)); @@ -33,12 +37,6 @@ Value = FooVar.getValue(); EXPECT_FALSE(Value); EXPECT_TRUE(FooVar.clearValue()); - - // Undefined variable: setValue works, getValue returns value set. - EXPECT_FALSE(FooVar.setValue(43)); - Value = FooVar.getValue(); - EXPECT_TRUE(Value); - EXPECT_EQ(43U, *Value); } uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } @@ -78,67 +76,69 @@ } TEST_F(FileCheckTest, ParseVar) { - StringRef VarName = "GoodVar42"; + StringRef OrigVarName = "GoodVar42"; + StringRef VarName = OrigVarName; + StringRef ParsedName; bool IsPseudo = true; - unsigned TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(ParsedName, OrigVarName); + EXPECT_TRUE(VarName.empty()); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size()); - VarName = "$GoodGlobalVar"; + VarName = OrigVarName = "$GoodGlobalVar"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(ParsedName, OrigVarName); + EXPECT_TRUE(VarName.empty()); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size()); - VarName = "@GoodPseudoVar"; + VarName = OrigVarName = "@GoodPseudoVar"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(ParsedName, OrigVarName); + EXPECT_TRUE(VarName.empty()); EXPECT_TRUE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size()); VarName = "42BadVar"; - EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); VarName = "$@"; - EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); - VarName = "B@dVar"; + VarName = OrigVarName = "B@dVar"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(VarName, OrigVarName.substr(1)); + EXPECT_EQ(ParsedName, "B"); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, 1U); - VarName = "B$dVar"; + VarName = OrigVarName = "B$dVar"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(VarName, OrigVarName.substr(1)); + EXPECT_EQ(ParsedName, "B"); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, 1U); VarName = "BadVar+"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(VarName, "+"); + EXPECT_EQ(ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size() - 1); VarName = "BadVar-"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(VarName, "-"); + EXPECT_EQ(ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size() - 1); VarName = "BadVar:"; IsPseudo = true; - TrailIdx = 0; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, IsPseudo, TrailIdx)); + EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + EXPECT_EQ(VarName, ":"); + EXPECT_EQ(ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); - EXPECT_EQ(TrailIdx, VarName.size() - 1); } static StringRef bufferize(SourceMgr &SM, StringRef Str) { @@ -149,76 +149,175 @@ 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 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) { @@ -236,7 +335,7 @@ // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); - FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10); + FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10); FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); FileCheckNumericSubstitution SubstitutionLine = @@ -257,7 +356,7 @@ EXPECT_FALSE(SubstitutionN.getResult()); // Substitution of a defined string variable returns the right value. - FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); + FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); Value = StringSubstitution.getResult(); EXPECT_TRUE(Value); @@ -359,9 +458,10 @@ StringRef EmptyVarStr = "EmptyVar"; StringRef UnknownVarStr = "UnknownVar"; llvm::Optional 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 EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); EXPECT_TRUE(LocalVar); @@ -383,9 +483,9 @@ // variable clearing due to --enable-var-scope happens after numeric // expressions are linked to the numeric variables they use. EXPECT_FALSE(NumExpr->eval()); - P = FileCheckPattern(Check::CheckPlain, &Cxt); - NumExpr = - P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM); + P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); + NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef, + DefinedNumericVariable, SM); EXPECT_FALSE(NumExpr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -400,9 +500,9 @@ llvm::Optional GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); EXPECT_EQ(*GlobalVar, "BAR"); - P = FileCheckPattern(Check::CheckPlain, &Cxt); - NumExpr = - P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM); + P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); + NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, + DefinedNumericVariable, SM); EXPECT_TRUE(NumExpr); NumExprVal = NumExpr->eval(); EXPECT_TRUE(NumExprVal); @@ -412,9 +512,9 @@ 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);