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 @@ -588,18 +590,11 @@ would match ``mov r5, 42`` and set ``REG`` to the value ``5``. -The syntax of a numeric substitution is ``[[#]]`` where: - -* ```` is the name of a defined numeric variable. - -* ```` is an optional numeric operation to perform on the value of - ````. Currently supported numeric operations are ``+`` and ``-``. - -* ```` is the immediate value that constitutes the second operand of - the numeric operation . It must be present if ```` is present, - absent otherwise. - -Spaces are accepted before, after and between any of these elements. +The syntax of a numeric substitution is ``[[#]]`` where ``>`` 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. +Spaces are accepted before, after and between the operands. 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 substitution 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; @@ -156,9 +209,10 @@ /// text their definition matched. virtual llvm::Optional getResult() const = 0; - /// \returns the name of the variable used in this substitution if undefined, - /// or an empty string otherwise. - virtual StringRef getUndefVarName() const = 0; + /// Records in \p UndefVarNames the name of the variables used in this + /// substitution, if undefined. + virtual void + getUndefVarNames(std::vector &UndefVarNames) const = 0; }; class FileCheckStringSubstitution : public FileCheckSubstitution { @@ -171,9 +225,9 @@ /// when defined, or None if the variable is undefined. llvm::Optional getResult() const; - /// \returns the name of the string variable used in this substitution if - /// undefined, or an empty string otherwise. - StringRef getUndefVarName() const; + /// Records in \p UndefVarNames the name of the string variables used in this + /// substitution, if undefined. + void getUndefVarNames(std::vector &UndefVarNames) const; }; class FileCheckNumericSubstitution : public FileCheckSubstitution { @@ -191,9 +245,9 @@ /// substitution as a string, or None if evaluation failed. llvm::Optional getResult() const; - /// \returns the name of the numeric variable used in this substitution if - /// undefined, or an empty string otherwise. - StringRef getUndefVarName() const; + /// Records in \p UndefVarNames the name of the numeric variables used in + /// this substitution, if undefined. + void getUndefVarNames(std::vector &UndefVarNames) const; }; //===----------------------------------------------------------------------===// @@ -282,17 +336,14 @@ /// pattern all definitions for that pattern are recorded in /// NumericVariableDefs table in the FileCheckPattern instance of that /// pattern. - 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; - /// Vector holding pointers to all substitutions. Used to automatically free /// them once they are guaranteed to no longer be used. std::vector> Substitutions; @@ -318,14 +369,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); /// Makes a new string substitution and registers it for destruction when the /// context is destroyed. @@ -410,20 +454,23 @@ 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 substitution block. \returns the class - /// representing the AST of the numeric expression whose value must be - /// substituted, or nullptr if parsing fails, in which case errors are - /// reported on \p SM. Sets \p DefinedNumericVariable to point to the class - /// representing the numeric variable defined in this numeric substitution - /// block, or nullptr if this block does not define any variable. + bool AcceptFail, const SourceMgr &SM) const; + /// Parses \p Expr for a numeric substitution block. Parameter \p Legacy + /// indicates whether Expr should be a legacy numeric substitution block. + /// \returns the class representing the AST of the numeric expression whose + /// value must be substituted, or nullptr if parsing fails, in which case + /// errors are reported on \p SM. Sets \p DefinedNumericVariable to point to + /// the class representing the numeric variable defined in this numeric + /// substitution block, or nullptr if this block does not define any + /// variable. FileCheckNumExpr * parseNumericSubstitution(StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, - const SourceMgr &SM) const; + bool Legacy, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// @@ -476,11 +523,23 @@ /// was not found. size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); - /// Parses \p Expr for a binary operation. - /// \returns the class representing the binary operation of the numeric - /// expression, or nullptr if parsing fails, in which case errors are - /// reported on \p SM. - FileCheckNumExpr *parseBinop(StringRef &Expr, const SourceMgr &SM) const; + 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. + 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 the + /// 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 + parseBinop(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,23 +44,27 @@ return false; } -llvm::Optional FileCheckNumExpr::eval() const { - assert(LeftOp && "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 FileCheckNumericSubstitution::getResult() const { - 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,17 +78,19 @@ return Regex::escape(*VarVal); } -StringRef FileCheckNumericSubstitution::getUndefVarName() const { +void FileCheckNumericSubstitution::getUndefVarNames( + std::vector &UndefVarNames) const { + UndefVarNames.clear(); // 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(); + // time, a numeric variable can get undefined later by ClearLocalVariables. + NumExpr->getAST()->appendUndefVarNames(UndefVarNames); } -StringRef FileCheckStringSubstitution::getUndefVarName() const { +void FileCheckStringSubstitution::getUndefVarNames( + std::vector &UndefVarNames) const { + UndefVarNames.clear(); if (!Context->getPatternVarValue(FromStr)) - return FromStr; - - return StringRef(); + UndefVarNames.emplace_back(FromStr); } bool FileCheckPattern::isValidVarNameStart(char C) { @@ -124,14 +136,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); @@ -167,7 +180,8 @@ // CHECK pattern. For each definition, the pointer to the 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. + // AST class instance corresponding to the last definition of this variable + // use. auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); if (VarTableIter == Context->GlobalNumericVariableTable.end()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, @@ -175,7 +189,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 +200,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,21 +233,16 @@ return LeftOp - RightOp; } -FileCheckNumExpr *FileCheckPattern::parseBinop(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::parseBinop(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; @@ -233,26 +267,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::parseNumericSubstitution( StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, - const SourceMgr &SM) const { + bool Legacy, const SourceMgr &SM) const { + std::shared_ptr NumExprAST; // Parse numeric variable definition. DefinedNumericVariable = nullptr; @@ -263,12 +294,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(this->LineNumber, Name); + new FileCheckNumericVariable(this->LineNumber, Name); DefExpr = DefExpr.ltrim(SpaceChars); if (!DefExpr.empty()) { @@ -284,12 +317,30 @@ "unexpected string after variable definition: '" + UseExpr + "'"); return nullptr; } - return Context->makeNumExpr(add, nullptr, 0); + } else { + // Parse numeric expression itself. + Expr = Expr.ltrim(SpaceChars); + // 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 = parseBinop(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; + } } - // Parse numeric expression itself. - Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + return Context->makeNumExpr(NumExprAST); } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -302,8 +353,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)) @@ -407,6 +458,7 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsVarDef = false; + bool Legacy = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; @@ -434,7 +486,7 @@ StringRef Name = MatchStr.substr(0, TrailIdx); StringRef Trailer = MatchStr.substr(TrailIdx); IsVarDef = (VarEndIdx != StringRef::npos); - IsNumBlock = IsPseudo; + Legacy = IsNumBlock = IsPseudo; if (IsVarDef) { if ((IsPseudo || !Trailer.consume_front(":"))) { @@ -463,8 +515,8 @@ FileCheckNumExpr *NumExpr; FileCheckNumericVariable *DefinedNumericVariable; if (IsNumBlock) { - NumExpr = - parseNumericSubstitution(MatchStr, DefinedNumericVariable, SM); + NumExpr = parseNumericSubstitution(MatchStr, DefinedNumericVariable, + Legacy, SM); if (NumExpr == nullptr) return true; if (DefinedNumericVariable) { @@ -513,7 +565,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 string variable as defined to detect collisions between @@ -681,13 +734,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. OS << "with \""; @@ -780,22 +836,11 @@ } FileCheckNumExpr * -FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - NumExprs.push_back(llvm::make_unique(EvalBinop, OperandLeft, - OperandRight)); +FileCheckPatternContext::makeNumExpr(std::shared_ptr AST) { + NumExprs.push_back(llvm::make_unique(AST)); return NumExprs.back().get(); } -template -FileCheckNumericVariable * -FileCheckPatternContext::makeNumericVariable(Types... args) { - NumericVariables.push_back( - llvm::make_unique(args...)); - return NumericVariables.back().get(); -} - FileCheckSubstitution * FileCheckPatternContext::makeStringSubstitution(StringRef VarName, size_t InsertIdx) { @@ -1734,7 +1779,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"); @@ -1763,7 +1808,8 @@ ErrorFound = true; continue; } - auto DefinedNumericVariable = makeNumericVariable(0, CmdlineName); + auto DefinedNumericVariable = + std::make_shared(0, CmdlineName); 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,53 +14,96 @@ 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(10U, *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(1, "FOO"); EXPECT_EQ("FOO", FooVar.getName()); - 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(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(42U, *Value); EXPECT_TRUE(FooVar.setValue(43)); - Value = FooVar.getValue(); + Value = FooVar.eval(); EXPECT_TRUE(Value); EXPECT_EQ(42U, *Value); + 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, NumExpr) { - FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42); - FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &FooVar, 18); +TEST_F(FileCheckTest, Binop) { + auto FooVar = std::make_shared("FOO", 42); + auto BarVar = std::make_shared("BAR", 18); + FileCheckASTBinop Binop = FileCheckASTBinop(doAdd, FooVar, BarVar); // Defined variable: eval returns right value, no undefined variable // returned. - llvm::Optional Value = NumExpr.eval(); + llvm::Optional Value = Binop.eval(); EXPECT_TRUE(Value); EXPECT_EQ(60U, *Value); - StringRef UndefVar = NumExpr.getUndefVarName(); - EXPECT_EQ("", UndefVar); + std::vector UndefVarNames; + Binop.appendUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); - // Undefined variable: eval fails, undefined variable returned. We call + // 1 undefined variable: eval fails, undefined variable returned. We call // getUndefVarName first to check that it can be called without calling // eval() first. - FooVar.clearValue(); - UndefVar = NumExpr.getUndefVarName(); - EXPECT_EQ("FOO", UndefVar); - Value = NumExpr.eval(); + FooVar->clearValue(); + Binop.appendUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("FOO", UndefVarNames[0]); + Value = Binop.eval(); + EXPECT_FALSE(Value); + + // 2 undefined variables: eval fails, undefined 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) { @@ -178,14 +221,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 parseSubstExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(SM, Expr); FileCheckNumericVariable *DefinedNumericVariable; return P.parseNumericSubstitution(ExprBufferRef, DefinedNumericVariable, - SM) == nullptr; + false, SM) == nullptr; } bool parsePatternExpect(StringRef Pattern) { @@ -277,6 +321,9 @@ // 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) { @@ -305,7 +352,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. @@ -337,17 +383,23 @@ GlobalDefines.emplace_back(std::string("FOO=BAR")); Context.defineCmdlineVariables(GlobalDefines, SM); - // Substitution of undefined string variable fails. + // Substitution of undefined strin variable fails, getUndefVarNames returns + // that variable's name. FileCheckStringSubstitution StringSubstitution = FileCheckStringSubstitution(&Context, "VAR404", 42); EXPECT_FALSE(StringSubstitution.getResult()); - - // Substitutions of defined pseudo and non-pseudo numeric variables return - // the right value. - FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); - FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10); - FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); - FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); + std::vector UndefVarNames; + EXPECT_TRUE(UndefVarNames.empty()); + StringSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("VAR404", UndefVarNames[0]); + + // Substitution of defined pseudo and non-pseudo numeric variable returns the + // right value, getUndefVarNames does not return any variable. + auto LineVar = std::make_shared("@LINE", 42); + auto NVar = std::make_shared("N", 10); + FileCheckNumExpr NumExprLine = FileCheckNumExpr(LineVar); + FileCheckNumExpr NumExprN = FileCheckNumExpr(NVar); FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12); FileCheckNumericSubstitution SubstitutionN = @@ -355,22 +407,43 @@ llvm::Optional Value = SubstitutionLine.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("42", *Value); + EXPECT_FALSE(UndefVarNames.empty()); + SubstitutionLine.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); Value = SubstitutionN.getResult(); EXPECT_TRUE(Value); - EXPECT_EQ("13", *Value); - - // Substitution of undefined numeric variable fails. - LineVar.clearValue(); + EXPECT_EQ("10", *Value); + UndefVarNames.push_back("ToClear"); + EXPECT_FALSE(UndefVarNames.empty()); + SubstitutionN.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); + + // Substitution of undefined numeric variable fails, getUndefVarNames returns + // that variable's name. + LineVar->clearValue(); EXPECT_FALSE(SubstitutionLine.getResult()); - NVar.clearValue(); + EXPECT_TRUE(UndefVarNames.empty()); + SubstitutionLine.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("@LINE", UndefVarNames[0]); + NVar->clearValue(); EXPECT_FALSE(SubstitutionN.getResult()); - - // Substitution of a defined string variable returns the right value. + UndefVarNames.clear(); + EXPECT_TRUE(UndefVarNames.empty()); + SubstitutionN.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("N", UndefVarNames[0]); + + // Substitution of a defined string variable returns the right value, + // getUndefVarNames does not return any variable. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); Value = StringSubstitution.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("BAR", *Value); + EXPECT_FALSE(UndefVarNames.empty()); + StringSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); } TEST_F(FileCheckTest, UndefVars) { @@ -380,33 +453,41 @@ GlobalDefines.emplace_back(std::string("FOO=BAR")); Context.defineCmdlineVariables(GlobalDefines, SM); - // getUndefVarName() on a string substitution with an undefined variable - // returns that variable. + // getUndefVarName() on a string variable substitution with an undefined + // variable returns that variable. FileCheckStringSubstitution StringSubstitution = FileCheckStringSubstitution(&Context, "VAR404", 42); - StringRef UndefVar = StringSubstitution.getUndefVarName(); - EXPECT_EQ("VAR404", UndefVar); + std::vector UndefVarNames; + StringSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("VAR404", UndefVarNames[0]); - // getUndefVarName() on a string substitution with a defined variable returns - // an empty string. + // getUndefVarName() on a string variable substitution with a defined + // variable returns an empty string. StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); - UndefVar = StringSubstitution.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + StringSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); // getUndefVarName() on a numeric substitution with a defined variable // returns an empty string. - FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); - FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0); + auto LineVar = std::make_shared("@LINE", 42); + auto Zero = std::make_shared(0); + auto Binop = std::make_shared(doAdd, LineVar, Zero); + FileCheckNumExpr NumExpr = FileCheckNumExpr(Binop); FileCheckNumericSubstitution NumericSubstitution = FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12); - UndefVar = NumericSubstitution.getUndefVarName(); - EXPECT_EQ("", UndefVar); + UndefVarNames.clear(); + NumericSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_TRUE(UndefVarNames.empty()); // getUndefVarName() on a numeric substitution with an undefined variable // returns that variable. - LineVar.clearValue(); - UndefVar = NumericSubstitution.getUndefVarName(); - EXPECT_EQ("@LINE", UndefVar); + LineVar->clearValue(); + UndefVarNames.clear(); + NumericSubstitution.getUndefVarNames(UndefVarNames); + EXPECT_EQ(1U, UndefVarNames.size()); + EXPECT_EQ("@LINE", UndefVarNames[0]); } TEST_F(FileCheckTest, FileCheckContext) { @@ -470,14 +551,14 @@ llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); FileCheckNumericVariable *DefinedNumericVariable; - FileCheckNumExpr *NumExpr = - P.parseNumericSubstitution(LocalNumVarRef, DefinedNumericVariable, SM); + FileCheckNumExpr *NumExpr = P.parseNumericSubstitution( + 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); @@ -492,10 +573,10 @@ // 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_FALSE(NumExpr->eval()); + EXPECT_FALSE(NumExpr->getAST()->eval()); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - NumExpr = - P.parseNumericSubstitution(LocalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericSubstitution(LocalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_FALSE(NumExpr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -511,10 +592,10 @@ EXPECT_TRUE(GlobalVar); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - NumExpr = - P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + NumExprVal = NumExpr->getAST()->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); @@ -523,10 +604,10 @@ GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - NumExpr = - P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, SM); + NumExpr = P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + NumExprVal = NumExpr->getAST()->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); }