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,17 +590,15 @@ would match ``mov r5, 42`` and set ``REG`` to the value ``5``. -The syntax of a numeric substitution is ``[[#]]`` where: +The syntax of a numeric substitution is ``[[#]]`` where ```` is an +expression. An expression is recursively defined as: -* ```` is the name of a defined numeric variable. +* a numeric operand, or +* an expression followed by an operator and a numeric operand. -* ```` is an optional operation to perform on the value of ````. - Currently supported operations are ``+`` and ``-``. - - the operation . It must be present if ```` is present, absent - otherwise. - -Spaces are accepted before, after and between any of these elements. +A numeric operand is a previously defined numeric variable, or an integer +literal. The supported operators are ``+`` and ``-``. Spaces are accepted +before, after and between any of these elements. 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 +/// Base class representing the AST of a given expression. +class FileCheckExpressionAST { +public: + virtual ~FileCheckExpressionAST() = 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 an expression. +class FileCheckExpressionLiteral : public FileCheckExpressionAST { +private: + /// Actual value of the literal. + uint64_t Value; + +public: + /// Constructor for a literal. + FileCheckExpressionLiteral(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 an /// 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 FileCheckExpressionAST { private: /// Name of the numeric variable. StringRef Name; @@ -70,7 +118,7 @@ StringRef getName() const { return Name; } /// \returns this variable's value. - Optional getValue() const { return Value; } + Expected eval() const; /// Sets value of this numeric variable if not defined. \returns whether the /// variable was already defined. @@ -87,53 +135,28 @@ /// 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 an expression consisting of either a single numeric -/// variable or a binary operation between a numeric variable and an -/// immediate. -class FileCheckExpression { +/// Class representing a single binary operation in the AST of an expression. +class FileCheckASTBinop : public FileCheckExpressionAST { 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: - FileCheckExpression(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 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,15 +213,14 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - FileCheckExpression *Expression; + std::shared_ptr ExpressionAST; public: - FileCheckNumericSubstitution(FileCheckPatternContext *Context, - StringRef ExpressionStr, - FileCheckExpression *Expression, - size_t InsertIdx) - : FileCheckSubstitution(Context, ExpressionStr, InsertIdx), - Expression(Expression) {} + FileCheckNumericSubstitution( + FileCheckPatternContext *Context, StringRef Expr, + std::shared_ptr ExpressionAST, size_t InsertIdx) + : FileCheckSubstitution(Context, Expr, InsertIdx), + ExpressionAST(ExpressionAST) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -277,15 +299,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 expressions. Used to automatically - /// free the expressions once they are guaranteed to no longer be used. - std::vector> Expressions; - - /// 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. @@ -310,17 +325,6 @@ void clearLocalVars(); private: - /// Makes a new expression instance and registers it for destruction when - /// the context is destroyed. - FileCheckExpression *makeExpression(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, @@ -330,7 +334,8 @@ /// the context is destroyed. FileCheckSubstitution * makeNumericSubstitution(StringRef ExpressionStr, - FileCheckExpression *Expression, size_t InsertIdx); + std::shared_ptr ExpressionAST, + size_t InsertIdx); }; /// Class to represent an error holding a diagnostic with location information @@ -469,16 +474,19 @@ 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 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 LegacyLineExpr indicates whether \p Expr should be a legacy @LINE + /// expression. \returns a pointer to the class instance representing the AST + /// of the 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, - const SourceMgr &SM) const; + bool LegacyLineExpr, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// @@ -503,7 +511,7 @@ Expected 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, @@ -535,13 +543,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 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 { LineVar, Literal, Any }; + /// Parses \p Expr for use of a numeric operand. Accepts both literal values + /// and numeric variables, depending on the value of \p AO. \returns the + /// class representing that operand in the AST of the 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 LegacyLineExpr indicates whether + /// we are parsing a legacy @LINE expression. \returns the class representing + /// the binary operation in the AST of the expression, or an error holding a + /// diagnostic against \p SM otherwise. + Expected> + parseBinop(StringRef &Expr, std::shared_ptr LeftOp, + bool LegacyLineExpr, 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 FileCheckExpression::eval() const { - assert(LeftOp && "Evaluating an empty 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 = Expression->eval(); + Expected EvaluatedValue = ExpressionAST->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); return utostr(*EvaluatedValue); @@ -133,15 +147,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 FileCheckErrorDiagnostic::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -151,13 +159,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 FileCheckErrorDiagnostic::get( SM, Name, "using undefined numeric variable '" + Name + "'"); - FileCheckNumericVariable *NumericVariable = VarTableIter->second; + std::shared_ptr NumericVariable = + VarTableIter->second; if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) return FileCheckErrorDiagnostic::get( SM, Name, @@ -166,6 +176,32 @@ return NumericVariable; } +Expected> +FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const { + if (AO == LineVar || 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 == LineVar) + return ParseVarResult.takeError(); + // Ignore the error and retry parsing as a literal. + consumeError(ParseVarResult.takeError()); + } + + // Otherwise, parse it as a literal. + uint64_t LiteralValue; + if (!Expr.consumeInteger(10 /*Radix*/, LiteralValue)) + return std::make_shared(LiteralValue); + + return FileCheckErrorDiagnostic::get(SM, Expr, + "invalid operand format '" + Expr + "'"); +} + static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { return LeftOp + RightOp; } @@ -174,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 LegacyLineExpr, 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->makeExpression(add, LeftOp, 0); SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); char Operator = popFront(Expr); binop_eval_t EvalBinop; @@ -208,22 +240,24 @@ if (Expr.empty()) return FileCheckErrorDiagnostic::get(SM, Expr, "missing operand in expression"); - uint64_t RightOp; - if (Expr.consumeInteger(10, RightOp)) - return FileCheckErrorDiagnostic::get( - SM, Expr, "invalid offset in expression '" + Expr + "'"); - Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) - return FileCheckErrorDiagnostic::get( - SM, Expr, "unexpected characters at end of expression '" + Expr + "'"); + // Second operand in legacy @LINE expression is a literal. + AllowedOperand AO = LegacyLineExpr ? Literal : Any; + Expected> RightOpResult = + parseNumericOperand(Expr, AO, SM); + if (!RightOpResult) + return RightOpResult; + std::shared_ptr RightOp = *RightOpResult; - return Context->makeExpression(EvalBinop, LeftOp, RightOp); + Expr = Expr.ltrim(SpaceChars); + return std::make_shared(EvalBinop, LeftOp, RightOp); } -Expected FileCheckPattern::parseNumericSubstitutionBlock( +Expected> +FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, - const SourceMgr &SM) const { + bool LegacyLineExpr, const SourceMgr &SM) const { + std::shared_ptr ExpressionAST = nullptr; // Parse the numeric variable definition. DefinedNumericVariable = None; size_t DefEnd = Expr.find(':'); @@ -238,9 +272,6 @@ if (ErrorDiagnostic) return std::move(ErrorDiagnostic); - DefinedNumericVariable = - Context->makeNumericVariable(this->LineNumber, Name); - DefExpr = DefExpr.ltrim(SpaceChars); if (!DefExpr.empty()) return FileCheckErrorDiagnostic::get( @@ -250,12 +281,30 @@ return FileCheckErrorDiagnostic::get( SM, UseExpr, "unexpected string after variable definition: '" + UseExpr + "'"); - return Context->makeExpression(add, nullptr, 0); + + DefinedNumericVariable = + new FileCheckNumericVariable(this->LineNumber, Name); + } else { + // Parse the expression itself. + Expr = Expr.ltrim(SpaceChars); + // First operand in legacy @LINE expression is the @LINE pseudo variable. + AllowedOperand AO = LegacyLineExpr ? LineVar : Any; + Expected> ParseResult = + parseNumericOperand(Expr, AO, SM); + while (ParseResult && !Expr.empty()) { + ParseResult = parseBinop(Expr, *ParseResult, LegacyLineExpr, SM); + // Legacy @LINE expressions only allow 2 operands. + if (ParseResult && LegacyLineExpr && !Expr.empty()) + return FileCheckErrorDiagnostic::get( + SM, Expr, + "unexpected characters at end of expression '" + Expr + "'"); + } + if (!ParseResult) + return ParseResult; + ExpressionAST = *ParseResult; } - // Parse the expression itself. - Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + return ExpressionAST; } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -268,8 +317,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)) @@ -373,12 +422,13 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsDefinition = false; + bool IsLegacyLineExpr = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; size_t SubstInsertIdx = RegExStr.size(); - // Parse string variable or legacy expression. + // Parse string variable or legacy @LINE expression. if (!IsNumBlock) { size_t VarEndIdx = MatchStr.find(":"); size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); @@ -422,23 +472,24 @@ } else { if (IsPseudo) { MatchStr = OrigMatchStr; - IsNumBlock = true; + IsLegacyLineExpr = IsNumBlock = true; } else SubstStr = Name; } } // Parse numeric substitution block. - FileCheckExpression *Expression; + std::shared_ptr ExpressionAST; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected ParseResult = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM); + Expected> ParseResult = + parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, + IsLegacyLineExpr, SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - Expression = *ParseResult; + ExpressionAST = *ParseResult; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); @@ -466,7 +517,7 @@ // previous CHECK patterns, and substitution of expressions. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, Expression, + ? Context->makeNumericSubstitution(SubstStr, ExpressionAST, SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); @@ -484,7 +535,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 @@ -655,7 +707,7 @@ Expected MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { bool UndefSeen = false; handleAllErrors(MatchedValue.takeError(), @@ -664,9 +716,10 @@ [](const FileCheckErrorDiagnostic &E) {}, [&](const FileCheckUndefVarError &E) { if (!UndefSeen) { - OS << "uses undefined variable "; + OS << "uses undefined variable(s):"; UndefSeen = true; } + OS << " "; E.log(OS); }, [](const ErrorInfoBase &E) { @@ -763,23 +816,6 @@ return VarIter->second; } -FileCheckExpression * -FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - Expressions.push_back(llvm::make_unique( - EvalBinop, OperandLeft, OperandRight)); - return Expressions.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) { @@ -789,10 +825,10 @@ } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, FileCheckExpression *Expression, - size_t InsertIdx) { + StringRef ExpressionStr, + std::shared_ptr ExpressionAST, size_t InsertIdx) { Substitutions.push_back(llvm::make_unique( - this, ExpressionStr, Expression, InsertIdx)); + this, ExpressionStr, ExpressionAST, InsertIdx)); return Substitutions.back().get(); } @@ -1772,7 +1808,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 operation '*' 51 52 BAD10: [[@LINE-x]] -53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in 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 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 = FileCheckExpressionLiteral(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, Expression) { - FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42); - FileCheckExpression Expression = FileCheckExpression(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 = Expression.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 = Expression.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); - FileCheckExpression LineExpression = FileCheckExpression(doAdd, &LineVar, 0); - FileCheckExpression NExpression = FileCheckExpression(doAdd, &NVar, 3); + auto LineVar = std::make_shared("@LINE", 42); + auto NVar = std::make_shared("N", 10); FileCheckNumericSubstitution SubstitutionLine = - FileCheckNumericSubstitution(&Context, "@LINE", &LineExpression, 12); + FileCheckNumericSubstitution(&Context, "@LINE", LineVar, 12); FileCheckNumericSubstitution SubstitutionN = - FileCheckNumericSubstitution(&Context, "N", &NExpression, 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 Expression = P.parseNumericSubstitutionBlock( - LocalNumVarRef, DefinedNumericVariable, SM); - Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + Expected> ExpressionAST = + P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, + false /*Legacy*/, SM); EXPECT_TRUE(static_cast(LocalVar)); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(static_cast(Expression)); - Expected ExpressionVal = (*Expression)->eval(); + Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); + Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + EXPECT_TRUE(static_cast(ExpressionAST)); + Expected ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 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((*Expression)->eval().takeError())); + EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - Expression = P.parseNumericSubstitutionBlock(LocalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(errorToBool(Expression.takeError())); + ExpressionAST = P.parseNumericSubstitutionBlock( + LocalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(errorToBool(ExpressionAST.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); - Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(static_cast(Expression)); - ExpressionVal = (*Expression)->eval(); + ExpressionAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(static_cast(ExpressionAST)); + ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); @@ -496,10 +522,10 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef, - DefinedNumericVariable, SM); - EXPECT_TRUE(static_cast(Expression)); - ExpressionVal = (*Expression)->eval(); + ExpressionAST = P.parseNumericSubstitutionBlock( + GlobalNumVarRef, DefinedNumericVariable, false /*Legacy*/, SM); + EXPECT_TRUE(static_cast(ExpressionAST)); + ExpressionVal = (*ExpressionAST)->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); }