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 @@ -105,10 +105,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 ``VAR`` to ```` that can be used - in ``CHECK:`` lines. + Sets a filecheck numeric variable ``VAR`` 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 @@ -585,16 +587,12 @@ would match ``mov r5, 42`` and set ``REG`` to the value ``5``. The syntax to check a numeric expression constraint is -``[[#]]`` where: +``[[#]]`` where: -*```` is the name of a numeric variable defined on a previous line. - -*```` is an optional numeric operation to perform on the value of -````. Currently supported numeric operations are ``+`` and ``-``. - -*```` is the immediate value that constitutes the second operand of -the numeric operation . It must be present if ```` is present, -absent otherwise. +``>`` is a numeric expression whose operands are either numeric + variables previously defined or integer literals. Currently supported + numeric operations are ``+`` and ``-``. A single numeric variable or + integer literal is also accepted. 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 @@ -40,29 +40,57 @@ // Numeric expression handling code. //===----------------------------------------------------------------------===// -class FileCheckASTBinop; +/// Base class representing the AST of a given numeric expression. +class FileCheckNumExprAST { +public: + virtual ~FileCheckNumExprAST() = default; + + /// Evaluate the value of the expression represented by this AST. + virtual llvm::Optional eval() const = 0; + + /// Append names of undefined variables used in the expression represented by + /// this AST. Must be overriden in any subclass representing an expression + /// that can contain a variable. + virtual void + appendUndefVarNames(std::vector &UndefVarNames) const {} +}; + +/// Class representing a literal in the AST of a numeric expression. +class FileCheckNumExprLiteral : public FileCheckNumExprAST { +private: + /// Actual value of the literal. + uint64_t Value; + +public: + /// Constructor for an unsigned literal. + FileCheckNumExprLiteral(uint64_t Val) : Value(Val) {} + + /// Evaluate the value of the expression represented by this AST. Therefore, + /// return the literal's value. + llvm::Optional eval() const { return Value; } +}; -/// Class representing a numeric expression. +/// Class representing a numeric expression and its matching format. class FileCheckNumExpr { private: /// Pointer to AST of the numeric expression. - std::unique_ptr AST; + std::shared_ptr AST; public: /// Generic constructor for a numeric expression whose equality constraint is /// represented by \p AST. - FileCheckNumExpr(FileCheckASTBinop *AST) : AST(std::move(AST)) {} + FileCheckNumExpr(std::shared_ptr AST) : AST(AST) {} /// Return pointer to AST of the numeric expression. Pointer is guaranteed to /// be valid as long as this object is. - FileCheckASTBinop *getAST() const { return AST.get(); } + FileCheckNumExprAST *getAST() const { return AST.get(); } }; /// Class representing a numeric variable with a given value in the AST of a /// numeric expression. Each definition of a variable gets its own instance of /// this class, variable uses share the same instance as the respective /// definition. -class FileCheckNumVar { +class FileCheckNumVar : public FileCheckNumExprAST { private: /// Name of the numeric variable. StringRef Name; @@ -90,8 +118,12 @@ /// Return name of that numeric variable. StringRef getName() const { return Name; } - /// Return value of this numeric variable. - llvm::Optional getValue() const; + /// Evaluate the value of the expression represented by this AST. Therefore, + /// return this variable's value. + llvm::Optional eval() const; + + /// Append numeric variable's name to UndefVarNames if undefined. + void appendUndefVarNames(std::vector &UndefVarNames) const; /// Set value of this numeric variable if not defined. Return whether /// the variable was already defined. @@ -110,29 +142,31 @@ /// Class representing a single binary operation in the AST of a numeric /// expression. -class FileCheckASTBinop { +class FileCheckASTBinop : public FileCheckNumExprAST { private: /// Left operand. - FileCheckNumVar *Opl; + std::shared_ptr Opl; /// Right operand. - uint64_t Opr; + std::shared_ptr Opr; /// Pointer to function that can evaluate this binary operation. binop_eval_t EvalBinop; public: - FileCheckASTBinop(binop_eval_t EvalBinop, FileCheckNumVar *OperandLeft, - uint64_t OperandRight) + FileCheckASTBinop(binop_eval_t EvalBinop, + std::shared_ptr OperandLeft, + std::shared_ptr OperandRight) : Opl(OperandLeft), Opr(OperandRight), EvalBinop(EvalBinop) {} /// Evaluate the value of the binary operation represented by this AST. Uses - /// EvalBinop to perform the binary operation. + /// EvalBinop to perform the binary operation on the values of recursively + /// evaluating the left and right operands. llvm::Optional eval() const; - /// Return the name of the undefined variable used in this substitution if - /// any or an empty string otherwise. - StringRef getUndefVarName() const; + /// Append names of undefined variables used in any of the operands of this + /// binary operation. + void appendUndefVarNames(std::vector &UndefVarNames) const; }; class FileCheckPatternContext; @@ -189,9 +223,9 @@ /// its definition matched. llvm::Optional getSubstitute() const; - /// Return the name of the undefined variable used in this substitution if - /// any or an empty string otherwise. - StringRef getUndefVarName() const; + /// Return in \p UndefVarNames the name of the undefined variables used in + /// this substitution if any. + void getUndefVarNames(std::vector &UndefVarNames) const; }; //===----------------------------------------------------------------------===// @@ -276,17 +310,13 @@ /// for a given variable is recorded in this table. When matching a pattern /// all definitions for that pattern are recorded in NumericVariableDefs /// table. - StringMap GlobalNumericVariableTable; + StringMap> GlobalNumericVariableTable; /// Vector holding pointers to all numeric expressions parsed. Used to /// automatically free the numeric expressions once they are guaranteed to no /// longer be used. std::vector> NumExprs; - /// Vector holding pointers to all numeric variables parsed. Used to - /// automatically free them once they are guaranteed to no longer be used. - std::vector> NumVars; - public: /// Return the value of pattern variable \p VarName or None if no such /// variable has been defined. @@ -305,11 +335,7 @@ private: /// Register a numeric expression for destruction when the context is /// destroyed. - FileCheckNumExpr *registerNumExpr(FileCheckASTBinop *AST); - - /// Make a new numeric variable and register it for destruction when the - /// context is destroyed. - template FileCheckNumVar *makeNumVar(Types... args); + FileCheckNumExpr *registerNumExpr(std::shared_ptr AST); }; class FileCheckPattern { @@ -385,17 +411,20 @@ static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); /// Parse \p Expr for use or definition (if \p IsDefinition is true) of a /// numeric variable. Return whether parsing fails in which case errors are - /// reported on \p SM. Otherwise, the name of the numeric variable is set in - /// \p Name. + /// reported on \p SM, unless \p AcceptFail is true and the error is in + /// parsing the variable name. Otherwise, the name of the numeric variable is + /// set in \p Name. bool parseNumericVariable(StringRef &Expr, StringRef &Name, bool IsDefinition, - const SourceMgr &SM) const; - /// Parse \p Expr for a numeric expression. Return the class representing the - /// AST of numeric expression or nullptr if parsing fails in which case - /// errors are reported on \p SM. Set \p NumVarDef to the pointer to the - /// class representing the variable defined to this numeric expression if - /// any. + bool AcceptFail, const SourceMgr &SM) const; + /// Parse \p Expr for a numeric expression. Parameter \p Legacy indicates + /// whether Expr should be a legacy numeric expression. Return the class + /// representing the AST of numeric expression or nullptr if parsing fails + /// in which case errors are reported on \p SM. Set \p NumVarDef to the + /// pointer to the class representing the variable defined to this numeric + /// expression if any. FileCheckNumExpr *parseNumericExpression(StringRef Expr, FileCheckNumVar *&NumVarDef, + bool Legacy, const SourceMgr &SM) const; bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, const FileCheckRequest &Req); @@ -420,8 +449,24 @@ size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); /// Numeric expression parsing helpers. - FileCheckASTBinop *parseFileCheckBinop(StringRef &Expr, - const SourceMgr &SM) const; + + enum AllowedOperand { LegacyVar, LegacyLiteral, Any }; + /// Parse \p Expr for use of a numeric operand. Accept both literal values + /// and numeric variables, depending on the value of \p AllowedOperandFlag. + /// Return the class representing that operand in the AST of the numeric + /// expression or nullptr if parsing fails in which case errors are reported + /// on \p SM. + std::shared_ptr + parseNumericOperand(StringRef &Expr, enum AllowedOperand AO, + const SourceMgr &SM) const; + /// Parse \p Expr for a binary operation. The left operand of this binary + /// operation is given in \p Opl and \p Legacy indicates whether we are + /// parsing a legacy numeric expression. Return the class representing this + /// binary operation in the AST of the numeric expression or nullptr if + /// parsing fails in which case errors are reported on \p SM. + std::shared_ptr + parseFileCheckBinop(StringRef &Expr, std::shared_ptr Opl, + bool Legacy, 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 @@ -24,12 +24,18 @@ using namespace llvm; -llvm::Optional FileCheckNumVar::getValue() const { +llvm::Optional FileCheckNumVar::eval() const { if (!Defined) return llvm::None; return Value; } +void FileCheckNumVar::appendUndefVarNames( + std::vector &UndefVarNames) const { + if (!Defined) + UndefVarNames.emplace_back(Name); +} + bool FileCheckNumVar::setValue(uint64_t Value) { if (Defined) return true; @@ -46,17 +52,22 @@ } llvm::Optional FileCheckASTBinop::eval() const { - llvm::Optional Opl = this->Opl->getValue(); - // Variable has been undefined. + llvm::Optional Opl = this->Opl->eval(); + llvm::Optional Opr = this->Opr->eval(); + + // Uses undefined variable. if (!Opl) return llvm::None; - return EvalBinop(*Opl, Opr); + if (!Opr) + return llvm::None; + + return EvalBinop(*Opl, *Opr); } -StringRef FileCheckASTBinop::getUndefVarName() const { - if (!Opl->getValue()) - return Opl->getName(); - return StringRef(); +void FileCheckASTBinop::appendUndefVarNames( + std::vector &UndefVarNames) const { + Opl->appendUndefVarNames(UndefVarNames); + Opr->appendUndefVarNames(UndefVarNames); } llvm::Optional FileCheckPatternSubst::getSubstitute() const { @@ -77,16 +88,16 @@ } } -StringRef FileCheckPatternSubst::getUndefVarName() const { +void FileCheckPatternSubst::getUndefVarNames( + std::vector &UndefVarNames) const { + UndefVarNames.clear(); if (IsNumExpr) // Although use of undefined numeric variable is tested at parse time, // numeric variable can get undefined later by ClearLocalVariables. - return NumExpr->getAST()->getUndefVarName(); + NumExpr->getAST()->appendUndefVarNames(UndefVarNames); else { if (!Context->getPatternVarValue(SubstStr)) - return SubstStr; - - return StringRef(); + UndefVarNames.emplace_back(SubstStr); } } @@ -132,14 +143,15 @@ } bool FileCheckPattern::parseNumericVariable(StringRef &Expr, StringRef &Name, - bool IsDefinition, + bool IsDefinition, bool AcceptFail, const SourceMgr &SM) const { bool IsPseudo; unsigned TrailIdx; if (parseVariable(Expr, IsPseudo, TrailIdx)) { - SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, - "invalid variable name"); + if (!AcceptFail) + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid variable name"); return true; } Name = Expr.substr(0, TrailIdx); @@ -182,7 +194,7 @@ return true; } - FileCheckNumVar *NumVar = VarTableIter->second; + FileCheckNumVar *NumVar = VarTableIter->second.get(); if (!IsPseudo && NumVar->getDefLineNumber() == LineNumber) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "numeric variable '" + NumVar->getName() + @@ -194,28 +206,45 @@ } } +std::shared_ptr +FileCheckPattern::parseNumericOperand(StringRef &Expr, enum AllowedOperand AO, + const SourceMgr &SM) const { + + // Try to parse as a numeric variable use. + if (AO == LegacyVar || AO == Any) { + StringRef Name; + // Error reporting done in ParseNumericVariable. + if (!parseNumericVariable(Expr, Name, false /*IsDefinition*/, + true /*AcceptFail*/, SM)) { + std::shared_ptr NumVar = + Context->GlobalNumericVariableTable.find(Name)->second; + return NumVar; + } + } + + // Otherwise, parse it as a literal. + if (AO != LegacyLiteral && AO != Any) + return nullptr; + uint64_t LiteralValue; + if (Expr.consumeInteger(10, LiteralValue)) + return nullptr; + return std::make_shared(LiteralValue); +} + static uint64_t doAdd(uint64_t Opl, uint64_t Opr) { return Opl + Opr; } static uint64_t doSub(uint64_t Opl, uint64_t Opr) { return Opl - Opr; } -/// Parse \p Expr for a binary operation. -/// Return the class representing that binary operation in the AST of the -/// numeric expression or nullptr if parsing fails in which case errors -/// are reported on \p SM. -FileCheckASTBinop * +std::shared_ptr FileCheckPattern::parseFileCheckBinop(StringRef &Expr, - const SourceMgr &SM) const { - StringRef Name; - if (parseNumericVariable(Expr, Name, false, SM)) - // Error reporting done in parseNumericVariable. - return nullptr; - FileCheckNumVar *Opl = Context->GlobalNumericVariableTable.find(Name)->second; + std::shared_ptr Opl, + bool Legacy, const SourceMgr &SM) const { + skipWhitespace(Expr); + if (Expr.empty()) + return Opl; // Check if this is a supported operation and selection function to perform // it. - skipWhitespace(Expr); - if (Expr.empty()) - return new FileCheckASTBinop(doAdd, Opl, 0); SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); char Operator = popFront(Expr); binop_eval_t EvalBinop; @@ -240,10 +269,12 @@ "missing operand in numeric expression"); return nullptr; } - uint64_t Opr; - if (Expr.consumeInteger(10, Opr)) { + // Second operand in legacy numeric expression is a literal. + enum AllowedOperand AO = Legacy ? LegacyLiteral : Any; + std::shared_ptr Opr = parseNumericOperand(Expr, AO, SM); + if (Opr == nullptr) { SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, - "invalid offset in numeric expression '" + Expr + "'"); + "invalid operand format '" + Expr + "'"); return nullptr; } skipWhitespace(Expr); @@ -254,16 +285,19 @@ return nullptr; } - return new FileCheckASTBinop(EvalBinop, Opl, Opr); + return std::make_shared(EvalBinop, Opl, Opr); } -/// Parse \p Expr for a numeric expression. Return the class representing the -/// AST of numeric expression or nullptr if parsing fails in which case errors -/// are reported on \p SM. Set \p NumVarDef to the pointer to the class -/// representing the variable defined to this numeric expression if any. +/// Parse \p Expr for a numeric expression. Parameter \p Legacy indicates +/// whether Expr should be a legacy numeric expression. Return the class +/// representing the AST of numeric expression or nullptr if parsing fails +/// in which case errors are reported on \p SM. Set \p NumVarDef to the +/// pointer to the class representing the variable defined to this numeric +/// expression if any. FileCheckNumExpr *FileCheckPattern::parseNumericExpression( - StringRef Expr, FileCheckNumVar *&NumVarDef, const SourceMgr &SM) const { - FileCheckASTBinop *NumExprAST = nullptr; + StringRef Expr, FileCheckNumVar *&NumVarDef, bool Legacy, + const SourceMgr &SM) const { + std::shared_ptr NumExprAST; // Parse numeric variable definition. NumVarDef = nullptr; @@ -274,11 +308,12 @@ skipWhitespace(DefExpr); StringRef Name; - if (parseNumericVariable(DefExpr, Name, true /*IsDefinition*/, SM)) + if (parseNumericVariable(DefExpr, Name, true /*IsDefinition*/, + false /*AcceptFail*/, SM)) // Invalid variable definition. Error reporting done in parsing function. return nullptr; - NumVarDef = Context->makeNumVar(Name, this->LineNumber); + NumVarDef = new FileCheckNumVar(Name, this->LineNumber); skipWhitespace(DefExpr); if (!DefExpr.empty()) { @@ -297,9 +332,24 @@ } else { // Parse numeric expression itself. skipWhitespace(Expr); - NumExprAST = parseFileCheckBinop(Expr, SM); - if (NumExprAST == nullptr) + // First operand in legacy numeric expression is the @LINE pseudo variable. + enum AllowedOperand AO = Legacy ? LegacyVar : Any; + NumExprAST = parseNumericOperand(Expr, AO, SM); + while (NumExprAST != nullptr && !Expr.empty()) { + NumExprAST = parseFileCheckBinop(Expr, NumExprAST, Legacy, SM); + // Legacy numeric expressions only allow 2 operands. + if (Legacy && !Expr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "unexpected characters at end of numeric expression '" + + Expr + "'"); + return nullptr; + } + } + if (NumExprAST == nullptr) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid operand format '" + Expr + "'"); return nullptr; + } } return Context->registerNumExpr(NumExprAST); @@ -321,7 +371,8 @@ // Create fake @LINE pseudo variable definition. StringRef LinePseudo = "@LINE"; uint64_t LineNumber64 = this->LineNumber; - auto LinePseudoVar = Context->makeNumVar(LinePseudo, LineNumber64); + auto LinePseudoVar = + std::make_shared(LinePseudo, LineNumber64); Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar; if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) @@ -425,6 +476,7 @@ PatternStr = PatternStr.substr(End + 4 + (int)IsNumExpr); bool IsVarDef; + bool Legacy = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; @@ -454,7 +506,7 @@ StringRef Name = MatchStr.substr(0, TrailIdx); StringRef Trailer = MatchStr.substr(TrailIdx); IsVarDef = (VarEndIdx != StringRef::npos); - IsNumExpr = IsPseudo; + Legacy = IsNumExpr = IsPseudo; if (IsVarDef) { if ((IsPseudo || !Trailer.consume_front(":"))) { @@ -481,7 +533,7 @@ // Parse numeric expression. if (IsNumExpr) { - NumExpr = parseNumericExpression(MatchStr, NumVarDef, SM); + NumExpr = parseNumericExpression(MatchStr, NumVarDef, Legacy, SM); if (NumExpr == nullptr) return true; IsNumExpr = true; @@ -528,7 +580,8 @@ // parseNumericVariable() to get the pointer to the class instance // of the right variable definition corresponding to a given numeric // variable use. - Context->GlobalNumericVariableTable[DefName] = NumVarDef; + Context->GlobalNumericVariableTable[DefName] = + std::shared_ptr(NumVarDef); } else { VariableDefs[DefName] = CurParen; // Mark pattern variable as defined to detect collision between pattern @@ -712,13 +765,16 @@ llvm::Optional MatchedValue = Subst.getSubstitute(); // Substitution failed or is not known at match time, print undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { - StringRef UndefVarName = Subst.getUndefVarName(); - if (UndefVarName.empty()) + std::vector UndefVarNames; + Subst.getUndefVarNames(UndefVarNames); + if (UndefVarNames.empty()) continue; - OS << "uses undefined variable \""; - OS.write_escaped(UndefVarName) << "\""; + for (auto UndefVarName : UndefVarNames) { + OS << "uses undefined variable \""; + OS.write_escaped(UndefVarName) << "\""; + } } else { // Substitution succeeded. Print substituted value. if (IsNumExpr) @@ -813,18 +869,12 @@ return VarIter->second; } -FileCheckNumExpr * -FileCheckPatternContext::registerNumExpr(FileCheckASTBinop *AST) { +FileCheckNumExpr *FileCheckPatternContext::registerNumExpr( + std::shared_ptr AST) { NumExprs.emplace_back(new FileCheckNumExpr(AST)); return NumExprs.back().get(); } -template -FileCheckNumVar *FileCheckPatternContext::makeNumVar(Types... args) { - NumVars.emplace_back(new FileCheckNumVar(args...)); - return NumVars.back().get(); -} - /// Finds the closing sequence of a regex variable usage or definition. /// /// \p Str has to point in the beginning of the definition (right after the @@ -1770,7 +1820,7 @@ StringRef CmdlineName; SMLoc CmdlineNameLoc = SMLoc::getFromPointer(Expr.data()); if (P.parseNumericVariable(Expr, CmdlineName, true /*IsDefinition*/, - SM) || + false /*AcceptFail*/, SM) || !Expr.empty()) { SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error, "invalid variable name"); @@ -1799,7 +1849,8 @@ ErrorFound = true; continue; } - auto NumVarDef = makeNumVar(CmdlineName, (unsigned)0); + auto NumVarDef = + std::make_shared(CmdlineName, (unsigned)0); NumVarDef->setValue(Val); // Record this variable definition. diff --git a/llvm/test/FileCheck/line-count.txt b/llvm/test/FileCheck/line-count.txt --- a/llvm/test/FileCheck/line-count.txt +++ b/llvm/test/FileCheck/line-count.txt @@ -50,7 +50,7 @@ 50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported numeric operation '*' 51 52 BAD10: [[@LINE-x]] -53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in numeric 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]]:19: error: unexpected characters at end of numeric expression 'x' 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 @@ -18,20 +18,20 @@ FileCheckNumVar FooVar = FileCheckNumVar("FOO", (uint64_t)42); // Defined variable: getValue returns a value, setValue fails. - llvm::Optional Value = FooVar.getValue(); + llvm::Optional Value = FooVar.eval(); EXPECT_TRUE(Value); EXPECT_EQ((uint64_t)42, *Value); EXPECT_TRUE(FooVar.setValue((uint64_t)43)); // Clearing variable: getValue fails, clearValue again fails. EXPECT_FALSE(FooVar.clearValue()); - Value = FooVar.getValue(); + Value = FooVar.eval(); EXPECT_FALSE(Value); EXPECT_TRUE(FooVar.clearValue()); // Undefined variable: setValue works, getValue returns value set. EXPECT_FALSE(FooVar.setValue((uint64_t)43)); - Value = FooVar.getValue(); + Value = FooVar.eval(); EXPECT_TRUE(Value); EXPECT_EQ((uint64_t)43, *Value); } @@ -39,22 +39,35 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, BinopEvalUndef) { - auto FooVar = new FileCheckNumVar("FOO", (uint64_t)42); - auto Binop = new FileCheckASTBinop(doAdd, FooVar, 18); + auto FooVar = std::make_shared("FOO", (uint64_t)42); + auto BarVar = std::make_shared("BAR", (uint64_t)18); + auto Binop = new FileCheckASTBinop(doAdd, FooVar, BarVar); // Defined variable: eval returns right value, no undef variable returned. llvm::Optional Value = Binop->eval(); EXPECT_TRUE(Value); EXPECT_EQ((uint64_t)60, *Value); - StringRef UndefVar = Binop->getUndefVarName(); - EXPECT_EQ("", UndefVar); + std::vector UndefVarNames; + Binop->appendUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); - // Undefined variable: eval fails, undef variable returned. + // 1 undefined variable: eval fails, undef variable returned. FooVar->clearValue(); Value = Binop->eval(); EXPECT_FALSE(Value); - UndefVar = Binop->getUndefVarName(); - EXPECT_EQ("FOO", UndefVar); + Binop->appendUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("FOO", UndefVarNames[0]); + + // 2 undefined variables: eval fails, undef variables returned. + BarVar->clearValue(); + Value = Binop->eval(); + EXPECT_FALSE(Value); + UndefVarNames.clear(); + Binop->appendUndefVarNames(UndefVarNames); + EXPECT_EQ(2U, UndefVarNames.size()); + EXPECT_EQ("FOO", UndefVarNames[0]); + EXPECT_EQ("BAR", UndefVarNames[1]); } TEST_F(FileCheckTest, ValidVarNameStart) { @@ -173,13 +186,15 @@ bool parseNumVarExpect(StringRef Expr, bool isDefinition) { StringRef ExprBufferRef = bufferize(Expr); StringRef Name; - return P.parseNumericVariable(ExprBufferRef, Name, isDefinition, SM); + return P.parseNumericVariable(ExprBufferRef, Name, isDefinition, + false /*AcceptFail*/, SM); } bool parseExprExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(Expr); FileCheckNumVar *NumVarDef; - return P.parseNumericExpression(ExprBufferRef, NumVarDef, SM) == nullptr; + return P.parseNumericExpression(ExprBufferRef, NumVarDef, false, SM) == + nullptr; } bool parsePatternExpect(StringRef Pattern) { @@ -299,7 +314,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. @@ -336,8 +350,9 @@ EXPECT_FALSE(Subst.getSubstitute()); // Substitution of defined numeric variable returns the right value. - auto LineVar = new FileCheckNumVar("@LINE", (uint64_t)42); - auto Binop = new FileCheckASTBinop(doAdd, LineVar, 0); + auto LineVar = std::make_shared("@LINE", (uint64_t)42); + auto Zero = std::make_shared(0); + auto Binop = std::make_shared(doAdd, LineVar, Zero); FileCheckNumExpr NumExpr = FileCheckNumExpr(Binop); Subst = FileCheckPatternSubst(&Context, "@LINE", &NumExpr, 12); llvm::Optional Value = Subst.getSubstitute(); @@ -366,29 +381,36 @@ // Undef var in pattern variable substitution with undefined variable returns // the variable. FileCheckPatternSubst Subst = FileCheckPatternSubst(&Context, "VAR404", 42); - StringRef UndefVar = Subst.getUndefVarName(); - EXPECT_EQ("VAR404", UndefVar); + std::vector UndefVarNames; + Subst.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("VAR404", UndefVarNames[0]); // Undef var in pattern variable substitution with defined variable is empty. Subst = FileCheckPatternSubst(&Context, "FOO", 42); - UndefVar = Subst.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + Subst.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); // Undef var in numeric expression substitution with defined variable is // empty. - auto LineVar = new FileCheckNumVar("@LINE", (uint64_t)42); - auto Binop = new FileCheckASTBinop(doAdd, LineVar, 0); + auto LineVar = std::make_shared("@LINE", (uint64_t)42); + auto Zero = std::make_shared(0); + auto Binop = std::make_shared(doAdd, LineVar, Zero); FileCheckNumExpr NumExpr = FileCheckNumExpr(Binop); Subst = FileCheckPatternSubst(&Context, "@LINE", &NumExpr, 12); - UndefVar = Subst.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + Subst.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); // Undef var in valid numeric expression substitution is empty. // Undef var in numeric expression substitution with undefined variable // returns the variable. LineVar->clearValue(); - UndefVar = Subst.getUndefVarName(); - EXPECT_EQ("@LINE", UndefVar); + UndefVarNames.clear(); + Subst.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("@LINE", UndefVarNames[0]); } TEST_F(FileCheckTest, FileCheckContext) {