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 previously defined numeric +variables 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,11 +40,59 @@ // 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 their respective +/// 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 or an error if evaluation fails. + virtual Expected eval() const = 0; +}; + +/// 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 a literal. + FileCheckNumExprLiteral(uint64_t Val) : Value(Val) {} + + /// \returns the literal's value. + Expected eval() const { return Value; } +}; + +/// Class to represent an undefined variable error which prints that variable's +/// name between quotes when printed. +class FileCheckUndefVarError : public ErrorInfo { +private: + StringRef VarName; + +public: + static char ID; + + FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {} + + StringRef getVarName() const { return VarName; } + + std::error_code convertToErrorCode() const override { + return inconvertibleErrorCode(); + } + + /// Print name of variable associated with this error. + void log(raw_ostream &OS) const override { + OS << "\""; + OS.write_escaped(VarName) << "\""; + } +}; + +/// Class representing a numeric variable 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 their respective /// definition. -class FileCheckNumericVariable { +class FileCheckNumericVariable : public FileCheckNumExprAST { private: /// Name of the numeric variable. StringRef Name; @@ -66,11 +114,11 @@ FileCheckNumericVariable(StringRef Name, uint64_t Value) : Name(Name), Value(Value), DefLineNumber(0) {} - /// \returns name of that numeric variable. + /// \returns name of this numeric variable. StringRef getName() const { return Name; } - /// \returns value of this numeric variable. - Optional getValue() const { return Value; } + /// \returns this variable's value. + Expected eval() const; /// Sets value of this numeric variable if not defined. \returns whether the /// variable was already defined. @@ -87,52 +135,29 @@ /// Type of functions evaluating a given binary operation. using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); -/// Class to represent an undefined variable error which prints that variable's -/// name between quotes when printed. -class FileCheckUndefVarError : public ErrorInfo { -private: - StringRef VarName; - -public: - static char ID; - - FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {} - - StringRef getVarName() const { return VarName; } - - std::error_code convertToErrorCode() const override { - return inconvertibleErrorCode(); - } - - /// Print name of variable associated with this error. - void log(raw_ostream &OS) const override { - OS << "\""; - OS.write_escaped(VarName) << "\""; - } -}; - -/// Class representing a numeric 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 an error 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 an error if a numeric variable used is undefined, or the + /// expression value otherwise. Expected eval() const; }; @@ -190,12 +215,14 @@ 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 a string containing the result of evaluating the numeric /// expression in this substitution, or an error if evaluation failed. @@ -274,16 +301,8 @@ /// pattern all definitions for that pattern are recorded in the /// 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. @@ -308,17 +327,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, @@ -326,9 +334,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 to represent a parsing error, holding a diagnostic with location @@ -456,16 +465,17 @@ static Error parseNumericVariableDefinition(StringRef &Expr, StringRef &Name, FileCheckPatternContext *Context, const SourceMgr &SM); - /// Parses \p Expr for a numeric substitution block. \returns the class - /// representing the AST of the numeric expression whose value must be - /// substituted, or an error holding a diagnostic against \p SM if parsing - /// fails. If substitution was successful, sets \p DefinedNumericVariable to - /// point to the class representing the numeric variable defined in this - /// numeric substitution block, or None if this block does not define any - /// variable. - Expected parseNumericSubstitutionBlock( + /// Parses \p Expr for a numeric substitution block. Parameter \p Legacy + /// indicates whether \p Expr should be a legacy numeric substitution block. + /// \returns a pointer to the class instance representing the AST of the + /// numeric expression whose value must be substitued, or an error holding a + /// diagnostic against \p SM if parsing fails. If substitution was + /// successful, sets \p DefinedNumericVariable to point to the class + /// representing the numeric variable defined in this numeric substitution + /// block, or None if this block does not define any variable. + Expected> parseNumericSubstitutionBlock( StringRef Expr, - Optional &DefinedNumericVariable, + Optional &DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. @@ -490,7 +500,7 @@ /// match defines new numeric values. size_t match(StringRef Buffer, size_t &MatchLen, const SourceMgr &SM) const; /// Prints the value of successful substitutions or the name of the undefined - /// string or numeric variable preventing a successful substitution. + /// string or numeric variables preventing a successful substitution. void printSubstitutions(const SourceMgr &SM, StringRef Buffer, SMRange MatchRange = None) const; void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer, @@ -522,13 +532,25 @@ /// Parses \p Expr for the use of a numeric variable. \returns the pointer to /// the class instance representing that variable if successful, or an error /// holding a diagnostic against \p SM otherwise. - Expected - parseNumericVariableUse(StringRef &Expr, const SourceMgr &SM) const; - /// Parses \p Expr for a binary operation. - /// \returns the class representing the binary operation of the numeric - /// expression, or an error holding a diagnostic against \p SM otherwise. - Expected parseBinop(StringRef &Expr, - const SourceMgr &SM) const; + Expected> + parseNumericVariableUse(StringRef Name, bool IsPseudo, + 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 an error holding a diagnostic against \p SM otherwise. + Expected> + parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const; + /// Parses \p Expr for a binary operation. The left operand of this binary + /// operation is given in \p LeftOp and \p 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 an error + /// holding a diagnostic against \p SM otherwise. + Expected> + 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; +Expected FileCheckNumericVariable::eval() const { + if (Value) + return *Value; + return make_error(Name); +} + bool FileCheckNumericVariable::setValue(uint64_t NewValue) { if (Value) return true; @@ -38,17 +44,25 @@ return false; } -Expected FileCheckNumExpr::eval() const { - assert(LeftOp && "Evaluating an empty numeric expression"); - Optional LeftOpValue = LeftOp->getValue(); - // Variable is undefined. - if (!LeftOpValue) - return make_error(LeftOp->getName()); - return EvalBinop(*LeftOpValue, RightOp); +Expected FileCheckASTBinop::eval() const { + Expected LeftOp = this->LeftOp->eval(); + Expected RightOp = this->RightOp->eval(); + + // Uses undefined variable(s). + if (!LeftOp || !RightOp) { + Error Err = Error::success(); + if (!LeftOp) + Err = joinErrors(std::move(Err), LeftOp.takeError()); + if (!RightOp) + Err = joinErrors(std::move(Err), RightOp.takeError()); + return std::move(Err); + } + + return EvalBinop(*LeftOp, *RightOp); } Expected FileCheckNumericSubstitution::getResult() const { - Expected EvaluatedValue = NumExpr->eval(); + Expected EvaluatedValue = NumExprAST->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); return utostr(*EvaluatedValue); @@ -132,15 +146,9 @@ return Error::success(); } -Expected -FileCheckPattern::parseNumericVariableUse(StringRef &Expr, +Expected> +FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, const SourceMgr &SM) const { - bool IsPseudo; - Expected ParseVarResult = parseVariable(Expr, IsPseudo, SM); - if (!ParseVarResult) - return ParseVarResult.takeError(); - StringRef Name = *ParseVarResult; - if (IsPseudo && !Name.equals("@LINE")) return FileCheckParseError::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -150,13 +158,15 @@ // 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()) return FileCheckParseError::get( SM, Name, "using undefined numeric variable '" + Name + "'"); - FileCheckNumericVariable *NumericVariable = VarTableIter->second; + std::shared_ptr NumericVariable = + VarTableIter->second; if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) return FileCheckParseError::get(SM, Name, "numeric variable '" + Name + @@ -165,6 +175,33 @@ return NumericVariable; } +Expected> +FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const { + if (AO == LegacyVar || AO == Any) { + // Try to parse as a numeric variable use. + bool IsPseudo; + Expected ParseVarResult = parseVariable(Expr, IsPseudo, SM); + if (ParseVarResult) { + StringRef Name = *ParseVarResult; + return parseNumericVariableUse(Name, IsPseudo, SM); + } + if (AO != LegacyLiteral && AO != Any) + return ParseVarResult.takeError(); + consumeError(ParseVarResult.takeError()); + } + + if (AO == LegacyLiteral || AO == Any) { + // Otherwise, parse it as a literal. + uint64_t LiteralValue; + if (!Expr.consumeInteger(10 /*Radix*/, LiteralValue)) + return std::make_shared(LiteralValue); + } + + return FileCheckParseError::get(SM, Expr, + "invalid operand format '" + Expr + "'"); +} + static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { return LeftOp + RightOp; } @@ -173,20 +210,16 @@ return LeftOp - RightOp; } -Expected -FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const { - Expected LeftParseResult = - parseNumericVariableUse(Expr, SM); - if (!LeftParseResult) { - return LeftParseResult.takeError(); - } - FileCheckNumericVariable *LeftOp = *LeftParseResult; +Expected> +FileCheckPattern::parseBinop(StringRef &Expr, + std::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; @@ -208,23 +241,24 @@ if (Expr.empty()) return FileCheckParseError::get(SM, Expr, "missing operand in numeric expression"); - uint64_t RightOp; - if (Expr.consumeInteger(10, RightOp)) - return FileCheckParseError::get( - SM, Expr, "invalid offset in numeric expression '" + Expr + "'"); - Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) - return FileCheckParseError::get( - SM, Expr, - "unexpected characters at end of numeric expression '" + Expr + "'"); + // Second operand in legacy numeric expression is a literal. + AllowedOperand AO = Legacy ? LegacyLiteral : Any; + Expected> RightOpResult = + parseNumericOperand(Expr, AO, SM); + if (!RightOpResult) + return RightOpResult; + std::shared_ptr RightOp = *RightOpResult; - return Context->makeNumExpr(EvalBinop, LeftOp, RightOp); + Expr = Expr.ltrim(SpaceChars); + return std::make_shared(EvalBinop, LeftOp, RightOp); } -Expected FileCheckPattern::parseNumericSubstitutionBlock( +Expected> +FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, - Optional &DefinedNumericVariable, + Optional &DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const { + std::shared_ptr NumExprAST = nullptr; // Parse the numeric variable definition. DefinedNumericVariable = None; size_t DefEnd = Expr.find(':'); @@ -239,9 +273,6 @@ if (ParseError) return std::move(ParseError); - DefinedNumericVariable = - Context->makeNumericVariable(this->LineNumber, Name); - DefExpr = DefExpr.ltrim(SpaceChars); if (!DefExpr.empty()) return FileCheckParseError::get(SM, DefExpr, @@ -251,12 +282,31 @@ return FileCheckParseError::get( SM, UseExpr, "unexpected string after variable definition: '" + UseExpr + "'"); - return Context->makeNumExpr(add, nullptr, 0); + + DefinedNumericVariable = + new FileCheckNumericVariable(this->LineNumber, Name); + } else { + // Parse the numeric expression itself. + Expr = Expr.ltrim(SpaceChars); + // First operand in legacy numeric expression is the @LINE pseudo variable. + AllowedOperand AO = Legacy ? LegacyVar : Any; + Expected> ParseResult = + parseNumericOperand(Expr, AO, SM); + while (ParseResult && !Expr.empty()) { + ParseResult = parseBinop(Expr, *ParseResult, Legacy, SM); + // Legacy numeric expressions only allow 2 operands. + if (ParseResult && Legacy && !Expr.empty()) + return FileCheckParseError::get( + SM, Expr, + "unexpected characters at end of numeric expression '" + Expr + + "'"); + } + if (!ParseResult) + return ParseResult; + NumExprAST = *ParseResult; } - // Parse the numeric expression itself. - Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + return NumExprAST; } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -269,8 +319,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)) @@ -374,6 +424,7 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsDefinition = false; + bool Legacy = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; @@ -423,23 +474,24 @@ } else { if (IsPseudo) { MatchStr = OrigMatchStr; - IsNumBlock = true; + Legacy = IsNumBlock = true; } else SubstStr = Name; } } // Parse numeric substitution block. - FileCheckNumExpr *NumExpr; + std::shared_ptr NumExprAST; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected ParseResult = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM); + Expected> ParseResult = + parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, + Legacy, SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - NumExpr = *ParseResult; + NumExprAST = *ParseResult; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); @@ -468,7 +520,7 @@ // previous CHECK patterns, and substitution of numeric expressions. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, NumExpr, + ? Context->makeNumericSubstitution(SubstStr, NumExprAST, SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); @@ -485,7 +537,8 @@ // parseNumericVariableUse() 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 the string variable as defined to detect collisions between @@ -659,13 +712,21 @@ Expected MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { - Error UndefVarError = MatchedValue.takeError(); - assert(UndefVarError.isA() && - "Unexpected error"); - logAllUnhandledErrors(std::move(UndefVarError), OS, - "uses undefined variable "); + Error UndefVarErrors = MatchedValue.takeError(); + /*assert(UndefVarErrors.isA() && + "Unexpected error");*/ + OS << "uses undefined variable(s):"; + handleAllErrors(std::move(UndefVarErrors), + [&](const FileCheckUndefVarError &E) { + OS << " "; + E.log(OS); + }, + [](const ErrorInfoBase &E) { + assert(false && "Unexpected error"); + }); + OS << "\n"; } else { // Substitution succeeded. Print substituted value. OS << "with \""; @@ -757,23 +818,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) { @@ -783,9 +827,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(); } @@ -1746,7 +1791,8 @@ CmdlineVal + "'")); continue; } - auto DefinedNumericVariable = makeNumericVariable(0, VarName); + auto DefinedNumericVariable = + std::make_shared(0, VarName); 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/test/FileCheck/var-scope.txt b/llvm/test/FileCheck/var-scope.txt --- a/llvm/test/FileCheck/var-scope.txt +++ b/llvm/test/FileCheck/var-scope.txt @@ -34,5 +34,5 @@ GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]] ERRUNDEF: expected string not found in input -ERRUNDEFLOCAL: uses undefined variable "LOCAL" -ERRUNDEFLOCNUM: uses undefined variable "LOCNUM" +ERRUNDEFLOCAL: uses undefined variable(s): "LOCAL" +ERRUNDEFLOCNUM: uses undefined variable(s): "LOCNUM" 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,55 +14,79 @@ class FileCheckTest : public ::testing::Test {}; +TEST_F(FileCheckTest, Literal) { + // Eval returns the literal's value. + auto Ten = FileCheckNumExprLiteral(10); + Expected Value = Ten.eval(); + EXPECT_TRUE(static_cast(Value)); + EXPECT_EQ(10U, *Value); +} + +static void expectUndefErrors(std::set ExpectedUndefVarNames, + Error Err) { + handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) { + ExpectedUndefVarNames.erase(E.getVarName()); + }); + EXPECT_TRUE(ExpectedUndefVarNames.empty()); +} + +static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) { + expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err)); +} + TEST_F(FileCheckTest, NumericVariable) { - // Undefined variable: getValue and clearValue fails, setValue works. + // Undefined variable: eval and clearValue fails, error returned by eval + // holds the name of the undefined variable and setValue works. FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO"); EXPECT_EQ("FOO", FooVar.getName()); - llvm::Optional Value = FooVar.getValue(); + Expected Value = FooVar.eval(); EXPECT_FALSE(Value); - EXPECT_TRUE(FooVar.clearValue()); + expectUndefError("FOO", Value.takeError()); EXPECT_FALSE(FooVar.setValue(42)); - // Defined variable: getValue returns value set, setValue fails. - Value = FooVar.getValue(); - EXPECT_TRUE(Value); + // Defined variable: eval returns value set and setValue fails. + Value = FooVar.eval(); + EXPECT_TRUE(static_cast(Value)); EXPECT_EQ(42U, *Value); EXPECT_TRUE(FooVar.setValue(43)); - Value = FooVar.getValue(); - EXPECT_TRUE(Value); + Value = FooVar.eval(); + EXPECT_TRUE(static_cast(Value)); EXPECT_EQ(42U, *Value); - // Clearing variable: getValue fails, clearValue again fails. + // Clearing variable: eval fails and clearValue again fails. Error returned + // by eval holds the name of the cleared variable. EXPECT_FALSE(FooVar.clearValue()); - Value = FooVar.getValue(); + Value = FooVar.eval(); EXPECT_FALSE(Value); + expectUndefError("FOO", Value.takeError()); EXPECT_TRUE(FooVar.clearValue()); } uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } -static void expectUndefError(const Twine &ExpectedStr, Error Err) { - handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) { - EXPECT_EQ(ExpectedStr.str(), E.getVarName()); - }); -} - -TEST_F(FileCheckTest, 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. - Expected Value = NumExpr.eval(); + Expected Value = Binop.eval(); EXPECT_TRUE(static_cast(Value)); EXPECT_EQ(60U, *Value); - // Undefined variable: eval fails, undefined variable returned. We call - // getUndefVarName first to check that it can be called without calling - // eval() first. - FooVar.clearValue(); - Error EvalError = NumExpr.eval().takeError(); - EXPECT_TRUE(errorToBool(std::move(EvalError))); - expectUndefError("FOO", std::move(EvalError)); + // 1 undefined variable: eval fails, error contains name of undefined + // variable. + FooVar->clearValue(); + Value = Binop.eval(); + EXPECT_FALSE(Value); + expectUndefError("FOO", Value.takeError()); + + // 2 undefined variables: eval fails, error contains names of all undefined + // variables. + BarVar->clearValue(); + Value = Binop.eval(); + EXPECT_FALSE(Value); + expectUndefErrors({"FOO", "BAR"}, Value.takeError()); } TEST_F(FileCheckTest, ValidVarNameStart) { @@ -201,7 +225,7 @@ StringRef ExprBufferRef = bufferize(SM, Expr); Optional DefinedNumericVariable; return errorToBool(P.parseNumericSubstitutionBlock( - ExprBufferRef, DefinedNumericVariable, SM) + ExprBufferRef, DefinedNumericVariable, false, SM) .takeError()); } @@ -282,6 +306,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) { @@ -310,7 +337,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. @@ -352,37 +378,36 @@ // 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); + 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); - Expected Value = SubstitutionLine.getResult(); - EXPECT_TRUE(static_cast(Value)); - EXPECT_EQ("42", *Value); - Value = SubstitutionN.getResult(); - EXPECT_TRUE(static_cast(Value)); - EXPECT_EQ("13", *Value); - - // Substitution of an undefined numeric variable fails. - LineVar.clearValue(); - SubstValue = SubstitutionLine.getResult().takeError(); + FileCheckNumericSubstitution(&Context, "N", NVar, 30); + SubstValue = SubstitutionLine.getResult(); + EXPECT_TRUE(static_cast(SubstValue)); + EXPECT_EQ("42", *SubstValue); + SubstValue = SubstitutionN.getResult(); + EXPECT_TRUE(static_cast(SubstValue)); + EXPECT_EQ("10", *SubstValue); + + // Substitution of an undefined numeric variable fails, error holds name of + // undefined variable. + LineVar->clearValue(); + SubstValue = SubstitutionLine.getResult(); EXPECT_FALSE(static_cast(SubstValue)); expectUndefError("@LINE", SubstValue.takeError()); - NVar.clearValue(); - SubstValue = SubstitutionN.getResult().takeError(); + NVar->clearValue(); + SubstValue = SubstitutionN.getResult(); EXPECT_FALSE(static_cast(SubstValue)); expectUndefError("N", SubstValue.takeError()); // Substitution of a defined string variable returns the right value. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); - Value = StringSubstitution.getResult(); - EXPECT_TRUE(static_cast(Value)); - EXPECT_EQ("BAR", *Value); + SubstValue = StringSubstitution.getResult(); + EXPECT_TRUE(static_cast(SubstValue)); + EXPECT_EQ("BAR", *SubstValue); } TEST_F(FileCheckTest, FileCheckContext) { @@ -445,14 +470,15 @@ Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); Optional DefinedNumericVariable; - Expected NumExpr = P.parseNumericSubstitutionBlock( - LocalNumVarRef, DefinedNumericVariable, SM); - Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + Expected> NumExprAST = + P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(static_cast(LocalVar)); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(static_cast(NumExpr)); - Expected NumExprVal = (*NumExpr)->eval(); + Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); + Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + EXPECT_TRUE(static_cast(NumExprAST)); + Expected NumExprVal = (*NumExprAST)->eval(); EXPECT_TRUE(static_cast(NumExprVal)); EXPECT_EQ(*NumExprVal, 18U); EXPECT_TRUE(static_cast(EmptyVar)); @@ -467,11 +493,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_TRUE(errorToBool((*NumExpr)->eval().takeError())); + EXPECT_TRUE(errorToBool((*NumExprAST)->eval().takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(errorToBool(NumExpr.takeError())); + NumExprAST = P.parseNumericSubstitutionBlock( + LocalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(errorToBool(NumExprAST.takeError())); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_TRUE(errorToBool(EmptyVar.takeError())); @@ -485,10 +511,10 @@ EXPECT_TRUE(static_cast(GlobalVar)); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(static_cast(NumExpr)); - NumExprVal = (*NumExpr)->eval(); + NumExprAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(static_cast(NumExprAST)); + NumExprVal = (*NumExprAST)->eval(); EXPECT_TRUE(static_cast(NumExprVal)); EXPECT_EQ(*NumExprVal, 36U); @@ -496,10 +522,10 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(static_cast(NumExpr)); - NumExprVal = (*NumExpr)->eval(); + NumExprAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(static_cast(NumExprAST)); + NumExprVal = (*NumExprAST)->eval(); EXPECT_TRUE(static_cast(NumExprVal)); EXPECT_EQ(*NumExprVal, 36U); }