Index: llvm/trunk/docs/CommandGuide/FileCheck.rst =================================================================== --- llvm/trunk/docs/CommandGuide/FileCheck.rst +++ llvm/trunk/docs/CommandGuide/FileCheck.rst @@ -107,10 +107,12 @@ Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be used in ``CHECK:`` lines. -.. option:: -D#= +.. option:: -D#= - Sets a filecheck numeric variable ``NUMVAR`` to ```` that can be used - in ``CHECK:`` lines. + Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating + ```` that can be used in ``CHECK:`` lines. See section + ``FileCheck Numeric Variables and Expressions`` for details on the format + and meaning of ````. .. option:: -version @@ -590,18 +592,15 @@ would match ``mov r5, 42`` and set ``REG`` to the value ``5``. -The syntax of a numeric substitution is ``[[#]]`` where: +The syntax of a numeric substitution is ``[[#]]`` where ```` is an +expression. An expression is recursively defined as: -* ```` is the name of a defined numeric variable. +* a numeric operand, or +* an expression followed by an operator and a numeric operand. -* ```` is an optional operation to perform on the value of ````. - Currently supported operations are ``+`` and ``-``. - -* ```` is the immediate value that constitutes the second operand of - the operation ````. It must be present if ```` is present, absent - otherwise. - -Spaces are accepted before, after and between any of these elements. +A numeric operand is a previously defined numeric variable, or an integer +literal. The supported operators are ``+`` and ``-``. Spaces are accepted +before, after and between any of these elements. For example: Index: llvm/trunk/include/llvm/Support/FileCheck.h =================================================================== --- llvm/trunk/include/llvm/Support/FileCheck.h +++ llvm/trunk/include/llvm/Support/FileCheck.h @@ -40,6 +40,54 @@ // Numeric substitution handling code. //===----------------------------------------------------------------------===// +/// Base class representing the AST of a given expression. +class FileCheckExpressionAST { +public: + virtual ~FileCheckExpressionAST() = default; + + /// Evaluates and \returns the value of the expression represented by this + /// AST or an error if evaluation fails. + virtual Expected eval() const = 0; +}; + +/// Class representing an unsigned literal in the AST of an expression. +class FileCheckExpressionLiteral : public FileCheckExpressionAST { +private: + /// Actual value of the literal. + uint64_t Value; + +public: + /// Constructs a literal with the specified value. + FileCheckExpressionLiteral(uint64_t Val) : Value(Val) {} + + /// \returns the literal's value. + Expected eval() const { return Value; } +}; + +/// Class to represent an undefined variable error, which quotes that +/// variable's name when printed. +class FileCheckUndefVarError : public ErrorInfo { +private: + StringRef VarName; + +public: + static char ID; + + FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {} + + StringRef getVarName() const { return VarName; } + + std::error_code convertToErrorCode() const override { + return inconvertibleErrorCode(); + } + + /// Print name of variable associated with this error. + void log(raw_ostream &OS) const override { + OS << "\""; + OS.write_escaped(VarName) << "\""; + } +}; + /// Class representing a numeric variable and its associated current value. class FileCheckNumericVariable { private: @@ -81,56 +129,53 @@ size_t getDefLineNumber() { return DefLineNumber; } }; -/// Type of functions evaluating a given binary operation. -using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); - -/// Class to represent an undefined variable error which prints that variable's -/// name between quotes when printed. -class FileCheckUndefVarError : public ErrorInfo { +/// Class representing the use of a numeric variable in the AST of an +/// expression. +class FileCheckNumericVariableUse : public FileCheckExpressionAST { private: - StringRef VarName; - -public: - static char ID; - - FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {} + /// Name of the numeric variable. + StringRef Name; - StringRef getVarName() const { return VarName; } + /// Pointer to the class instance for the variable this use is about. + FileCheckNumericVariable *NumericVariable; - std::error_code convertToErrorCode() const override { - return inconvertibleErrorCode(); - } +public: + FileCheckNumericVariableUse(StringRef Name, + FileCheckNumericVariable *NumericVariable) + : Name(Name), NumericVariable(NumericVariable) {} - /// Print name of variable associated with this error. - void log(raw_ostream &OS) const override { - OS << "\""; - OS.write_escaped(VarName) << "\""; - } + /// \returns the value of the variable referenced by this instance. + Expected eval() const; }; -/// Class representing an expression consisting of either a single numeric -/// variable or a binary operation between a numeric variable and an -/// immediate. -class FileCheckExpression { +/// Type of functions evaluating a given binary operation. +using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); + +/// Class representing a single binary operation in the AST of an expression. +class FileCheckASTBinop : public FileCheckExpressionAST { private: /// Left operand. - FileCheckNumericVariable *LeftOp; + std::unique_ptr LeftOperand; /// Right operand. - uint64_t RightOp; + std::unique_ptr RightOperand; /// Pointer to function that can evaluate this binary operation. binop_eval_t EvalBinop; public: - FileCheckExpression(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) - : LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {} - - /// Evaluates the value of this expression, using EvalBinop to perform the - /// binary operation it consists of. \returns an error if the numeric - /// variable used is undefined, or the expression value otherwise. + FileCheckASTBinop(binop_eval_t EvalBinop, + std::unique_ptr LeftOp, + std::unique_ptr RightOp) + : EvalBinop(EvalBinop) { + LeftOperand = std::move(LeftOp); + RightOperand = std::move(RightOp); + } + + /// Evaluates the value of the binary operation represented by this AST, + /// using EvalBinop on the result of recursively evaluating the operands. + /// \returns the expression value or an error if an undefined numeric + /// variable is used in one of the operands. Expected eval() const; }; @@ -187,15 +232,15 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - FileCheckExpression *Expression; + std::unique_ptr ExpressionAST; public: - FileCheckNumericSubstitution(FileCheckPatternContext *Context, - StringRef ExpressionStr, - FileCheckExpression *Expression, + FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, + std::unique_ptr ExprAST, size_t InsertIdx) - : FileCheckSubstitution(Context, ExpressionStr, InsertIdx), - Expression(Expression) {} + : FileCheckSubstitution(Context, Expr, InsertIdx) { + ExpressionAST = std::move(ExprAST); + } /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -278,10 +323,6 @@ /// easily updating its value. FileCheckNumericVariable *LineVariable = nullptr; - /// Vector holding pointers to all parsed expressions. Used to automatically - /// free the expressions once they are guaranteed to no longer be used. - std::vector> Expressions; - /// Vector holding pointers to all parsed numeric variables. Used to /// automatically free them once they are guaranteed to no longer be used. std::vector> NumericVariables; @@ -313,12 +354,6 @@ void clearLocalVars(); private: - /// Makes a new expression instance and registers it for destruction when - /// the context is destroyed. - FileCheckExpression *makeExpression(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight); - /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. template @@ -333,7 +368,8 @@ /// the context is destroyed. FileCheckSubstitution * makeNumericSubstitution(StringRef ExpressionStr, - FileCheckExpression *Expression, size_t InsertIdx); + std::unique_ptr ExpressionAST, + size_t InsertIdx); }; /// Class to represent an error holding a diagnostic with location information @@ -458,13 +494,20 @@ /// \returns whether \p C is a valid first character for a variable name. static bool isValidVarNameStart(char C); + + /// Parsing information about a variable. + struct VariableProperties { + StringRef Name; + bool IsPseudo; + }; + /// Parses the string at the start of \p Str for a variable name. \returns - /// an error holding a diagnostic against \p SM if parsing fail, or the - /// name of the variable otherwise. In the latter case, sets \p IsPseudo to - /// indicate if it is a pseudo variable and strips \p Str from the variable - /// name. - static Expected parseVariable(StringRef &Str, bool &IsPseudo, - const SourceMgr &SM); + /// a VariableProperties structure holding the variable name and whether it + /// is the name of a pseudo variable, or an error holding a diagnostic + /// against \p SM if parsing fail. If parsing was successful, also strips + /// \p Str from the variable name. + static Expected parseVariable(StringRef &Str, + const SourceMgr &SM); /// Parses \p Expr for the name of a numeric variable to be defined at line /// \p LineNumber. \returns a pointer to the class instance representing that /// variable, creating it if needed, or an error holding a diagnostic against @@ -473,16 +516,19 @@ parseNumericVariableDefinition(StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber, const SourceMgr &SM); - /// Parses \p Expr for a numeric substitution block. \returns the class - /// representing the AST of the expression whose value must be substituted, - /// or an error holding a diagnostic against \p SM if parsing fails. If - /// substitution was successful, sets \p DefinedNumericVariable to point to - /// the class representing the numeric variable defined in this numeric + /// Parses \p Expr for a numeric substitution block. Parameter + /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE + /// expression. \returns a pointer to the class instance representing the AST + /// of the expression whose value must be substituted, or an error holding a + /// diagnostic against \p SM if parsing fails. If substitution was + /// successful, sets \p DefinedNumericVariable to point to the class + /// representing the numeric variable being defined in this numeric /// substitution block, or None if this block does not define any variable. - Expected parseNumericSubstitutionBlock( + Expected> + parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, - const SourceMgr &SM) const; + bool IsLegacyLineExpr, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// @@ -507,7 +553,7 @@ Expected 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. + /// string or numeric variables preventing a successful substitution. void printSubstitutions(const SourceMgr &SM, StringRef Buffer, SMRange MatchRange = None) const; void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer, @@ -536,16 +582,28 @@ /// 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 an error + /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use. + /// \returns the pointer to the class instance representing that variable if + /// successful, or an error holding a diagnostic against \p SM otherwise. + Expected> + parseNumericVariableUse(StringRef Name, bool IsPseudo, + const SourceMgr &SM) const; + enum class AllowedOperand { LineVar, Literal, Any }; + /// Parses \p Expr for use of a numeric operand. Accepts both literal values + /// and numeric variables, depending on the value of \p AO. \returns the + /// class representing that operand in the AST of the expression or an error /// holding a diagnostic against \p SM otherwise. - Expected - parseNumericVariableUse(StringRef &Expr, const SourceMgr &SM) const; - /// Parses \p Expr for a binary operation. - /// \returns the class representing the binary operation of the expression, - /// or an error holding a diagnostic against \p SM otherwise. - Expected parseBinop(StringRef &Expr, - const SourceMgr &SM) const; + Expected> + parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const; + /// Parses \p Expr for a binary operation. The left operand of this binary + /// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether + /// we are parsing a legacy @LINE expression. \returns the class representing + /// the binary operation in the AST of the expression, or an error holding a + /// diagnostic against \p SM otherwise. + Expected> + parseBinop(StringRef &Expr, std::unique_ptr LeftOp, + bool IsLegacyLineExpr, const SourceMgr &SM) const; }; //===----------------------------------------------------------------------===// Index: llvm/trunk/lib/Support/FileCheck.cpp =================================================================== --- llvm/trunk/lib/Support/FileCheck.cpp +++ llvm/trunk/lib/Support/FileCheck.cpp @@ -35,17 +35,33 @@ Value = None; } -Expected FileCheckExpression::eval() const { - assert(LeftOp && "Evaluating an empty expression"); - Optional LeftOpValue = LeftOp->getValue(); - // Variable is undefined. - if (!LeftOpValue) - return make_error(LeftOp->getName()); - return EvalBinop(*LeftOpValue, RightOp); +Expected FileCheckNumericVariableUse::eval() const { + Optional Value = NumericVariable->getValue(); + if (Value) + return *Value; + return make_error(Name); +} + +Expected FileCheckASTBinop::eval() const { + Expected LeftOp = LeftOperand->eval(); + Expected RightOp = RightOperand->eval(); + + // Bubble up any error (e.g. undefined variables) in the recursive + // evaluation. + if (!LeftOp || !RightOp) { + Error Err = Error::success(); + if (!LeftOp) + Err = joinErrors(std::move(Err), LeftOp.takeError()); + if (!RightOp) + Err = joinErrors(std::move(Err), RightOp.takeError()); + return std::move(Err); + } + + return EvalBinop(*LeftOp, *RightOp); } Expected FileCheckNumericSubstitution::getResult() const { - Expected EvaluatedValue = Expression->eval(); + Expected EvaluatedValue = ExpressionAST->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); return utostr(*EvaluatedValue); @@ -63,15 +79,14 @@ return C == '_' || isalpha(C); } -Expected FileCheckPattern::parseVariable(StringRef &Str, - bool &IsPseudo, - const SourceMgr &SM) { +Expected +FileCheckPattern::parseVariable(StringRef &Str, const SourceMgr &SM) { if (Str.empty()) return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name"); bool ParsedOneChar = false; unsigned I = 0; - IsPseudo = Str[0] == '@'; + bool IsPseudo = Str[0] == '@'; // Global vars start with '$'. if (Str[0] == '$' || IsPseudo) @@ -89,7 +104,7 @@ StringRef Name = Str.take_front(I); Str = Str.substr(I); - return Name; + return VariableProperties {Name, IsPseudo}; } // StringRef holding all characters considered as horizontal whitespaces by @@ -111,13 +126,12 @@ FileCheckPattern::parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber, const SourceMgr &SM) { - bool IsPseudo; - Expected ParseVarResult = parseVariable(Expr, IsPseudo, SM); + Expected ParseVarResult = parseVariable(Expr, SM); if (!ParseVarResult) return ParseVarResult.takeError(); - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; - if (IsPseudo) + if (ParseVarResult->IsPseudo) return FileCheckErrorDiagnostic::get( SM, Name, "definition of pseudo numeric variable unsupported"); @@ -143,15 +157,9 @@ return DefinedNumericVariable; } -Expected -FileCheckPattern::parseNumericVariableUse(StringRef &Expr, +Expected> +FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, const SourceMgr &SM) const { - bool IsPseudo; - Expected ParseVarResult = parseVariable(Expr, IsPseudo, SM); - if (!ParseVarResult) - return ParseVarResult.takeError(); - StringRef Name = *ParseVarResult; - if (IsPseudo && !Name.equals("@LINE")) return FileCheckErrorDiagnostic::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -178,7 +186,32 @@ SM, Name, "numeric variable '" + Name + "' defined on the same line as used"); - return NumericVariable; + return llvm::make_unique(Name, NumericVariable); +} + +Expected> +FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const { + if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { + // Try to parse as a numeric variable use. + Expected ParseVarResult = + parseVariable(Expr, SM); + if (ParseVarResult) + return parseNumericVariableUse(ParseVarResult->Name, + ParseVarResult->IsPseudo, SM); + if (AO == AllowedOperand::LineVar) + return ParseVarResult.takeError(); + // Ignore the error and retry parsing as a literal. + consumeError(ParseVarResult.takeError()); + } + + // Otherwise, parse it as a literal. + uint64_t LiteralValue; + if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) + return llvm::make_unique(LiteralValue); + + return FileCheckErrorDiagnostic::get(SM, Expr, + "invalid operand format '" + Expr + "'"); } static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { @@ -189,20 +222,16 @@ return LeftOp - RightOp; } -Expected -FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const { - Expected LeftParseResult = - parseNumericVariableUse(Expr, SM); - if (!LeftParseResult) { - return LeftParseResult.takeError(); - } - FileCheckNumericVariable *LeftOp = *LeftParseResult; +Expected> +FileCheckPattern::parseBinop(StringRef &Expr, + std::unique_ptr LeftOp, + bool IsLegacyLineExpr, const SourceMgr &SM) const { + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) + return std::move(LeftOp); // Check if this is a supported operation and select a function to perform // it. - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) - return Context->makeExpression(add, LeftOp, 0); SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); char Operator = popFront(Expr); binop_eval_t EvalBinop; @@ -223,22 +252,24 @@ if (Expr.empty()) return FileCheckErrorDiagnostic::get(SM, Expr, "missing operand in expression"); - uint64_t RightOp; - if (Expr.consumeInteger(10, RightOp)) - return FileCheckErrorDiagnostic::get( - SM, Expr, "invalid offset in expression '" + Expr + "'"); - Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) - return FileCheckErrorDiagnostic::get( - SM, Expr, "unexpected characters at end of expression '" + Expr + "'"); + // The second operand in a legacy @LINE expression is always a literal. + AllowedOperand AO = + IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; + Expected> RightOpResult = + parseNumericOperand(Expr, AO, SM); + if (!RightOpResult) + return RightOpResult; - return Context->makeExpression(EvalBinop, LeftOp, RightOp); + Expr = Expr.ltrim(SpaceChars); + return llvm::make_unique(EvalBinop, std::move(LeftOp), + std::move(*RightOpResult)); } -Expected FileCheckPattern::parseNumericSubstitutionBlock( +Expected> +FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, - const SourceMgr &SM) const { + bool IsLegacyLineExpr, const SourceMgr &SM) const { // Parse the numeric variable definition. DefinedNumericVariable = None; size_t DefEnd = Expr.find(':'); @@ -259,12 +290,29 @@ return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; - return Context->makeExpression(add, nullptr, 0); + return nullptr; } // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + // The first operand in a legacy @LINE expression is always the @LINE pseudo + // variable. + AllowedOperand AO = + IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; + Expected> ParseResult = + parseNumericOperand(Expr, AO, SM); + while (ParseResult && !Expr.empty()) { + ParseResult = + parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM); + // Legacy @LINE expressions only allow 2 operands. + if (ParseResult && IsLegacyLineExpr && !Expr.empty()) + return FileCheckErrorDiagnostic::get( + SM, Expr, + "unexpected characters at end of expression '" + Expr + "'"); + } + if (!ParseResult) + return ParseResult; + return std::move(*ParseResult); } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -375,12 +423,15 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsDefinition = false; + // Whether the substitution block is a legacy use of @LINE with string + // substitution block syntax. + bool IsLegacyLineExpr = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; size_t SubstInsertIdx = RegExStr.size(); - // Parse string variable or legacy expression. + // Parse string variable or legacy @LINE expression. if (!IsNumBlock) { size_t VarEndIdx = MatchStr.find(":"); size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); @@ -391,15 +442,15 @@ } // Get the name (e.g. "foo") and verify it is well formed. - bool IsPseudo; StringRef OrigMatchStr = MatchStr; - Expected ParseVarResult = - parseVariable(MatchStr, IsPseudo, SM); + Expected ParseVarResult = + parseVariable(MatchStr, SM); if (!ParseVarResult) { logAllUnhandledErrors(ParseVarResult.takeError(), errs()); return true; } - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; + bool IsPseudo = ParseVarResult->IsPseudo; IsDefinition = (VarEndIdx != StringRef::npos); if (IsDefinition) { @@ -424,23 +475,24 @@ } else { if (IsPseudo) { MatchStr = OrigMatchStr; - IsNumBlock = true; + IsLegacyLineExpr = IsNumBlock = true; } else SubstStr = Name; } } // Parse numeric substitution block. - FileCheckExpression *Expression; + std::unique_ptr ExpressionAST; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected ParseResult = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM); + Expected> ParseResult = + parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, + IsLegacyLineExpr, SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - Expression = *ParseResult; + ExpressionAST = std::move(*ParseResult); if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); @@ -468,8 +520,8 @@ // previous CHECK patterns, and substitution of expressions. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, Expression, - SubstInsertIdx) + ? Context->makeNumericSubstitution( + SubstStr, std::move(ExpressionAST), SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -660,7 +712,7 @@ Expected MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { bool UndefSeen = false; handleAllErrors(MatchedValue.takeError(), @@ -669,13 +721,11 @@ [](const FileCheckErrorDiagnostic &E) {}, [&](const FileCheckUndefVarError &E) { if (!UndefSeen) { - OS << "uses undefined variable "; + OS << "uses undefined variable(s):"; UndefSeen = true; } + OS << " "; E.log(OS); - }, - [](const ErrorInfoBase &E) { - llvm_unreachable("Unexpected error"); }); } else { // Substitution succeeded. Print substituted value. @@ -768,15 +818,6 @@ return VarIter->second; } -FileCheckExpression * -FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - Expressions.push_back(llvm::make_unique( - EvalBinop, OperandLeft, OperandRight)); - return Expressions.back().get(); -} - template FileCheckNumericVariable * FileCheckPatternContext::makeNumericVariable(Types... args) { @@ -794,10 +835,10 @@ } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, FileCheckExpression *Expression, - size_t InsertIdx) { + StringRef ExpressionStr, + std::unique_ptr ExpressionAST, size_t InsertIdx) { Substitutions.push_back(llvm::make_unique( - this, ExpressionStr, Expression, InsertIdx)); + this, ExpressionStr, std::move(ExpressionAST), InsertIdx)); return Substitutions.back().get(); } @@ -1777,9 +1818,8 @@ std::pair CmdlineNameVal = CmdlineDef.split('='); StringRef CmdlineName = CmdlineNameVal.first; StringRef OrigCmdlineName = CmdlineName; - bool IsPseudo; - Expected ParseVarResult = - FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM); + Expected ParseVarResult = + FileCheckPattern::parseVariable(CmdlineName, SM); if (!ParseVarResult) { Errs = joinErrors(std::move(Errs), ParseVarResult.takeError()); continue; @@ -1787,7 +1827,7 @@ // Check that CmdlineName does not denote a pseudo variable is only // composed of the parsed numeric variable. This catches cases like // "FOO+2" in a "FOO+2=10" definition. - if (IsPseudo || !CmdlineName.empty()) { + if (ParseVarResult->IsPseudo || !CmdlineName.empty()) { Errs = joinErrors(std::move(Errs), FileCheckErrorDiagnostic::get( SM, OrigCmdlineName, @@ -1795,7 +1835,7 @@ OrigCmdlineName + "'")); continue; } - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; // Detect collisions between string and numeric variables when the former // is created later than the latter. Index: llvm/trunk/test/FileCheck/line-count.txt =================================================================== --- llvm/trunk/test/FileCheck/line-count.txt +++ llvm/trunk/test/FileCheck/line-count.txt @@ -50,7 +50,7 @@ 50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*' 51 52 BAD10: [[@LINE-x]] -53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in expression 'x' +53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x' 54 55 BAD11: [[@LINE-1x]] 56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x' Index: llvm/trunk/test/FileCheck/numeric-expression.txt =================================================================== --- llvm/trunk/test/FileCheck/numeric-expression.txt +++ llvm/trunk/test/FileCheck/numeric-expression.txt @@ -59,8 +59,8 @@ 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. +; Numeric expressions using variables defined on other lines and an immediate +; interpreted as an unsigned value. ; Note: 9223372036854775819 = 0x8000000000000000 + 11 ; 9223372036854775808 = 0x8000000000000000 USE UNSIGNED IMM @@ -68,21 +68,29 @@ CHECK-LABEL: USE UNSIGNED IMM CHECK-NEXT: [[#VAR1+9223372036854775808]] -; Numeric expression using undefined variable. +; Numeric expressions using more than one variable defined on other lines. +USE MULTI VAR +31 +42 +CHECK-LABEL: USE MULTI VAR +CHECK-NEXT: [[#VAR2:]] +CHECK-NEXT: [[#VAR1+VAR2]] + +; Numeric expression using undefined variables. RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s UNDEF VAR USE UNDEFVAR: 11 UNDEF-USE-LABEL: UNDEF VAR USE -UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]] +UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]] UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:17: error: {{U}}NDEF-USE-NEXT: expected string not found in input -UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}} +UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} UNDEF-USE-MSG-NEXT: {{^ \^$}} UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-6]]:1: note: scanning from here UNDEF-USE-MSG-NEXT: UNDEFVAR: 11 UNDEF-USE-MSG-NEXT: {{^\^$}} -UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable "UNDEFVAR" +UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2" UNDEF-USE-MSG-NEXT: UNDEFVAR: 11 UNDEF-USE-MSG-NEXT: {{^\^$}} Index: llvm/trunk/test/FileCheck/var-scope.txt =================================================================== --- llvm/trunk/test/FileCheck/var-scope.txt +++ llvm/trunk/test/FileCheck/var-scope.txt @@ -34,5 +34,5 @@ GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]] ERRUNDEF: expected string not found in input -ERRUNDEFLOCAL: uses undefined variable "LOCAL" -ERRUNDEFLOCNUM: uses undefined variable "LOCNUM" +ERRUNDEFLOCAL: uses undefined variable(s): "LOCAL" +ERRUNDEFLOCNUM: uses undefined variable(s): "LOCNUM" Index: llvm/trunk/unittests/Support/FileCheckTest.cpp =================================================================== --- llvm/trunk/unittests/Support/FileCheckTest.cpp +++ llvm/trunk/unittests/Support/FileCheckTest.cpp @@ -8,56 +8,112 @@ #include "llvm/Support/FileCheck.h" #include "gtest/gtest.h" +#include using namespace llvm; namespace { class FileCheckTest : public ::testing::Test {}; +TEST_F(FileCheckTest, Literal) { + // Eval returns the literal's value. + FileCheckExpressionLiteral Ten(10); + Expected Value = Ten.eval(); + EXPECT_TRUE(bool(Value)); + EXPECT_EQ(10U, *Value); + + // Max value can be correctly represented. + FileCheckExpressionLiteral Max(std::numeric_limits::max()); + Value = Max.eval(); + EXPECT_TRUE(bool(Value)); + EXPECT_EQ(std::numeric_limits::max(), *Value); +} + +static std::string toString(const std::unordered_set &Set) { + bool First = true; + std::string Str; + for (StringRef S : Set) { + Str += Twine(First ? "{" + S : ", " + S).str(); + First = false; + } + Str += '}'; + return Str; +} + +static void +expectUndefErrors(std::unordered_set ExpectedUndefVarNames, + Error Err) { + handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) { + ExpectedUndefVarNames.erase(E.getVarName()); + }); + EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames); +} + +static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) { + expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err)); +} + TEST_F(FileCheckTest, NumericVariable) { - // Undefined variable: getValue fails, setValue does not trigger assert. + // Undefined variable: getValue and eval fail, error returned by eval holds + // the name of the undefined variable and setValue does not trigger assert. FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO"); EXPECT_EQ("FOO", FooVar.getName()); - llvm::Optional Value = FooVar.getValue(); - EXPECT_FALSE(Value); - FooVar.clearValue(); + FileCheckNumericVariableUse FooVarUse = + FileCheckNumericVariableUse("FOO", &FooVar); + EXPECT_FALSE(FooVar.getValue()); + Expected EvalResult = FooVarUse.eval(); + EXPECT_FALSE(EvalResult); + expectUndefError("FOO", EvalResult.takeError()); FooVar.setValue(42); - // Defined variable: getValue returns value set. - Value = FooVar.getValue(); - EXPECT_TRUE(Value); + // Defined variable: getValue and eval return value set. + Optional Value = FooVar.getValue(); + EXPECT_TRUE(bool(Value)); EXPECT_EQ(42U, *Value); + EvalResult = FooVarUse.eval(); + EXPECT_TRUE(bool(EvalResult)); + EXPECT_EQ(42U, *EvalResult); - // Clearing variable: getValue fails. + // Clearing variable: getValue and eval fail. Error returned by eval holds + // the name of the cleared variable. FooVar.clearValue(); Value = FooVar.getValue(); EXPECT_FALSE(Value); + EvalResult = FooVarUse.eval(); + EXPECT_FALSE(EvalResult); + expectUndefError("FOO", EvalResult.takeError()); } uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } -static void expectUndefError(const Twine &ExpectedStr, Error Err) { - handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) { - EXPECT_EQ(ExpectedStr.str(), E.getVarName()); - }); -} - -TEST_F(FileCheckTest, Expression) { +TEST_F(FileCheckTest, Binop) { FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42); - FileCheckExpression Expression = FileCheckExpression(doAdd, &FooVar, 18); + std::unique_ptr FooVarUse = + llvm::make_unique("FOO", &FooVar); + FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 18); + std::unique_ptr BarVarUse = + llvm::make_unique("BAR", &BarVar); + FileCheckASTBinop Binop = + FileCheckASTBinop(doAdd, std::move(FooVarUse), std::move(BarVarUse)); // Defined variable: eval returns right value. - Expected Value = Expression.eval(); + Expected Value = Binop.eval(); EXPECT_TRUE(bool(Value)); EXPECT_EQ(60U, *Value); - // Undefined variable: eval fails, undefined variable returned. We call - // getUndefVarName first to check that it can be called without calling - // eval() first. + // 1 undefined variable: eval fails, error contains name of undefined + // variable. FooVar.clearValue(); - Error EvalError = Expression.eval().takeError(); - EXPECT_TRUE(errorToBool(std::move(EvalError))); - expectUndefError("FOO", std::move(EvalError)); + Value = Binop.eval(); + EXPECT_FALSE(Value); + expectUndefError("FOO", Value.takeError()); + + // 2 undefined variables: eval fails, error contains names of all undefined + // variables. + BarVar.clearValue(); + Value = Binop.eval(); + EXPECT_FALSE(Value); + expectUndefErrors({"FOO", "BAR"}, Value.takeError()); } TEST_F(FileCheckTest, ValidVarNameStart) { @@ -84,77 +140,69 @@ SourceMgr SM; StringRef OrigVarName = bufferize(SM, "GoodVar42"); StringRef VarName = OrigVarName; - bool IsPseudo = true; - Expected ParsedName = - FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); - EXPECT_EQ(*ParsedName, OrigVarName); + Expected ParsedVarResult = + FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); + EXPECT_EQ(ParsedVarResult->Name, OrigVarName); EXPECT_TRUE(VarName.empty()); - EXPECT_FALSE(IsPseudo); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); - EXPECT_EQ(*ParsedName, OrigVarName); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); + EXPECT_EQ(ParsedVarResult->Name, OrigVarName); EXPECT_TRUE(VarName.empty()); - EXPECT_FALSE(IsPseudo); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); - EXPECT_EQ(*ParsedName, OrigVarName); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); + EXPECT_EQ(ParsedVarResult->Name, OrigVarName); EXPECT_TRUE(VarName.empty()); - EXPECT_TRUE(IsPseudo); + EXPECT_TRUE(ParsedVarResult->IsPseudo); VarName = bufferize(SM, "42BadVar"); - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(errorToBool(ParsedName.takeError())); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(errorToBool(ParsedVarResult.takeError())); VarName = bufferize(SM, "$@"); - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(errorToBool(ParsedName.takeError())); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(errorToBool(ParsedVarResult.takeError())); VarName = OrigVarName = bufferize(SM, "B@dVar"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); EXPECT_EQ(VarName, OrigVarName.substr(1)); - EXPECT_EQ(*ParsedName, "B"); - EXPECT_FALSE(IsPseudo); + EXPECT_EQ(ParsedVarResult->Name, "B"); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = OrigVarName = bufferize(SM, "B$dVar"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); EXPECT_EQ(VarName, OrigVarName.substr(1)); - EXPECT_EQ(*ParsedName, "B"); - EXPECT_FALSE(IsPseudo); + EXPECT_EQ(ParsedVarResult->Name, "B"); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = bufferize(SM, "BadVar+"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); EXPECT_EQ(VarName, "+"); - EXPECT_EQ(*ParsedName, "BadVar"); - EXPECT_FALSE(IsPseudo); + EXPECT_EQ(ParsedVarResult->Name, "BadVar"); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = bufferize(SM, "BadVar-"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); EXPECT_EQ(VarName, "-"); - EXPECT_EQ(*ParsedName, "BadVar"); - EXPECT_FALSE(IsPseudo); + EXPECT_EQ(ParsedVarResult->Name, "BadVar"); + EXPECT_FALSE(ParsedVarResult->IsPseudo); VarName = bufferize(SM, "BadVar:"); - IsPseudo = true; - ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); - EXPECT_TRUE(bool(ParsedName)); + ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM); + EXPECT_TRUE(bool(ParsedVarResult)); EXPECT_EQ(VarName, ":"); - EXPECT_EQ(*ParsedName, "BadVar"); - EXPECT_FALSE(IsPseudo); + EXPECT_EQ(ParsedVarResult->Name, "BadVar"); + EXPECT_FALSE(ParsedVarResult->IsPseudo); } class PatternTester { @@ -197,7 +245,7 @@ StringRef ExprBufferRef = bufferize(SM, Expr); Optional DefinedNumericVariable; return errorToBool(P.parseNumericSubstitutionBlock( - ExprBufferRef, DefinedNumericVariable, SM) + ExprBufferRef, DefinedNumericVariable, false, SM) .takeError()); } @@ -269,15 +317,12 @@ // Missing offset operand. EXPECT_TRUE(Tester.parseSubstExpect("@LINE+")); - // Cannot parse offset operand. - EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x")); - - // Unexpected string at end of numeric expression. - EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x")); - // Valid expression. EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5")); EXPECT_FALSE(Tester.parseSubstExpect("FOO+4")); + Tester.initNextPattern(); + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]")); } TEST_F(FileCheckTest, ParsePattern) { @@ -306,7 +351,6 @@ 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. @@ -365,35 +409,37 @@ // the right value. FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10); - FileCheckExpression LineExpression = FileCheckExpression(doAdd, &LineVar, 0); - FileCheckExpression NExpression = FileCheckExpression(doAdd, &NVar, 3); - FileCheckNumericSubstitution SubstitutionLine = - FileCheckNumericSubstitution(&Context, "@LINE", &LineExpression, 12); + auto LineVarUse = + llvm::make_unique("@LINE", &LineVar); + auto NVarUse = llvm::make_unique("N", &NVar); + FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution( + &Context, "@LINE", std::move(LineVarUse), 12); FileCheckNumericSubstitution SubstitutionN = - FileCheckNumericSubstitution(&Context, "N", &NExpression, 30); - Expected Value = SubstitutionLine.getResult(); - EXPECT_TRUE(bool(Value)); - EXPECT_EQ("42", *Value); - Value = SubstitutionN.getResult(); - EXPECT_TRUE(bool(Value)); - EXPECT_EQ("13", *Value); + FileCheckNumericSubstitution(&Context, "N", std::move(NVarUse), 30); + SubstValue = SubstitutionLine.getResult(); + EXPECT_TRUE(bool(SubstValue)); + EXPECT_EQ("42", *SubstValue); + SubstValue = SubstitutionN.getResult(); + EXPECT_TRUE(bool(SubstValue)); + EXPECT_EQ("10", *SubstValue); - // Substitution of an undefined numeric variable fails. + // Substitution of an undefined numeric variable fails, error holds name of + // undefined variable. LineVar.clearValue(); - SubstValue = SubstitutionLine.getResult().takeError(); + SubstValue = SubstitutionLine.getResult(); EXPECT_FALSE(bool(SubstValue)); expectUndefError("@LINE", SubstValue.takeError()); NVar.clearValue(); - SubstValue = SubstitutionN.getResult().takeError(); + SubstValue = SubstitutionN.getResult(); EXPECT_FALSE(bool(SubstValue)); expectUndefError("N", SubstValue.takeError()); // Substitution of a defined string variable returns the right value. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); - Value = StringSubstitution.getResult(); - EXPECT_TRUE(bool(Value)); - EXPECT_EQ("BAR", *Value); + SubstValue = StringSubstitution.getResult(); + EXPECT_TRUE(bool(SubstValue)); + EXPECT_EQ("BAR", *SubstValue); } TEST_F(FileCheckTest, FileCheckContext) { @@ -456,14 +502,15 @@ Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); Optional DefinedNumericVariable; - Expected Expression = P.parseNumericSubstitutionBlock( - LocalNumVarRef, DefinedNumericVariable, SM); - Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + Expected> ExpressionAST = + P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, + /*IsLegacyLineExpr=*/false, SM); EXPECT_TRUE(bool(LocalVar)); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(bool(Expression)); - Expected ExpressionVal = (*Expression)->eval(); + Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); + Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + EXPECT_TRUE(bool(ExpressionAST)); + Expected ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 18U); EXPECT_TRUE(bool(EmptyVar)); @@ -478,12 +525,12 @@ // local variables, if it was created before. This is important because local // variable clearing due to --enable-var-scope happens after numeric // expressions are linked to the numeric variables they use. - EXPECT_TRUE(errorToBool((*Expression)->eval().takeError())); + EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - Expression = P.parseNumericSubstitutionBlock(LocalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(bool(Expression)); - ExpressionVal = (*Expression)->eval(); + ExpressionAST = P.parseNumericSubstitutionBlock( + LocalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); + EXPECT_TRUE(bool(ExpressionAST)); + ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_TRUE(errorToBool(EmptyVar.takeError())); @@ -501,10 +548,10 @@ EXPECT_TRUE(bool(GlobalVar)); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(bool(Expression)); - ExpressionVal = (*Expression)->eval(); + ExpressionAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); + EXPECT_TRUE(bool(ExpressionAST)); + ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); @@ -512,10 +559,10 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(bool(Expression)); - ExpressionVal = (*Expression)->eval(); + ExpressionAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM); + EXPECT_TRUE(bool(ExpressionAST)); + ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); }