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 ``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 @@ -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,10 +40,58 @@ // Numeric expression handling code. //===----------------------------------------------------------------------===// -/// Class representing a numeric variable with a given value in 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 FileCheckNumericVariable { +/// Base class representing the AST of a given numeric expression. +class FileCheckNumExprAST { +public: + virtual ~FileCheckNumExprAST() = default; + + /// Evaluates and \returns the value of the expression represented by this + /// AST. + virtual llvm::Optional eval() const = 0; + + /// Appends 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) {} + + /// Evaluates and returns the value of the expression represented by this + /// AST. Therefore, \returns the literal's value. + llvm::Optional eval() const { return Value; } +}; + +/// Class representing a numeric expression and its matching format. +class FileCheckNumExpr { +private: + /// Pointer to AST of the numeric expression. + std::shared_ptr AST; + +public: + /// Generic constructor for a numeric expression whose equality constraint is + /// represented by \p AST. + FileCheckNumExpr(std::shared_ptr AST) : AST(AST) {} + + /// \returns pointer to AST of the numeric expression. Pointer is guaranteed + /// to be valid as long as this object is. + 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 FileCheckNumericVariable : public FileCheckNumExprAST { private: /// Name of the numeric variable. StringRef Name; @@ -68,8 +116,12 @@ /// \returns name of that numeric variable. StringRef getName() const { return Name; } - /// \returns value of this numeric variable. - llvm::Optional getValue() const { return Value; } + /// Evaluates and returns the value of the expression represented by this + /// AST. Therefore, \returns this variable's value. + llvm::Optional eval() const { return Value; } + + /// Appends numeric variable's name to UndefVarNames if undefined. + void appendUndefVarNames(std::vector &UndefVarNames) const; /// Sets value of this numeric variable if not defined. \returns whether the /// variable was already defined. @@ -86,33 +138,34 @@ /// Type of functions evaluating a given binary operation. using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); -/// Class representing a numeric expression consisting of either a single -/// numeric variable or a binary operation between a numeric variable and an -/// immediate. -class FileCheckNumExpr { +/// Class representing a single binary operation in the AST of a numeric +/// expression. +class FileCheckASTBinop : public FileCheckNumExprAST { private: /// Left operand. - FileCheckNumericVariable *LeftOp; + std::shared_ptr LeftOp; /// Right operand. - uint64_t RightOp; + std::shared_ptr RightOp; /// Pointer to function that can evaluate this binary operation. binop_eval_t EvalBinop; public: - FileCheckNumExpr(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, uint64_t OperandRight) + FileCheckASTBinop(binop_eval_t EvalBinop, + std::shared_ptr OperandLeft, + std::shared_ptr OperandRight) : LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {} - /// Evaluates the value of this numeric expression, using EvalBinop to - /// perform the binary operation it consists of. \returns None if the numeric - /// variable used is undefined, or the expression value otherwise. + /// Evaluates the value of the binary operation represented by this AST, + /// using EvalBinop on the result of recursively evaluating the operands. + /// \returns None if any numeric variable used is undefined, or the + /// expression value otherwise. llvm::Optional eval() const; - /// \returns the name of the undefined variable used in this expression if - /// any or an empty string otherwise. - StringRef getUndefVarName() const; + /// Appends names of undefined variables used in any of the operands of this + /// binary operation. + void appendUndefVarNames(std::vector &UndefVarNames) const; }; class FileCheckPatternContext; @@ -169,9 +222,9 @@ /// text their definition matched. llvm::Optional getResult() const; - /// \returns the name of the undefined variable used in this substitution, if - /// any, or an empty string otherwise. - StringRef getUndefVarName() const; + /// Records in \p UndefVarNames the name of the undefined variables used in + /// this substitution, if any. + void getUndefVarNames(std::vector &UndefVarNames) const; }; //===----------------------------------------------------------------------===// @@ -257,17 +310,14 @@ /// their own class instance not referenced from 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 parsed numeric expressions. Used to /// automatically free the numeric expressions once they are guaranteed to no /// longer be used. std::vector> NumExprs; - /// Vector holding pointers to all parsed numeric variables. Used to - /// automatically free them once they are guaranteed to no longer be used. - std::vector> NumericVariables; - public: /// \returns the value of pattern variable \p VarName or None if no such /// variable has been defined. @@ -287,14 +337,7 @@ private: /// Makes a new numeric expression instance and registers it for destruction /// when the context is destroyed. - FileCheckNumExpr *makeNumExpr(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight); - - /// Makes a new numeric variable and registers it for destruction when the - /// context is destroyed. - template - FileCheckNumericVariable *makeNumericVariable(Types... args); + FileCheckNumExpr *makeNumExpr(std::shared_ptr AST); }; class FileCheckPattern { @@ -371,19 +414,21 @@ static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); /// Parses \p Expr for use or definition (if \p IsDefinition is true) of a /// numeric variable. \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. + /// reported on \p SM, unless \p AcceptFail is true and the error is in + /// parsing the variable name. Otherwise, sets \p Name to the name of the + /// parsed numeric variable. bool parseNumericVariable(StringRef &Expr, StringRef &Name, bool IsDefinition, - const SourceMgr &SM) const; - /// Parses \p Expr for a numeric expression. \returns the class representing - /// the AST of numeric expression or nullptr if parsing fails in which case - /// errors are reported on \p SM. Sets \p DefinedNumericVariable to the - /// pointer to the class representing the variable defined to this numeric - /// expression if any. + bool AcceptFail, const SourceMgr &SM) const; + /// Parses \p Expr for a numeric expression. Parameter \p Legacy indicates + /// whether Expr should be a legacy numeric expression. \p returns the class + /// representing the AST of numeric expression or nullptr if parsing fails + /// in which case errors are reported on \p SM. Sets + /// \p DefinedNumericVariable to the pointer to the class representing the + /// variable defined to this numeric expression, if any. FileCheckNumExpr * parseNumericExpression(StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, - const SourceMgr &SM) const; + bool Legacy, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initialize this FileCheckPattern /// instance accordingly. /// @@ -437,12 +482,24 @@ /// or npos if it was not found. size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); - /// Parses \p Expr for a binary operation. - /// \returns the class representing that binary operation of the numeric + enum AllowedOperand { LegacyVar, LegacyLiteral, Any }; + /// Parses \p Expr for use of a numeric operand. Accepts both literal values + /// and numeric variables, depending on the value of \p AllowedOperandFlag. + /// \returns 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. - FileCheckNumExpr *parseFileCheckBinop(StringRef &Expr, - const SourceMgr &SM) const; + std::shared_ptr + parseNumericOperand(StringRef &Expr, enum 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 Legacy indicates whether we are + /// parsing a legacy numeric expression. \returns 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 LeftOp, 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,6 +24,12 @@ using namespace llvm; +void FileCheckNumericVariable::appendUndefVarNames( + std::vector &UndefVarNames) const { + if (!Value) + UndefVarNames.emplace_back(Name); +} + bool FileCheckNumericVariable::setValue(uint64_t NewValue) { if (Value) return true; @@ -38,24 +44,28 @@ return false; } -llvm::Optional FileCheckNumExpr::eval() const { - assert(LeftOp != nullptr && "Evaluating an empty numeric expression"); - llvm::Optional LeftOpValue = LeftOp->getValue(); - // Variable is undefined. - if (!LeftOpValue) +llvm::Optional FileCheckASTBinop::eval() const { + llvm::Optional LeftOp = this->LeftOp->eval(); + llvm::Optional RightOp = this->RightOp->eval(); + + // Uses undefined variable. + if (!LeftOp || !RightOp) return llvm::None; - return EvalBinop(*LeftOpValue, RightOp); + + return EvalBinop(*LeftOp, *RightOp); } -StringRef FileCheckNumExpr::getUndefVarName() const { - if (!LeftOp->getValue()) - return LeftOp->getName(); - return StringRef(); +void FileCheckASTBinop::appendUndefVarNames( + std::vector &UndefVarNames) const { + LeftOp->appendUndefVarNames(UndefVarNames); + RightOp->appendUndefVarNames(UndefVarNames); } llvm::Optional FileCheckPatternSubstitution::getResult() const { if (IsNumExpr) { - llvm::Optional EvaluatedValue = NumExpr->eval(); + assert(NumExpr->getAST() != nullptr && + "Substituting empty numeric expression"); + llvm::Optional EvaluatedValue = NumExpr->getAST()->eval(); if (!EvaluatedValue) return llvm::None; return utostr(*EvaluatedValue); @@ -68,16 +78,17 @@ return Regex::escape(*VarVal); } -StringRef FileCheckPatternSubstitution::getUndefVarName() const { +void FileCheckPatternSubstitution::getUndefVarNames( + std::vector &UndefVarNames) const { + UndefVarNames.clear(); if (IsNumExpr) // Although a use of an undefined numeric variable is detected at parse - // time, a numeric variable can be undefined later by ClearLocalVariables. - return NumExpr->getUndefVarName(); - - if (!Context->getPatternVarValue(FromStr)) - return FromStr; - - return StringRef(); + // time, a numeric variable can get undefined later by ClearLocalVariables. + NumExpr->getAST()->appendUndefVarNames(UndefVarNames); + else { + if (!Context->getPatternVarValue(FromStr)) + UndefVarNames.emplace_back(FromStr); + } } bool FileCheckPattern::isValidVarNameStart(char C) { @@ -123,14 +134,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); @@ -163,11 +175,10 @@ // 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 corresponding - // class instance of the corresponding numeric variable definition is - // stored in GlobalNumericVariableTable. Therefore, the pointer we get - // below is for the class instance corresponding to the last definition of - // this variable use. + // CHECK pattern. For each definition, the pointer to the corresponding AST + // class instance is stored in GlobalNumericVariableTable. Therefore the + // pointer we get below is for the AST class instance corresponding to the + // last definition of the variable before this use. auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); if (VarTableIter == Context->GlobalNumericVariableTable.end()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, @@ -175,7 +186,7 @@ return true; } - FileCheckNumericVariable *NumericVariable = VarTableIter->second; + FileCheckNumericVariable *NumericVariable = VarTableIter->second.get(); if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "numeric variable '" + NumericVariable->getName() + @@ -186,6 +197,31 @@ return false; } +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 NumericVariable = + Context->GlobalNumericVariableTable.find(Name)->second; + return NumericVariable; + } + } + + // 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 add(uint64_t LeftOp, uint64_t RightOp) { return LeftOp + RightOp; } @@ -194,22 +230,15 @@ return LeftOp - RightOp; } -FileCheckNumExpr * -FileCheckPattern::parseFileCheckBinop(StringRef &Expr, - const SourceMgr &SM) const { - StringRef Name; - if (parseNumericVariable(Expr, Name, false, SM)) { - // Error reporting done in parseNumericVariable. - return nullptr; - } - FileCheckNumericVariable *LeftOp = - Context->GlobalNumericVariableTable.find(Name)->second; +std::shared_ptr FileCheckPattern::parseFileCheckBinop( + StringRef &Expr, std::shared_ptr LeftOp, bool Legacy, + const SourceMgr &SM) const { + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) + return LeftOp; // Check if this is a supported operation and select a function to perform // it. - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) - return Context->makeNumExpr(add, LeftOp, 0); SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); char Operator = popFront(Expr); binop_eval_t EvalBinop; @@ -234,26 +263,23 @@ "missing operand in numeric expression"); return nullptr; } - uint64_t RightOp; - if (Expr.consumeInteger(10, RightOp)) { + // Second operand in legacy numeric expression is a literal. + enum AllowedOperand AO = Legacy ? LegacyLiteral : Any; + std::shared_ptr RightOp = + parseNumericOperand(Expr, AO, SM); + if (RightOp == nullptr) { SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, - "invalid offset in numeric expression '" + Expr + "'"); + "invalid operand format '" + Expr + "'"); return nullptr; } Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, - "unexpected characters at end of numeric expression '" + - Expr + "'"); - return nullptr; - } - - return Context->makeNumExpr(EvalBinop, LeftOp, RightOp); + return std::make_shared(EvalBinop, LeftOp, RightOp); } FileCheckNumExpr *FileCheckPattern::parseNumericExpression( StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, - const SourceMgr &SM) const { + bool Legacy, const SourceMgr &SM) const { + std::shared_ptr NumExprAST; // Parse numeric variable definition. DefinedNumericVariable = nullptr; @@ -264,12 +290,14 @@ DefExpr = DefExpr.ltrim(SpaceChars); 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; + } DefinedNumericVariable = - Context->makeNumericVariable(Name, this->LineNumber); + new FileCheckNumericVariable(Name, this->LineNumber); DefExpr = DefExpr.ltrim(SpaceChars); if (!DefExpr.empty()) { @@ -285,12 +313,30 @@ "unexpected string after variable definition: '" + UseExpr + "'"); return nullptr; } - return Context->makeNumExpr(add, nullptr, 0); - } else - + } else { // Parse numeric expression itself. Expr = Expr.ltrim(SpaceChars); - return parseFileCheckBinop(Expr, SM); + // 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->makeNumExpr(NumExprAST); } bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, @@ -303,8 +349,8 @@ // Create fake @LINE pseudo variable definition. StringRef LinePseudo = "@LINE"; uint64_t LineNumber64 = LineNumber; - FileCheckNumericVariable *LinePseudoVar = - Context->makeNumericVariable(LinePseudo, LineNumber64); + std::shared_ptr LinePseudoVar = + std::make_shared(LinePseudo, LineNumber64); Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar; if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) @@ -410,6 +456,7 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsVarDef; + bool Legacy = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; @@ -439,7 +486,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(":"))) { @@ -466,7 +513,8 @@ // Parse numeric expression. if (IsNumExpr) { - NumExpr = parseNumericExpression(MatchStr, DefinedNumericVariable, SM); + NumExpr = parseNumericExpression(MatchStr, DefinedNumericVariable, + Legacy, SM); if (NumExpr == nullptr) return true; IsNumExpr = true; @@ -514,7 +562,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] = DefinedNumericVariable; + Context->GlobalNumericVariableTable[DefName] = + std::shared_ptr(DefinedNumericVariable); } else { VariableDefs[DefName] = CurParen; // Mark pattern variable as defined to detect collision between pattern @@ -684,13 +733,16 @@ llvm::Optional MatchedValue = Substitution.getResult(); // Substitution failed or is not known at match time, print the undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { - StringRef UndefVarName = Substitution.getUndefVarName(); - if (UndefVarName.empty()) + std::vector UndefVarNames; + Substitution.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) @@ -786,23 +838,12 @@ } FileCheckNumExpr * -FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - auto NewNumExpr = new FileCheckNumExpr(EvalBinop, OperandLeft, OperandRight); +FileCheckPatternContext::makeNumExpr(std::shared_ptr AST) { + auto NewNumExpr = new FileCheckNumExpr(AST); NumExprs.push_back(std::unique_ptr(NewNumExpr)); return NumExprs.back().get(); } -template -FileCheckNumericVariable * -FileCheckPatternContext::makeNumericVariable(Types... args) { - auto NewNumericVariable = new FileCheckNumericVariable(args...); - NumericVariables.push_back( - std::unique_ptr(NewNumericVariable)); - return NumericVariables.back().get(); -} - size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { // Offset keeps track of the current offset within the input Str size_t Offset = 0; @@ -1726,7 +1767,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"); @@ -1756,7 +1797,7 @@ continue; } auto DefinedNumericVariable = - makeNumericVariable(CmdlineName, (unsigned)0); + std::make_shared(CmdlineName, (unsigned)0); DefinedNumericVariable->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]]:20: 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 @@ -14,46 +14,89 @@ class FileCheckTest : public ::testing::Test {}; +TEST_F(FileCheckTest, Literal) { + // Eval returns the literal's value. + auto Ten = FileCheckNumExprLiteral(10); + llvm::Optional Value = Ten.eval(); + EXPECT_TRUE(Value); + EXPECT_EQ((uint64_t)10, *Value); + + // Literal has no undefined variable. + std::vector UndefVarNames; + EXPECT_TRUE(UndefVarNames.empty()); + Ten.appendUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); +} + TEST_F(FileCheckTest, NumericVariable) { - // Undefined variable: getValue and clearValue fails, setValue works. + // Undefined variable: eval and clearValue fails, appendUndefVarNames returns + // the variable and setValue works. FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1U); - llvm::Optional Value = FooVar.getValue(); + llvm::Optional Value = FooVar.eval(); EXPECT_FALSE(Value); EXPECT_TRUE(FooVar.clearValue()); + std::vector UndefVarNames; + EXPECT_TRUE(UndefVarNames.empty()); + FooVar.appendUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("FOO", UndefVarNames[0]); EXPECT_FALSE(FooVar.setValue((uint64_t)42)); - // Defined variable: getValue returns value set, setValue fails. - Value = FooVar.getValue(); + // Defined variable: eval returns value set, setValue fails and + // appendUndefVarNames is a nop. + Value = FooVar.eval(); EXPECT_TRUE(Value); EXPECT_EQ((uint64_t)42, *Value); EXPECT_TRUE(FooVar.setValue((uint64_t)43)); + UndefVarNames.clear(); + EXPECT_TRUE(UndefVarNames.empty()); + FooVar.appendUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); - // Clearing variable: getValue fails, clearValue again fails. + // Clearing variable: eval fails and clearValue again fails. + // appendUndefVarNames returns the variable again. EXPECT_FALSE(FooVar.clearValue()); - Value = FooVar.getValue(); + Value = FooVar.eval(); EXPECT_FALSE(Value); EXPECT_TRUE(FooVar.clearValue()); + EXPECT_TRUE(UndefVarNames.empty()); + FooVar.appendUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("FOO", UndefVarNames[0]); } uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } -TEST_F(FileCheckTest, NumExprEvalUndef) { - auto FooVar = new FileCheckNumericVariable("FOO", (uint64_t)42); - auto NumExpr = new FileCheckNumExpr(doAdd, FooVar, 18); +TEST_F(FileCheckTest, BinopEvalUndef) { + 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 = NumExpr->eval(); + llvm::Optional Value = Binop->eval(); EXPECT_TRUE(Value); EXPECT_EQ((uint64_t)60, *Value); - StringRef UndefVar = NumExpr->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 = NumExpr->eval(); + Value = Binop->eval(); EXPECT_FALSE(Value); - UndefVar = NumExpr->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) { @@ -171,14 +214,15 @@ bool parseNumVarExpect(StringRef Expr, bool isDefinition) { StringRef ExprBufferRef = bufferize(SM, 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(SM, Expr); FileCheckNumericVariable *DefinedNumericVariable; return P.parseNumericExpression(ExprBufferRef, DefinedNumericVariable, - SM) == nullptr; + false, SM) == nullptr; } bool parsePatternExpect(StringRef Pattern) { @@ -270,6 +314,9 @@ // Valid expression. EXPECT_FALSE(Tester.parseExprExpect("@LINE+5")); EXPECT_FALSE(Tester.parseExprExpect("FOO+4")); + Tester.initNextPattern(); + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]")); } TEST_F(FileCheckTest, ParsePattern) { @@ -298,7 +345,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. @@ -330,29 +376,51 @@ GlobalDefines.emplace_back(std::string("FOO=BAR")); Context.defineCmdlineVariables(GlobalDefines, SM); - // Substitution of undefined pattern variable fails. + // Substitution of undefined pattern variable fails, getUndefVarNames + // returns the variable name. FileCheckPatternSubstitution Substitution = FileCheckPatternSubstitution(&Context, "VAR404", 42); EXPECT_FALSE(Substitution.getResult()); - - // Substitution of defined numeric variable returns the right value. - auto LineVar = new FileCheckNumericVariable("@LINE", (uint64_t)42); - FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, LineVar, 0); + std::vector UndefVarNames; + EXPECT_TRUE(UndefVarNames.empty()); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("VAR404", UndefVarNames[0]); + + // Substitution of defined numeric variable returns the right value, + // getUndefVarNames does not return any variable. + 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); Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12); llvm::Optional Value = Substitution.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("42", *Value); + EXPECT_FALSE(UndefVarNames.empty()); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); - // Substitution of undefined numeric variable fails. + // Substitution of undefined numeric variable fails, getUndefVarNames returns + // that variable. LineVar->clearValue(); EXPECT_FALSE(Substitution.getResult()); + EXPECT_TRUE(UndefVarNames.empty()); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("@LINE", UndefVarNames[0]); - // Substitution of defined pattern variable returns the right value. + // Substitution of defined pattern variable returns the right value, + // getUndefVarNames does not return any variable. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42); Value = Substitution.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("BAR", *Value); + EXPECT_FALSE(UndefVarNames.empty()); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); } TEST_F(FileCheckTest, UndefVars) { @@ -366,28 +434,37 @@ // the variable. FileCheckPatternSubstitution Substitution = FileCheckPatternSubstitution(&Context, "VAR404", 42); - StringRef UndefVar = Substitution.getUndefVarName(); - EXPECT_EQ("VAR404", UndefVar); + std::vector UndefVarNames; + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("VAR404", UndefVarNames[0]); // Undef var in pattern variable substitution with defined variable is empty. Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42); - UndefVar = Substitution.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); // Undef var in numeric expression substitution with defined variable is // empty. - auto LineVar = new FileCheckNumericVariable("@LINE", (uint64_t)42); - FileCheckNumExpr NumExpr = FileCheckNumExpr(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); Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12); - UndefVar = Substitution.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + Substitution.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 = Substitution.getUndefVarName(); - EXPECT_EQ("@LINE", UndefVar); + UndefVarNames.clear(); + Substitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("@LINE", UndefVarNames[0]); } TEST_F(FileCheckTest, FileCheckContext) { @@ -451,14 +528,14 @@ llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); FileCheckNumericVariable *DefinedNumericVariable; - FileCheckNumExpr *NumExpr = - P.parseNumericExpression(LocalNumVarRef, DefinedNumericVariable, SM); + FileCheckNumExpr *NumExpr = P.parseNumericExpression( + LocalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); EXPECT_TRUE(LocalVar); EXPECT_EQ(*LocalVar, "FOO"); EXPECT_TRUE(NumExpr); - llvm::Optional NumExprVal = NumExpr->eval(); + llvm::Optional NumExprVal = NumExpr->getAST()->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 18U); EXPECT_TRUE(EmptyVar); @@ -470,10 +547,10 @@ LocalVar = Cxt.getPatternVarValue(LocalVarStr); EXPECT_FALSE(LocalVar); // Check eval fails even if we kept a pointer to the numeric expression. - EXPECT_FALSE(NumExpr->eval()); + EXPECT_FALSE(NumExpr->getAST()->eval()); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - NumExpr = - P.parseNumericExpression(LocalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericExpression(LocalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_FALSE(NumExpr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -489,10 +566,10 @@ EXPECT_TRUE(GlobalVar); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - NumExpr = - P.parseNumericExpression(GlobalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericExpression(GlobalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + NumExprVal = NumExpr->getAST()->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); @@ -501,10 +578,10 @@ GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - NumExpr = - P.parseNumericExpression(GlobalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericExpression(GlobalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + NumExprVal = NumExpr->getAST()->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); }