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,42 @@ // 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 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 +100,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 +122,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 +193,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,29 +209,31 @@ /// 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 { private: /// Pointer to the class representing the numeric expression whose value is /// to be substituted. - FileCheckNumExpr *NumExpr; + std::shared_ptr NumExprAST; public: FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, - FileCheckNumExpr *NumExpr, size_t InsertIdx) - : FileCheckSubstitution(Context, Expr, InsertIdx), NumExpr(NumExpr) {} + std::shared_ptr NumExprAST, + size_t InsertIdx) + : FileCheckSubstitution(Context, Expr, InsertIdx), + NumExprAST(NumExprAST) {} /// \returns the result of evaluating the numeric expression in this /// 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,16 +322,8 @@ /// pattern all definitions for that pattern are recorded in /// NumericVariableDefs table in the FileCheckPattern instance of that /// pattern. - 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; + StringMap> + GlobalNumericVariableTable; /// Vector holding pointers to all substitutions. Used to automatically free /// them once they are guaranteed to no longer be used. @@ -316,17 +348,6 @@ void clearLocalVars(); 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); - /// Makes a new string substitution and registers it for destruction when the /// context is destroyed. FileCheckSubstitution *makeStringSubstitution(StringRef VarName, @@ -334,9 +355,10 @@ /// Makes a new numeric substitution and registers it for destruction when /// the context is destroyed. - FileCheckSubstitution *makeNumericSubstitution(StringRef Expr, - FileCheckNumExpr *NumExpr, - size_t InsertIdx); + FileCheckSubstitution * + makeNumericSubstitution(StringRef Expr, + std::shared_ptr NumExprAST, + size_t InsertIdx); }; class FileCheckPattern { @@ -410,18 +432,22 @@ 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. - FileCheckNumExpr *parseNumericSubstitutionBlock( - StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, + 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. + /// Sets \p NumExprAST to point to the class instance representing the + /// numeric expression whose value must be substituted. Also 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 defined any variable. \returns whether parsing fails, in + /// which case errors are reported on \p SM. + bool parseNumericSubstitutionBlock( + StringRef Expr, std::shared_ptr &NumExprAST, + FileCheckNumericVariable *&DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. @@ -475,11 +501,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,25 @@ 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(); + llvm::Optional EvaluatedValue = NumExprAST->eval(); if (!EvaluatedValue) return llvm::None; return utostr(*EvaluatedValue); @@ -68,17 +76,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. + NumExprAST->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 +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); @@ -167,7 +178,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 +187,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 +198,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 +231,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,27 +265,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::parseNumericSubstitutionBlock( - StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, +bool FileCheckPattern::parseNumericSubstitutionBlock( + StringRef Expr, std::shared_ptr &NumExprAST, + FileCheckNumericVariable *&DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const { - // Parse numeric variable definition. DefinedNumericVariable = nullptr; size_t DefEnd = Expr.find(':'); @@ -263,33 +291,53 @@ 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); + return true; + } DefExpr = DefExpr.ltrim(SpaceChars); if (!DefExpr.empty()) { SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), SourceMgr::DK_Error, "invalid numeric variable definition"); - return nullptr; + return true; } UseExpr = UseExpr.ltrim(SpaceChars); if (!UseExpr.empty()) { SM.PrintMessage( SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, "unexpected string after variable definition: '" + UseExpr + "'"); - return nullptr; + return true; + } + + DefinedNumericVariable = + new FileCheckNumericVariable(this->LineNumber, Name); + } 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 true; + } + } + if (NumExprAST == nullptr) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid operand format '" + Expr + "'"); + return true; } - return Context->makeNumExpr(add, nullptr, 0); } - // Parse numeric expression itself. - Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + return false; } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -302,8 +350,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 +455,7 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsVarDef = false; + bool Legacy = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; @@ -434,7 +483,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(":"))) { @@ -460,12 +509,11 @@ } // Parse numeric substitution block. - FileCheckNumExpr *NumExpr; + std::shared_ptr NumExprAST; FileCheckNumericVariable *DefinedNumericVariable; if (IsNumBlock) { - NumExpr = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM); - if (NumExpr == nullptr) + if (parseNumericSubstitutionBlock(MatchStr, NumExprAST, + DefinedNumericVariable, Legacy, SM)) return true; if (DefinedNumericVariable) { IsVarDef = true; @@ -495,7 +543,7 @@ // previous CHECK pattern or substitution of a numeric expression. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, NumExpr, + ? Context->makeNumericSubstitution(SubstStr, NumExprAST, SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); @@ -513,7 +561,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 +730,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 \""; @@ -779,23 +831,6 @@ return VarIter->second; } -FileCheckNumExpr * -FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - NumExprs.push_back(llvm::make_unique(EvalBinop, OperandLeft, - OperandRight)); - 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) { @@ -805,9 +840,10 @@ } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) { + StringRef Expr, std::shared_ptr NumExprAST, + size_t InsertIdx) { Substitutions.push_back(llvm::make_unique( - this, Expr, NumExpr, InsertIdx)); + this, Expr, NumExprAST, InsertIdx)); return Substitutions.back().get(); } @@ -1734,7 +1770,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 +1799,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,16 @@ 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); + std::shared_ptr NumExprAST; FileCheckNumericVariable *DefinedNumericVariable; - return P.parseNumericSubstitutionBlock( - ExprBufferRef, DefinedNumericVariable, SM) == nullptr; + return P.parseNumericSubstitutionBlock(ExprBufferRef, NumExprAST, + DefinedNumericVariable, false, SM); } bool parsePatternExpect(StringRef Pattern) { @@ -277,6 +322,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 +353,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,40 +384,65 @@ 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); FileCheckNumericSubstitution SubstitutionLine = - FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12); + FileCheckNumericSubstitution(&Context, "@LINE", LineVar, 12); FileCheckNumericSubstitution SubstitutionN = - FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30); + FileCheckNumericSubstitution(&Context, "N", NVar, 30); 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 +452,40 @@ 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); FileCheckNumericSubstitution NumericSubstitution = - FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12); - UndefVar = NumericSubstitution.getUndefVarName(); - EXPECT_EQ("", UndefVar); + FileCheckNumericSubstitution(&Context, "@LINE", Binop, 12); + 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) { @@ -469,17 +548,18 @@ StringRef UnknownVarStr = "UnknownVar"; llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); + std::shared_ptr NumExprAST; FileCheckNumericVariable *DefinedNumericVariable; - FileCheckNumExpr *NumExpr = P.parseNumericSubstitutionBlock( - LocalNumVarRef, DefinedNumericVariable, SM); - llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + EXPECT_FALSE(P.parseNumericSubstitutionBlock(LocalNumVarRef, NumExprAST, + DefinedNumericVariable, + false /*Legacy*/, SM)); EXPECT_TRUE(LocalVar); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(NumExpr); - llvm::Optional NumExprVal = NumExpr->eval(); + llvm::Optional NumExprVal = NumExprAST->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 18U); + llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); + llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); EXPECT_TRUE(EmptyVar); EXPECT_EQ(*EmptyVar, ""); EXPECT_FALSE(UnknownVar); @@ -492,11 +572,11 @@ // 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(NumExprAST->eval()); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_FALSE(NumExpr); + EXPECT_TRUE(P.parseNumericSubstitutionBlock(LocalNumVarRef, NumExprAST, + DefinedNumericVariable, + false /*Legacy*/, SM)); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -511,10 +591,10 @@ EXPECT_TRUE(GlobalVar); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + EXPECT_FALSE(P.parseNumericSubstitutionBlock(GlobalNumVarRef, NumExprAST, + DefinedNumericVariable, + false /*Legacy*/, SM)); + NumExprVal = NumExprAST->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); @@ -523,10 +603,10 @@ GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + EXPECT_FALSE(P.parseNumericSubstitutionBlock(GlobalNumVarRef, NumExprAST, + DefinedNumericVariable, + false /*Legacy*/, SM)); + NumExprVal = NumExprAST->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); }