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,11 @@ 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 the result of evaluating - ```` that can be used in ``CHECK:`` lines. See section + Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to + the result of evaluating ```` that can be used in + ``CHECK:`` lines. See section ``FileCheck Numeric Variables and Expressions`` for details on supported numeric expressions. @@ -578,27 +579,44 @@ substitution. This allows ``CHECK:`` directives to verify a numeric relation between two numbers, such as the need for consecutive registers to be used. -The syntax to define a numeric variable is ``[[#:]]`` where -```` is the name of the numeric variable to define to the matching -value. +The syntax to define a numeric variable is ``[[#%,:]]`` where: + +* ``%`` is an optional scanf-style matching format specifier to + indicate what number format to match (e.g. hex number). Currently accepted + format specifier are ``%u``, ``%x`` and ``%X``. If absent, the format + specifier defaults to ``%u``. + +* ```` is the name of the numeric variable to define to the matching + value. For example: .. code-block:: llvm - ; CHECK: mov r[[#REG:]], 42 + ; CHECK: mov r[[#REG:]], 0x[[#%X,IMM:]] + +would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM`` +to the value ``61680``. + +The syntax of a numeric substitution is ``[[#%:]]`` where: -would match ``mov r5, 42`` and set ``REG`` to the value ``5``. +* ``%`` is the same matching format specifier as for defining numeric + variables but acting as a printf-style format to indicate how a numeric + expression value should be matched against. If absent, the format specifier + is inferred from the matching format of the numeric variable(s) used by the + expression constraint if any and they are all the same, and default to ``%u`` + if no numeric variable is used. In case of conflict between matching formats + of several numeric variables the format specifier is mandatory. -The syntax of a numeric substitution is ``[[#]]`` where ```` is an -expression. An expression is recursively defined as: +* ```` is an expression. An expression is in turn recursively defined + as: -* a numeric operand, or -* an expression followed by an operator and a numeric operand. + * a numeric operand, or + * an expression followed by an operator and a numeric operand. -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. + 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: @@ -606,6 +624,7 @@ ; CHECK: load r[[#REG:]], [r0] ; CHECK: load r[[#REG+1]], [r1] + ; CHECK: Loading from 0x[[#%x,ADDR:] to 0x[[#ADDR + 7]] The above example would match the text: @@ -613,6 +632,7 @@ load r5, [r0] load r6, [r1] + Loading from 0xa0463440 to 0xa0463447 but would not match the text: @@ -620,8 +640,10 @@ load r5, [r0] load r7, [r1] + Loading from 0xa0463440 to 0xa0463443 -due to ``7`` being unequal to ``5 + 1``. +Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to +``a0463440 + 7``. A numeric variable can also be defined to the result of a numeric expression, in which case the numeric expression constraint is checked and if verified the 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,6 +40,55 @@ // Numeric substitution handling code. //===----------------------------------------------------------------------===// +/// Bitfield representing the format an expression value should be printed into +/// for matching. Used to represent both explicit format specifiers as well as +/// implicit format from using numeric variables. +struct FileCheckExpressionFormat { + /// Value should be printed as hex number. + unsigned Hex : 1; + /// Value should be printed using upper case letters, only used for hex + /// numbers. + unsigned Cap : 1; + + /// When unset, denote absence of format and thus that all of the other + /// fields are to be ignored. Used for implicit format of literals and empty + /// expressions. + unsigned Valid : 1; + + /// If set, there are several conflicting implicit formats in an expression. + unsigned Conflict : 1; + + /// Define format equality: formats are equal if all bits are identical. + bool operator==(const FileCheckExpressionFormat &other); + bool operator!=(const FileCheckExpressionFormat &other) { + return !(*this == other); + } + + /// \returns wildcard regexp StringRef to match any value in the format + /// represented by this instance. + StringRef getWildcardRegex() const; + + /// \returns the string representation of \p Value in the format represented + /// by this instance. + std::string getMatchingString(uint64_t Value) const; + + /// \returns the value corresponding to string representation \p StrVal + /// according to the matching format represented by this instance or an error + /// with diagnostic against \p SM if \p StrVal does not correspond to a valid + /// and representable value. + Expected valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const; +}; + +/// Initializer for an expression without format. +const FileCheckExpressionFormat FormatNone = {0, 0, 0, 0}; +/// Initializer for an expression matched as unsigned value. +const FileCheckExpressionFormat FormatUnsigned = {0, 0, 1, 0}; +/// Initializer for an expression matched as lower case hex value. +const FileCheckExpressionFormat FormatLowHex = {1, 0, 1, 0}; +/// Initializer for an expression matched as capital case hex value. +const FileCheckExpressionFormat FormatCapHex = {1, 1, 1, 0}; + /// Base class representing the AST of a given expression. class FileCheckExpressionAST { public: @@ -48,6 +97,11 @@ /// Evaluates and \returns the value of the expression represented by this /// AST or an error if evaluation fails. virtual Expected eval() const = 0; + + /// \returns implicit format of this AST, FormatConflict if implicit formats + /// of the AST's components conflict and FormatNone if the AST has no + /// implicit format (e.g. AST is made of a single literal). + virtual FileCheckExpressionFormat getImplicitFormat() const = 0; }; /// Class representing a literal in the AST of an expression. @@ -62,6 +116,9 @@ /// \returns the literal's value. Expected eval() const { return Value; } + + /// \returns implicit format of this literal, therefore FormatNone. + FileCheckExpressionFormat getImplicitFormat() const { return FormatNone; } }; /// Class to represent an undefined variable error which prints that variable's @@ -88,6 +145,33 @@ } }; +/// Class representing an expression and its matching format. +class FileCheckExpression { +private: + /// Pointer to AST of the expression. + std::shared_ptr AST; + + /// Matching format, i.e format (e.g. hex upper case letters) to use for the + /// value when matching it. + FileCheckExpressionFormat Format; + +public: + /// Generic constructor for an expression whose equality constraint is + /// represented by \p AST and matching format is \p Format. If matching format + /// is unset (ie. no explicit or implicit matching format), set it to default + /// one (unsigned decimal integer). + FileCheckExpression(std::shared_ptr AST, + FileCheckExpressionFormat Format); + + /// \returns pointer to AST of the expression. Pointer is guaranteed to be + /// valid as long as this object is. + FileCheckExpressionAST *getAST() const { return AST.get(); } + + /// \returns effective format of this expression, ie (i) its explicit format + /// if any, (ii) its implicit format if any or (iii) the default format. + FileCheckExpressionFormat getEffectiveFormat() { return Format; } +}; + /// 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 @@ -99,8 +183,9 @@ /// Pointer to expression defining this numeric variable. Null for pseudo /// variable whose value is known at parse time (e.g. @LINE pseudo variable) - /// or cleared local variable. - std::shared_ptr ExpressionAST; + /// or cleared local variable. If expression is empty Expression points to a + /// FileCheckExpression with a null AST. + FileCheckExpression *Expression; /// Value of numeric variable, if defined, or None otherwise. Optional Value; @@ -111,21 +196,22 @@ public: /// Constructor for a variable \p Name defined at line \p DefLineNumber to - /// the expression represented by \p ExpressionAST. - FileCheckNumericVariable( - size_t DefLineNumber, StringRef Name, - std::shared_ptr ExpressionAST) - : Name(Name), ExpressionAST(ExpressionAST), DefLineNumber(DefLineNumber) { - } + /// the expression represented by \p Expression. + FileCheckNumericVariable(size_t DefLineNumber, StringRef Name, + FileCheckExpression *Expression) + : Name(Name), Expression(Expression), DefLineNumber(DefLineNumber) {} /// Constructor for numeric variable \p Name with a known \p Value at parse /// time (e.g. the @LINE numeric variable). FileCheckNumericVariable(StringRef Name, uint64_t Value) - : Name(Name), ExpressionAST(nullptr), Value(Value), DefLineNumber(0) {} + : Name(Name), Expression(nullptr), Value(Value), DefLineNumber(0) {} /// \returns name of this numeric variable. StringRef getName() const { return Name; } + /// \returns expression associated with this numeric variable. + FileCheckExpression *getExpression() const { return Expression; } + /// \returns this variable's value. Expected eval() const; @@ -133,6 +219,9 @@ /// performing the substitutions. bool isMatchTimeKnown() const; + /// \returns implicit format of this numeric variable. + FileCheckExpressionFormat getImplicitFormat() const; + /// Sets value of this numeric variable if not defined. \returns whether the /// variable was already defined. bool setValue(uint64_t Value); @@ -171,6 +260,11 @@ /// \returns an error if a numeric variable used is undefined, or the /// expression value otherwise. Expected eval() const; + + /// \returns implicit format of this AST, FormatConflict if implicit formats + /// of the AST's components conflict and Format none if the AST has no + /// implicit format (e.g. AST is made of a single literal). + FileCheckExpressionFormat getImplicitFormat() const; }; class FileCheckPatternContext; @@ -226,14 +320,15 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - std::shared_ptr ExpressionAST; + FileCheckExpression *Expression; public: - FileCheckNumericSubstitution( - FileCheckPatternContext *Context, StringRef ExpressionStr, - std::shared_ptr ExpressionAST, size_t InsertIdx) + FileCheckNumericSubstitution(FileCheckPatternContext *Context, + StringRef ExpressionStr, + FileCheckExpression *Expression, + size_t InsertIdx) : FileCheckSubstitution(Context, ExpressionStr, InsertIdx), - ExpressionAST(ExpressionAST) {} + Expression(Expression) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -315,6 +410,10 @@ 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 substitutions. Used to automatically free /// them once they are guaranteed to no longer be used. std::vector> Substitutions; @@ -338,6 +437,12 @@ void clearLocalVars(); private: + /// Makes a new expression instance and registers it for destruction when the + /// context is destroyed. + FileCheckExpression * + makeExpression(std::shared_ptr AST, + FileCheckExpressionFormat Format); + /// Makes a new string substitution and registers it for destruction when the /// context is destroyed. FileCheckSubstitution *makeStringSubstitution(StringRef VarName, @@ -347,8 +452,7 @@ /// the context is destroyed. FileCheckSubstitution * makeNumericSubstitution(StringRef ExpressionStr, - std::shared_ptr ExpressionAST, - size_t InsertIdx); + FileCheckExpression *Expression, size_t InsertIdx); }; /// Class to represent an error holding a diagnostic with location information @@ -426,12 +530,12 @@ std::map VariableDefs; /// Structure representing the definition of a numeric variable in a pattern. - /// It holds the pointer to the class representing the numeric variable whose - /// value is being defined and the number of the parenthesis group in - /// RegExStr to capture that value. + /// It holds the pointer to the class instance holding the value and matching + /// format of the numeric variable whose value is being defined and the + /// number of the parenthesis group in RegExStr to capture that value. struct FileCheckNumericVariableMatch { - /// Pointer to class representing the numeric variable whose value is being - /// defined. + /// Pointer to class instance holding the value and matching format of the + /// numeric variable being defined. FileCheckNumericVariable *DefinedNumericVariable; /// Number of the parenthesis group in RegExStr that captures the value of @@ -484,14 +588,13 @@ /// where 0 indicate the command-line. Parameter \p LegacyLineExpr indicates /// whether \p Expr should be a legacy @LINE expression and \p Context points /// to the class instance holding the live string and numeric variables. - /// \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. - static Expected> - parseNumericSubstitutionBlock( + /// \returns a pointer to the class instance representing 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. + static Expected parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, bool LegacyLineExpr, size_t LineNumber, FileCheckPatternContext *Context, 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,28 +24,73 @@ using namespace llvm; +bool FileCheckExpressionFormat:: +operator==(const FileCheckExpressionFormat &other) { + return Valid == other.Valid && Hex == other.Hex && Cap == other.Cap; +} + +StringRef FileCheckExpressionFormat::getWildcardRegex() const { + assert(Valid && !Conflict && "Trying to match value with invalid format"); + if (!Hex) + return StringRef("[[:digit:]]+"); + if (Cap) + return StringRef("[[:digit:]A-F]+"); + return StringRef("[[:digit:]a-f]+"); +} + +std::string FileCheckExpressionFormat::getMatchingString(uint64_t Value) const { + assert(Valid && !Conflict && "Trying to match value with invalid format"); + if (Hex) + return utohexstr(Value, !Cap); + return utostr(Value); +} + +Expected +FileCheckExpressionFormat::valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const { + unsigned Radix = Hex ? 16 : 10; + uint64_t Value; + + if (StrVal.getAsInteger(Radix, Value)) + return FileCheckErrorDiagnostic::get(SM, StrVal, + "Unable to represent numeric value"); + + return Value; +} + +FileCheckExpression::FileCheckExpression( + std::shared_ptr AST, + FileCheckExpressionFormat Format) + : AST(AST) { + this->Format = Format.Valid ? Format : FormatUnsigned; +} + Expected FileCheckNumericVariable::eval() const { if (Value) return *Value; - if (ExpressionAST == nullptr) + if (Expression == nullptr || Expression->getAST() == nullptr) return make_error(Name); - return ExpressionAST->eval(); + return Expression->getAST()->eval(); } bool FileCheckNumericVariable::isMatchTimeKnown() const { if (Value) return true; - return ExpressionAST != nullptr; + return Expression != nullptr && Expression->getAST() != nullptr; +} + +FileCheckExpressionFormat FileCheckNumericVariable::getImplicitFormat() const { + return Expression ? Expression->getEffectiveFormat() : FormatNone; } bool FileCheckNumericVariable::setValue(uint64_t NewValue) { if (Value) return true; - if (ExpressionAST != nullptr) { - Expected EvaluatedValue = ExpressionAST->eval(); + if (Expression != nullptr && Expression->getAST() != nullptr) { + Expected EvaluatedValue = Expression->getAST()->eval(); if (!EvaluatedValue || *EvaluatedValue != NewValue) return true; } @@ -57,7 +102,7 @@ if (!Value) return true; Value = None; - ExpressionAST = nullptr; + Expression = nullptr; return false; } @@ -78,11 +123,25 @@ return EvalBinop(*LeftOp, *RightOp); } +FileCheckExpressionFormat FileCheckASTBinop::getImplicitFormat() const { + FileCheckExpressionFormat LeftFormat = LeftOp->getImplicitFormat(); + FileCheckExpressionFormat RightFormat = RightOp->getImplicitFormat(); + + FileCheckExpressionFormat Format = + (LeftFormat.Valid) ? LeftFormat : RightFormat; + if (LeftFormat.Valid && RightFormat.Valid && LeftFormat != RightFormat) + Format.Conflict = 1; + + return Format; +} + Expected FileCheckNumericSubstitution::getResult() const { - Expected EvaluatedValue = ExpressionAST->eval(); + assert(Expression->getAST() != nullptr && "Substituting empty expression"); + Expected EvaluatedValue = Expression->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); - return utostr(*EvaluatedValue); + FileCheckExpressionFormat Format = Expression->getEffectiveFormat(); + return Format.getMatchingString(*EvaluatedValue); } Expected FileCheckStringSubstitution::getResult() const { @@ -130,6 +189,12 @@ // FileCheck input canonicalization. StringRef SpaceChars = " \t"; +// Parsing helper function that strips the string in SkipStr from S. Returns +// true if string SkipStr was not in S. Returns false otherwise. +static bool stripFront(StringRef &S, const StringRef &SkipStr) { + return !S.consume_front(SkipStr); +} + // Parsing helper function that strips the first character in S and returns it. static char popFront(StringRef &S) { char C = S.front(); @@ -217,8 +282,9 @@ } // Otherwise, parse it as a literal. + unsigned Radix = (AO == Literal) ? 10 : 0; uint64_t LiteralValue; - if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) + if (!Expr.consumeInteger(Radix, LiteralValue)) return std::make_shared(LiteralValue); return FileCheckErrorDiagnostic::get(SM, Expr, @@ -275,8 +341,7 @@ return std::make_shared(EvalBinop, LeftOp, RightOp); } -Expected> -FileCheckPattern::parseNumericSubstitutionBlock( +Expected FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, bool LegacyLineExpr, size_t LineNumber, FileCheckPatternContext *Context, @@ -284,6 +349,40 @@ std::shared_ptr ExpressionAST = nullptr; StringRef DefExpr = StringRef(); DefinedNumericVariable = None; + FileCheckExpressionFormat ExplicitFormat = FormatNone; + + // Parse format specifier. + size_t FormatSpecEnd = Expr.find(','); + if (FormatSpecEnd != StringRef::npos) { + Expr = Expr.ltrim(SpaceChars); + if (stripFront(Expr, "%")) + return FileCheckErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + + // Check for unknown matching format specifier and set matching format in + // class instance representing this expression. + SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); + switch (popFront(Expr)) { + case 'u': + ExplicitFormat = FormatUnsigned; + break; + case 'x': + ExplicitFormat = FormatLowHex; + break; + case 'X': + ExplicitFormat = FormatCapHex; + break; + default: + return FileCheckErrorDiagnostic::get( + SM, fmtloc, "invalid format specifier in expression"); + } + + Expr = Expr.ltrim(SpaceChars); + if (stripFront(Expr, ",")) + return FileCheckErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + } + // Save variable definition expression if any. size_t DefEnd = Expr.find(':'); if (DefEnd != StringRef::npos) { @@ -293,6 +392,7 @@ // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); + StringRef UseExpr = Expr; if (!Expr.empty()) { // First operand in legacy @LINE expression is the @LINE pseudo variable. AllowedOperand AO = LegacyLineExpr ? LineVar : Any; @@ -308,10 +408,27 @@ "unexpected characters at end of expression '" + Expr + "'"); } if (!ParseResult) - return ParseResult; + return ParseResult.takeError(); ExpressionAST = *ParseResult; } + FileCheckExpressionFormat Format, + ImplicitFormat = + ExpressionAST ? ExpressionAST->getImplicitFormat() : FormatNone; + // Select explicit matching format if any, implicit one otherwise. Error out + // in case of conflicting implicit format without explicit format. + if (ExplicitFormat.Valid) + Format = ExplicitFormat; + else if (ImplicitFormat.Conflict) + return FileCheckErrorDiagnostic::get( + SM, UseExpr, + "variables with conflicting format specifier: need an explicit one"); + else + Format = ImplicitFormat; + + FileCheckExpression *Expression = + Context->makeExpression(ExpressionAST, Format); + // Parse the numeric variable definition. if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); @@ -327,10 +444,10 @@ SM, DefExpr, "invalid numeric variable definition"); DefinedNumericVariable = - new FileCheckNumericVariable(LineNumber, Name, ExpressionAST); + new FileCheckNumericVariable(LineNumber, Name, Expression); } - return ExpressionAST; + return Expression; } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -508,10 +625,10 @@ } // Parse numeric substitution block. - std::shared_ptr ExpressionAST; + FileCheckExpression *Expression; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected> ParseResult = + Expected ParseResult = parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, IsLegacyLineExpr, LineNumber, Context, SM); @@ -519,15 +636,17 @@ logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - ExpressionAST = *ParseResult; + Expression = *ParseResult; IsDefinition = static_cast(DefinedNumericVariable); - SubstNeeded = ExpressionAST != nullptr; + SubstNeeded = Expression->getAST() != nullptr; if (IsDefinition) DefName = (*DefinedNumericVariable)->getName(); if (SubstNeeded) SubstStr = MatchStr; - else - MatchRegexp = StringRef("[0-9]+"); + else { + FileCheckExpressionFormat Format = Expression->getEffectiveFormat(); + MatchRegexp = Format.getWildcardRegex(); + } } // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. @@ -585,7 +704,7 @@ // previous CHECK patterns, and substitution of expressions. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, ExpressionAST, + ? Context->makeNumericSubstitution(SubstStr, Expression, SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); @@ -701,11 +820,13 @@ NumericVariableMatch.DefinedNumericVariable; StringRef MatchedValue = MatchInfo[CaptureParenGroup]; - uint64_t Val; - if (MatchedValue.getAsInteger(10, Val)) - return FileCheckErrorDiagnostic::get(SM, MatchedValue, - "Unable to represent numeric value"); - if (DefinedNumericVariable->setValue(Val)) + assert(DefinedNumericVariable->getExpression() != nullptr); + FileCheckExpressionFormat Format = + DefinedNumericVariable->getExpression()->getEffectiveFormat(); + Expected Value = Format.valueFromStringRepr(MatchedValue, SM); + if (!Value) + return Value.takeError(); + if (DefinedNumericVariable->setValue(*Value)) llvm_unreachable("Numeric variable redefined"); } @@ -853,6 +974,13 @@ return VarIter->second; } +FileCheckExpression *FileCheckPatternContext::makeExpression( + std::shared_ptr AST, + FileCheckExpressionFormat Format) { + Expressions.push_back(llvm::make_unique(AST, Format)); + return Expressions.back().get(); +} + FileCheckSubstitution * FileCheckPatternContext::makeStringSubstitution(StringRef VarName, size_t InsertIdx) { @@ -862,10 +990,10 @@ } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, - std::shared_ptr ExpressionAST, size_t InsertIdx) { + StringRef ExpressionStr, FileCheckExpression *Expression, + size_t InsertIdx) { Substitutions.push_back(llvm::make_unique( - this, ExpressionStr, ExpressionAST, InsertIdx)); + this, ExpressionStr, Expression, InsertIdx)); return Substitutions.back().get(); } @@ -1833,16 +1961,15 @@ // class instance. StringRef CmdlineDefExpr = CmdlineDef.substr(1); Optional DefinedNumericVariable; - Expected> ExpressionASTResult = + Expected ExpressionResult = FileCheckPattern::parseNumericSubstitutionBlock( CmdlineDefExpr, DefinedNumericVariable, false, 0, this, SM); - if (!ExpressionASTResult) { - Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError()); + if (!ExpressionResult) { + Errs = joinErrors(std::move(Errs), ExpressionResult.takeError()); continue; } - std::shared_ptr ExpressionAST = - *ExpressionASTResult; - Expected Value = ExpressionAST->eval(); + FileCheckExpression *Expression = *ExpressionResult; + Expected Value = Expression->getAST()->eval(); if (!Value) { Errs = joinErrors(std::move(Errs), Value.takeError()); continue; 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 @@ -10,57 +10,61 @@ ; RUN: not FileCheck -check-prefix BAD9 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR9 %s ; RUN: not FileCheck -check-prefix BAD10 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR10 %s ; RUN: not FileCheck -check-prefix BAD11 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR11 %s -13 -14 aaa -15 bbb -16 ccc -17 CHECK: [[@LINE-3]] {{a}}aa -18 CHECK: [[@LINE-3]] {{b}}bb -19 CHECK: [[@LINE-3]] {{c}}cc -20 foobar -21 CHECK: [[@LINE-1]] {{foo}}bar -22 -23 arst CHECK: [[@LINE]] {{a}}rst -24 -25 BAD1: [[@LINE:cant-have-regex]] -26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in string variable definition -27 -28 BAD2: [[ @LINE]] -29 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace -30 -31 BAD3: [[@LINE ]] -32 ERR3: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace -33 -34 BAD4: [[ @LINE-1]] -35 ERR4: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace -36 -37 BAD5: [[@LINE -1]] -38 ERR5: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace -39 -40 BAD6: [[@LINE- 1]] -41 ERR6: line-count.txt:[[#@LINE-1]]:18: error: unexpected whitespace -42 -43 BAD7: [[@LINE-1 ]] -44 ERR7: line-count.txt:[[#@LINE-1]]:19: error: unexpected whitespace -45 -46 BAD8: [[@LIN]] -47 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN' -48 -49 BAD9: [[@LINE*2]] -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 operand format 'x' -54 -55 BAD11: [[@LINE-1x]] -56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x' -57 -58 CHECK: [[#@LINE]] CHECK -59 CHECK: [[# @LINE]] CHECK -60 CHECK: [[# @LINE ]] CHECK +; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR12 %s +14 +15 aaa +16 bbb +17 ccc +18 CHECK: [[@LINE-3]] {{a}}aa +19 CHECK: [[@LINE-3]] {{b}}bb +20 CHECK: [[@LINE-3]] {{c}}cc +21 foobar +22 CHECK: [[@LINE-1]] {{foo}}bar +23 +24 arst CHECK: [[@LINE]] {{a}}rst +25 +26 BAD1: [[@LINE:cant-have-regex]] +27 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in string variable definition +28 +29 BAD2: [[ @LINE]] +30 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace +31 +32 BAD3: [[@LINE ]] +33 ERR3: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace +34 +35 BAD4: [[ @LINE-1]] +36 ERR4: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace +37 +38 BAD5: [[@LINE -1]] +39 ERR5: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace +40 +41 BAD6: [[@LINE- 1]] +42 ERR6: line-count.txt:[[#@LINE-1]]:18: error: unexpected whitespace +43 +44 BAD7: [[@LINE-1 ]] +45 ERR7: line-count.txt:[[#@LINE-1]]:19: error: unexpected whitespace +46 +47 BAD8: [[@LIN]] +48 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN' +49 +50 BAD9: [[@LINE*2]] +51 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*' +52 +53 BAD10: [[@LINE-x]] +54 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x' +55 +56 BAD11: [[@LINE-1x]] +57 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x' +58 +59 BAD12: [[@LINE-0xA]] +60 ERR12: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA' 61 -62 CHECK: [[#@LINE-1]] -63 CHECK: [[# @LINE-1]] CHECK -64 CHECK: [[# @LINE -1]] CHECK -65 CHECK: [[# @LINE - 1]] CHECK -66 CHECK: [[# @LINE - 1 ]] CHECK +62 CHECK: [[#@LINE]] CHECK +63 CHECK: [[# @LINE]] CHECK +64 CHECK: [[# @LINE ]] CHECK +65 +66 CHECK: [[#@LINE-1]] +67 CHECK: [[# @LINE-1]] CHECK +68 CHECK: [[# @LINE -1]] CHECK +69 CHECK: [[# @LINE - 1]] CHECK +70 CHECK: [[# @LINE - 1 ]] CHECK diff --git a/llvm/test/FileCheck/numeric-defines-diagnostics.txt b/llvm/test/FileCheck/numeric-defines-diagnostics.txt --- a/llvm/test/FileCheck/numeric-defines-diagnostics.txt +++ b/llvm/test/FileCheck/numeric-defines-diagnostics.txt @@ -1,12 +1,12 @@ ; Test incorrect syntax for -D# option is correctly diagnosed. ; Invalid variable name: starts with a digit. -RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT +RUN: not FileCheck -D#10VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRCLINAME -NUMERRCLIFMT: Global defines:1:46: error: invalid variable name -NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) -NUMERRCLIFMT-NEXT: {{^ \^$}} +NUMERRCLINAME: Global defines:1:46: error: invalid variable name +NUMERRCLINAME-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) +NUMERRCLINAME-NEXT: {{^ \^$}} ; Invalid definition of pseudo variable. RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \ @@ -23,3 +23,11 @@ NUMERRCLITRAIL: Global defines:1:51: error: invalid numeric variable definition NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}}) NUMERRCLITRAIL-NEXT: {{^ \^$}} + +; Invalid format for variable. +RUN: not FileCheck -D#,VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRCLIFMT + +NUMERRCLIFMT: Global defines:1:45: error: invalid matching format specification in expression +NUMERRCLIFMT-NEXT: Global define #1: #,VALUE=10 (parsed as: {{\[\[#,VALUE:10\]\]}}) +NUMERRCLIFMT-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/numeric-defines.txt b/llvm/test/FileCheck/numeric-defines.txt --- a/llvm/test/FileCheck/numeric-defines.txt +++ b/llvm/test/FileCheck/numeric-defines.txt @@ -1,22 +1,22 @@ ; Test functionality of -D# option: numeric variables are defined to the right ; value and CHECK directives using them match as expected given the value set. -RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefix CHECKNUM -input-file %s %s -RUN: not FileCheck -D#NUMVAL2=8 -check-prefix CHECKNUM -input-file %s %s 2>&1 \ +RUN: FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefix CHECKNUM -input-file %s %s +RUN: not FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+6' -check-prefix CHECKNUM -input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG -RUN: not FileCheck -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \ +RUN: not FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+4' -check-prefix NUMNOT -input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace -check-prefix NOT-NUMERRMSG -RUN: FileCheck -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s +RUN: FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+6' -check-prefixes NUMNOT -input-file %s %s -Numeric value #2 = 12 +Numeric value #2 = C CHECKNUM: Numeric value #2 = [[#NUMVAL2]] NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]] NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input NUMERRMSG: defines.txt:1:1: note: scanning from here -NUMERRMSG: defines.txt:1:1: note: with "NUMVAL2" equal to "8" +NUMERRMSG: defines.txt:1:1: note: with "NUMVAL2" equal to "E" NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input -NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here -NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "NUMVAL2" equal to "12" +NOT-NUMERRMSG: defines.txt:[[#NUMVALLINE:@LINE-10]]:1: note: found here +NOT-NUMERRMSG: defines.txt:[[#NUMVALLINE]]:1: note: with "NUMVAL2" equal to "C" diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -2,85 +2,210 @@ ; We use CHECK-NEXT directives to force a match on all lines with digits. -; Numeric variable definition without spaces. -DEF NO SPC +; Numeric variable definition with default matching format. +DEF DEFAULT FMT 11 -CHECK-LABEL: DEF NO SPC +CHECK-LABEL: DEF DEFAULT FMT CHECK-NEXT: [[#VAR1:]] -; Numeric variable definition with different spacing. -DEF SPC +; Numeric variable definition with default matching format with different +; spacing. +DEF DEFAULT FMT SPC 11 11 11 -CHECK-LABEL: DEF SPC +CHECK-LABEL: DEF DEFAULT FMT SPC CHECK-NEXT: [[# VAR1a:]] CHECK-NEXT: [[# VAR1b :]] CHECK-NEXT: [[# VAR1c : ]] -; Numeric expressions using variables defined on other lines without spaces. -USE NO SPC +; Numeric variable definition with explicit matching format. +DEF FMT +c +D +CHECK-LABEL: DEF FMT +CHECK-NEXT: [[#%x,VAR2:]] +CHECK-NEXT: [[#%X,VAR3:]] + +; Numeric variable definition with explicit matching format with different +; spacing. +DEF FMT SPC +c +c +c +c +c +CHECK-LABEL: DEF FMT SPC +CHECK-NEXT: [[#%x, VAR2a:]] +CHECK-NEXT: [[# %x, VAR2b:]] +CHECK-NEXT: [[# %x , VAR2c:]] +CHECK-NEXT: [[# %x , VAR2d :]] +CHECK-NEXT: [[# %x , VAR2e : ]] + +; Numeric expressions in explicit matching format and default matching rule using +; variables defined on other lines. +USE DEF FMT IMPL MATCH 11 12 10 +c +d +b +1a +D +E +C +1B 11 11 11 -CHECK-LABEL: USE -CHECK-NEXT: [[#VAR1]] -CHECK-NEXT: [[#VAR1+1]] -CHECK-NEXT: [[#VAR1-1]] -CHECK-NEXT: [[#VAR1a]] -CHECK-NEXT: [[#VAR1b]] -CHECK-NEXT: [[#VAR1c]] +c +c +c +c +c +CHECK-LABEL: USE DEF FMT IMPL MATCH +CHECK-NEXT: [[#%u,VAR1]] +CHECK-NEXT: [[#%u,VAR1+1]] +CHECK-NEXT: [[#%u,VAR1-1]] +CHECK-NEXT: [[#%x,VAR2]] +CHECK-NEXT: [[#%x,VAR2+1]] +CHECK-NEXT: [[#%x,VAR2-1]] +CHECK-NEXT: [[#%x,VAR2+14]] +CHECK-NEXT: [[#%X,VAR3]] +CHECK-NEXT: [[#%X,VAR3+1]] +CHECK-NEXT: [[#%X,VAR3-1]] +CHECK-NEXT: [[#%X,VAR3+14]] +CHECK-NEXT: [[#%u,VAR1a]] +CHECK-NEXT: [[#%u,VAR1b]] +CHECK-NEXT: [[#%u,VAR1c]] +CHECK-NEXT: [[#%x,VAR2a]] +CHECK-NEXT: [[#%x,VAR2b]] +CHECK-NEXT: [[#%x,VAR2c]] +CHECK-NEXT: [[#%x,VAR2d]] +CHECK-NEXT: [[#%x,VAR2e]] -; Numeric expressions using variables defined on other lines with different -; spacing. -USE SPC +; Numeric expressions in explicit matching format and default matching rule using +; variables defined on other lines with different spacing. +USE EXPL FMT IMPL MATCH SPC +11 11 11 12 12 12 12 +12 +12 10 10 10 10 -CHECK-LABEL: USE SPC -CHECK-NEXT: [[# VAR1]] -CHECK-NEXT: [[# VAR1 ]] -CHECK-NEXT: [[# VAR1+1]] -CHECK-NEXT: [[# VAR1 +1]] -CHECK-NEXT: [[# VAR1 + 1]] -CHECK-NEXT: [[# VAR1 + 1 ]] -CHECK-NEXT: [[# VAR1-1]] -CHECK-NEXT: [[# VAR1 -1]] -CHECK-NEXT: [[# VAR1 - 1]] -CHECK-NEXT: [[# VAR1 - 1 ]] +10 +10 +CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC +CHECK-NEXT: [[#%u, VAR1]] +CHECK-NEXT: [[# %u, VAR1]] +CHECK-NEXT: [[# %u, VAR1 ]] +CHECK-NEXT: [[#%u, VAR1+1]] +CHECK-NEXT: [[# %u, VAR1+1]] +CHECK-NEXT: [[# %u , VAR1+1]] +CHECK-NEXT: [[# %u , VAR1 +1]] +CHECK-NEXT: [[# %u , VAR1 + 1]] +CHECK-NEXT: [[# %u , VAR1 + 1 ]] +CHECK-NEXT: [[#%u, VAR1-1]] +CHECK-NEXT: [[# %u, VAR1-1]] +CHECK-NEXT: [[# %u , VAR1-1]] +CHECK-NEXT: [[# %u , VAR1 -1]] +CHECK-NEXT: [[# %u , VAR1 - 1]] +CHECK-NEXT: [[# %u , VAR1 - 1 ]] + +; Numeric expressions in implicit matching format and default matching rule using +; variables defined on other lines +USE IMPL FMT IMPL MATCH +11 +12 +10 +c +d +b +1a +D +E +C +1B +CHECK-LABEL: USE IMPL FMT IMPL MATCH +CHECK-NEXT: [[#VAR1]] +CHECK-NEXT: [[#VAR1+1]] +CHECK-NEXT: [[#VAR1-1]] +CHECK-NEXT: [[#VAR2]] +CHECK-NEXT: [[#VAR2+1]] +CHECK-NEXT: [[#VAR2-1]] +CHECK-NEXT: [[#VAR2+14]] +CHECK-NEXT: [[#VAR3]] +CHECK-NEXT: [[#VAR3+1]] +CHECK-NEXT: [[#VAR3-1]] +CHECK-NEXT: [[#VAR3+14]] + +; Explicit format override implicit format conflicts +VAR USE IMPL OVERRIDE FMT CONFLICT +23 +CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT +CHECK-NEXT: [[# %u, VAR1 + VAR2]] ; Numeric expressions using variables defined on the command-line and an ; immediate interpreted as an unsigned value. ; Note: 9223372036854775819 = 0x8000000000000000 + 11 -; 9223372036854775808 = 0x8000000000000000 -USE UNSIGNED IMM +USE IMPL FMT IMPL MATCH UNSIGNED IMM 9223372036854775819 -CHECK-LABEL: USE UNSIGNED IMM -CHECK-NEXT: [[#VAR1+9223372036854775808]] +CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM +CHECK-NEXT: [[#VAR1+0x8000000000000000]] + +; Numeric expressions with conversion matching format and implicit matching rule +; using variables defined on other lines +USE CONV FMT IMPL MATCH +b +B +12 +13 +CHECK-LABEL: USE CONV FMT IMPL MATCH +CHECK-NEXT: [[# %x, VAR1]] +CHECK-NEXT: [[# %X, VAR1]] +CHECK-NEXT: [[# %u, VAR2]] +CHECK-NEXT: [[# %u, VAR3]] + +; Numeric variable definition with unsupported matching format +RUN: not FileCheck -check-prefixes ERR,INVALID-FMT-SPEC1 -input-file %s %s 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-FMT-SPEC-MSG1 %s +RUN: not FileCheck -check-prefixes ERR,INVALID-FMT-SPEC2 -input-file %s %s 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-FMT-SPEC-MSG2 %s + +DEF INVALID FMT +INVVAR1=a +INVVAR2=11 +ERR-LABEL: DEF INVALID FMT +INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]] +INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]] +INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression +INVALID-FMT-SPEC-MSG1-NEXT: {{I}}NVALID-FMT-SPEC1-NEXT: INVVAR1={{\[\[#%c,INVVAR1:\]\]}} +INVALID-FMT-SPEC-MSG1-NEXT: {{^ \^$}} +INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression +INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}} +INVALID-FMT-SPEC-MSG2-NEXT: {{^ \^$}} ; Numeric expression using a variable defined from a numeric expression. DEF EXPR GOOD MATCH 42 41 43 -; CHECK-LABEL: DEF EXPR GOOD MATCH -; CHECK-NEXT: [[# VAR42:VAR1+31]] -; CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]] +CHECK-LABEL: DEF EXPR GOOD MATCH +CHECK-NEXT: [[# VAR42:VAR1+31]] +CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]] ; Empty numeric expression. EMPTY NUM EXPR foo 104 bar -; CHECK-LABEL: EMPTY NUM EXPR -; CHECK-NEXT: foo [[#]] bar +CHECK-LABEL: EMPTY NUM EXPR +CHECK-NEXT: foo [[#]] bar ; Numeric expression using undefined variable. RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ @@ -165,3 +290,15 @@ DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH DEF-EXPR-FAIL-NEXT: [[# VAR20:]] DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]] + +; Conflicting implicit format +RUN: not FileCheck -check-prefixes CHECK,FMT-CONFLICT -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix FMT-CONFLICT-MSG %s + +VAR USE IMPL FMT CONFLICT +23 +FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT +FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]] +FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one +FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} +FMT-CONFLICT-MSG-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/string-defines-diagnostics.txt b/llvm/test/FileCheck/string-defines-diagnostics.txt --- a/llvm/test/FileCheck/string-defines-diagnostics.txt +++ b/llvm/test/FileCheck/string-defines-diagnostics.txt @@ -25,12 +25,12 @@ ERRCLIVAR2: Missing variable name in command-line definition '-D=' ; Invalid variable name: starts with a digit. -RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT +RUN: not FileCheck -D10VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix ERRCLINAME -ERRCLIFMT: Global defines:1:19: error: invalid variable name -ERRCLIFMT-NEXT: Global define #1: 10VALUE=10 -ERRCLIFMT-NEXT: {{^ \^$}} +ERRCLINAME: Global defines:1:19: error: invalid variable name +ERRCLINAME-NEXT: Global define #1: 10VALUE=10 +ERRCLINAME-NEXT: {{^ \^$}} ; Invalid definition of pseudo variable. RUN: not FileCheck -D@VALUE=10 --input-file %s %s 2>&1 \ 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 @@ -44,7 +44,9 @@ // Undefined variable: isMatchTimeKnown returns false, eval and clearValue // fail, error returned by eval holds the name of the undefined variable and // setValue works. - auto FooVar = std::make_shared(1, "FOO", nullptr); + auto NumVarExpr = FileCheckExpression(nullptr, FormatUnsigned); + auto FooVar = + std::make_shared(1, "FOO", &NumVarExpr); EXPECT_EQ("FOO", FooVar->getName()); EXPECT_FALSE(FooVar->isMatchTimeKnown()); Expected Value = FooVar->eval(); @@ -68,8 +70,9 @@ // true, eval returns value of expression, and setValue succeeds. auto One = std::make_shared(1); auto Binop = std::make_shared(doAdd, FooVar, One); + auto FoobarExpr = FileCheckExpression(Binop, FormatUnsigned); FileCheckNumericVariable FoobarVar = - FileCheckNumericVariable(2, "FOOBAR", Binop); + FileCheckNumericVariable(2, "FOOBAR", &FoobarExpr); EXPECT_TRUE(FoobarVar.isMatchTimeKnown()); Value = FoobarVar.eval(); EXPECT_TRUE(static_cast(Value)); @@ -93,9 +96,13 @@ } TEST_F(FileCheckTest, Binop) { - auto FooVar = std::make_shared(1, "FOO", nullptr); + FileCheckExpression DefExpression = + FileCheckExpression(nullptr, FormatUnsigned); + auto FooVar = + std::make_shared(1, "FOO", &DefExpression); FooVar->setValue(42); - auto BarVar = std::make_shared(2, "BAR", nullptr); + auto BarVar = + std::make_shared(2, "BAR", &DefExpression); BarVar->setValue(18); FileCheckASTBinop Binop = FileCheckASTBinop(doAdd, FooVar, BarVar); @@ -390,15 +397,20 @@ // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. + auto DefExpression = FileCheckExpression(nullptr, FormatUnsigned); auto LineVar = - std::make_shared(1, "@LINE", nullptr); - auto NVar = std::make_shared(1, "N", nullptr); + std::make_shared(1, "@LINE", &DefExpression); + auto NVar = + std::make_shared(1, "N", &DefExpression); LineVar->setValue(42); NVar->setValue(10); + FileCheckExpression ExpressionLine = + FileCheckExpression(LineVar, FormatUnsigned); + FileCheckExpression ExpressionN = FileCheckExpression(NVar, FormatUnsigned); FileCheckNumericSubstitution SubstitutionLine = - FileCheckNumericSubstitution(&Context, "@LINE", LineVar, 12); + FileCheckNumericSubstitution(&Context, "@LINE", &ExpressionLine, 12); FileCheckNumericSubstitution SubstitutionN = - FileCheckNumericSubstitution(&Context, "N", NVar, 30); + FileCheckNumericSubstitution(&Context, "N", &ExpressionN, 30); SubstValue = SubstitutionLine.getResult(); EXPECT_TRUE(static_cast(SubstValue)); EXPECT_EQ("42", *SubstValue); @@ -485,16 +497,15 @@ Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); Optional DefinedNumericVariable; - Expected> ExpressionAST = - P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, - /*LegacyLineExpr=*/false, - /*LineNumber=*/1, &Cxt, SM); + Expected Expression = P.parseNumericSubstitutionBlock( + LocalNumVarRef, DefinedNumericVariable, /*LegacyLineExpr=*/false, + /*LineNumber=*/1, &Cxt, SM); EXPECT_TRUE(static_cast(LocalVar)); EXPECT_EQ(*LocalVar, "FOO"); Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); - EXPECT_TRUE(static_cast(ExpressionAST)); - Expected ExpressionVal = (*ExpressionAST)->eval(); + EXPECT_TRUE(static_cast(Expression)); + Expected ExpressionVal = (*Expression)->getAST()->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 18U); EXPECT_TRUE(static_cast(EmptyVar)); @@ -509,12 +520,12 @@ // 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((*ExpressionAST)->eval().takeError())); + EXPECT_TRUE(errorToBool((*Expression)->getAST()->eval().takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( LocalNumVarRef, DefinedNumericVariable, /*LegacyLineExpr=*/false, /*LineNumber=*/2, &Cxt, SM); - EXPECT_TRUE(errorToBool(ExpressionAST.takeError())); + EXPECT_TRUE(errorToBool(Expression.takeError())); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_TRUE(errorToBool(EmptyVar.takeError())); @@ -528,11 +539,11 @@ EXPECT_TRUE(static_cast(GlobalVar)); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*LegacyLineExpr=*/false, /*LineNumber=*/3, &Cxt, SM); - EXPECT_TRUE(static_cast(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + EXPECT_TRUE(static_cast(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); @@ -540,11 +551,11 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*LegacyLineExpr=*/false, /*LineNumber=*/4, &Cxt, SM); - EXPECT_TRUE(static_cast(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + EXPECT_TRUE(static_cast(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); EXPECT_TRUE(static_cast(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); }