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 @@ -112,10 +112,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. @@ -588,27 +589,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 ``0xF0F0``. -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 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 defaults to ``%u`` if no numeric variable + is used. In case of conflict between matching formats of several numeric + variables the format specifier is mandatory. -* a numeric operand, or -* an expression followed by an operator and a numeric operand. +* ```` is an expression. An expression is in turn recursively defined + as: -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, 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. For example: @@ -616,6 +634,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: @@ -623,6 +642,7 @@ load r5, [r0] load r6, [r1] + Loading from 0xa0463440 to 0xa0463447 but would not match the text: @@ -630,8 +650,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``. The syntax also supports an empty expression, equivalent to writing {{[0-9]+}}, for cases where the input must contain a numeric value but the value itself @@ -646,8 +668,21 @@ A numeric variable can also be defined to the result of a numeric expression, in which case the numeric expression is checked and if verified the variable is assigned to the value. The unified syntax for both defining numeric variables -and checking a numeric expression is thus ``[[#: ]]`` with each -element as described previously. +and checking a numeric expression is thus ``[[#%,: ]]`` +with each element as described previously. One can use this syntax to make a +testcase more self-describing by using variables instead of values: + +.. code-block:: gas + + ; CHECK: mov r[[#REG_OFFSET:]], 0x[[#%X,FIELD_OFFSET:12]] + ; CHECK-NEXT: load r[[#]], [r[[#REG_BASE:]], r[[#REG_OFFSET]]] + +which would match: + +.. code-block:: gas + + mov r4, 0xC + load r6, [r5, r4] The ``--enable-var-scope`` option has the same effect on numeric variables as on string variables. 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 @@ -25,6 +25,47 @@ using namespace llvm; +Expected ExpressionFormat::getWildcardRegex() const { + switch (Value) { + case Kind::Unsigned: + return StringRef("[0-9]+"); + case Kind::HexUpper: + return StringRef("[0-9A-F]+"); + case Kind::HexLower: + return StringRef("[0-9a-f]+"); + default: + return createStringError(std::errc::invalid_argument, + "trying to match value with invalid format"); + } +} + +Expected +ExpressionFormat::getMatchingString(uint64_t IntegerValue) const { + switch (Value) { + case Kind::Unsigned: + return utostr(IntegerValue); + case Kind::HexUpper: + return utohexstr(IntegerValue, /*LowerCase=*/false); + case Kind::HexLower: + return utohexstr(IntegerValue, /*LowerCase=*/true); + default: + return createStringError(std::errc::invalid_argument, + "trying to match value with invalid format"); + } +} + +Expected +ExpressionFormat::valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const { + bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; + uint64_t IntegerValue; + if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue)) + return ErrorDiagnostic::get(SM, StrVal, + "Unable to represent numeric value"); + + return IntegerValue; +} + Expected NumericVariableUse::eval() const { Optional Value = Variable->getValue(); if (Value) @@ -51,11 +92,28 @@ return EvalBinop(*LeftOp, *RightOp); } +ExpressionFormat BinaryOperation::getImplicitFormat() const { + ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat(); + ExpressionFormat RightFormat = RightOperand->getImplicitFormat(); + + ExpressionFormat Format = + LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat; + if (LeftFormat != ExpressionFormat::Kind::NoFormat && + RightFormat != ExpressionFormat::Kind::NoFormat && + LeftFormat != RightFormat) + Format = ExpressionFormat(ExpressionFormat::Kind::Conflict); + + return Format; +} + Expected NumericSubstitution::getResult() const { - Expected EvaluatedValue = ExpressionASTPointer->eval(); + assert(ExpressionPointer->getAST() != nullptr && + "Substituting empty expression"); + Expected EvaluatedValue = ExpressionPointer->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); - return utostr(*EvaluatedValue); + ExpressionFormat Format = ExpressionPointer->getFormat(); + return Format.getMatchingString(*EvaluatedValue); } Expected StringSubstitution::getResult() const { @@ -113,7 +171,8 @@ Expected Pattern::parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, - Optional LineNumber, const SourceMgr &SM) { + Optional LineNumber, ExpressionFormat ImplicitFormat, + const SourceMgr &SM) { Expected ParseVarResult = parseVariable(Expr, SM); if (!ParseVarResult) return ParseVarResult.takeError(); @@ -137,10 +196,14 @@ NumericVariable *DefinedNumericVariable; auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); - if (VarTableIter != Context->GlobalNumericVariableTable.end()) + if (VarTableIter != Context->GlobalNumericVariableTable.end()) { DefinedNumericVariable = VarTableIter->second; - else - DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber); + if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat) + return ErrorDiagnostic::get( + SM, Expr, "format different from previous variable definition"); + } else + DefinedNumericVariable = + Context->makeNumericVariable(Name, ImplicitFormat, LineNumber); return DefinedNumericVariable; } @@ -165,7 +228,8 @@ if (VarTableIter != Context->GlobalNumericVariableTable.end()) NumericVariable = VarTableIter->second; else { - NumericVariable = Context->makeNumericVariable(Name); + NumericVariable = Context->makeNumericVariable( + Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); Context->GlobalNumericVariableTable[Name] = NumericVariable; } @@ -198,7 +262,8 @@ // Otherwise, parse it as a literal. uint64_t LiteralValue; - if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) + if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, + LiteralValue)) return std::make_unique(LiteralValue); return ErrorDiagnostic::get(SM, Expr, @@ -244,7 +309,7 @@ return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); // The second operand in a legacy @LINE expression is always a literal. AllowedOperand AO = - IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; + IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; Expected> RightOpResult = parseNumericOperand(Expr, AO, LineNumber, Context, SM); if (!RightOpResult) @@ -255,13 +320,47 @@ std::move(*RightOpResult)); } -Expected> Pattern::parseNumericSubstitutionBlock( +Expected> Pattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, bool IsLegacyLineExpr, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { std::unique_ptr ExpressionASTPointer = nullptr; StringRef DefExpr = StringRef(); DefinedNumericVariable = None; + ExpressionFormat ExplicitFormat = ExpressionFormat(); + + // Parse format specifier. + size_t FormatSpecEnd = Expr.find(','); + if (FormatSpecEnd != StringRef::npos) { + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front("%")) + return ErrorDiagnostic::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 = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + break; + case 'x': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); + break; + case 'X': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper); + break; + default: + return ErrorDiagnostic::get(SM, fmtloc, + "invalid format specifier in expression"); + } + + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front(",")) + return ErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + } + // Save variable definition expression if any. size_t DefEnd = Expr.find(':'); if (DefEnd != StringRef::npos) { @@ -271,6 +370,7 @@ // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); + StringRef UseExpr = Expr; if (!Expr.empty()) { // The first operand in a legacy @LINE expression is always the @LINE // pseudo variable. @@ -288,22 +388,44 @@ "unexpected characters at end of expression '" + Expr + "'"); } if (!ParseResult) - return ParseResult; + return ParseResult.takeError(); ExpressionASTPointer = std::move(*ParseResult); } + // Select format of the expression, i.e. (i) its explicit format, if any, + // otherwise (ii) its implicit format, if any, otherwise (iii) the default + // format (unsigned). Error out in case of conflicting implicit format + // without explicit format. + ExpressionFormat Format, + ImplicitFormat = ExpressionASTPointer + ? ExpressionASTPointer->getImplicitFormat() + : ExpressionFormat(ExpressionFormat::Kind::NoFormat); + if (bool(ExplicitFormat)) + Format = ExplicitFormat; + else if (ImplicitFormat == ExpressionFormat::Kind::Conflict) + return ErrorDiagnostic::get( + SM, UseExpr, + "variables with conflicting format specifier: need an explicit one"); + else if (bool(ImplicitFormat)) + Format = ImplicitFormat; + else + Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + + std::unique_ptr ExpressionPointer = + std::make_unique(std::move(ExpressionASTPointer), Format); + // Parse the numeric variable definition. if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); - Expected ParseResult = - parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM); + Expected ParseResult = parseNumericVariableDefinition( + DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM); if (!ParseResult) return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; } - return std::move(ExpressionASTPointer); + return std::move(ExpressionPointer); } bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -476,10 +598,10 @@ } // Parse numeric substitution block. - std::unique_ptr ExpressionASTPointer; + std::unique_ptr ExpressionPointer; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected> ParseResult = + Expected> ParseResult = parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, IsLegacyLineExpr, LineNumber, Context, SM); @@ -487,16 +609,18 @@ logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - ExpressionASTPointer = std::move(*ParseResult); - SubstNeeded = ExpressionASTPointer != nullptr; + ExpressionPointer = std::move(*ParseResult); + SubstNeeded = ExpressionPointer->getAST() != nullptr; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); } if (SubstNeeded) SubstStr = MatchStr; - else - MatchRegexp = "[0-9]+"; + else { + ExpressionFormat Format = ExpressionPointer->getFormat(); + MatchRegexp = cantFail(Format.getWildcardRegex()); + } } // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. @@ -554,8 +678,7 @@ Substitution *Substitution = IsNumBlock ? Context->makeNumericSubstitution( - SubstStr, std::move(ExpressionASTPointer), - SubstInsertIdx) + SubstStr, std::move(ExpressionPointer), SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -676,11 +799,11 @@ NumericVariableMatch.DefinedNumericVariable; StringRef MatchedValue = MatchInfo[CaptureParenGroup]; - uint64_t Val; - if (MatchedValue.getAsInteger(10, Val)) - return ErrorDiagnostic::get(SM, MatchedValue, - "Unable to represent numeric value"); - DefinedNumericVariable->setValue(Val); + ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); + Expected Value = Format.valueFromStringRepr(MatchedValue, SM); + if (!Value) + return Value.takeError(); + DefinedNumericVariable->setValue(*Value); } // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after @@ -837,10 +960,10 @@ } Substitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, - std::unique_ptr ExpressionASTPointer, size_t InsertIdx) { + StringRef ExpressionStr, std::unique_ptr Expression, + size_t InsertIdx) { Substitutions.push_back(std::make_unique( - this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx)); + this, ExpressionStr, std::move(Expression), InsertIdx)); return Substitutions.back().get(); } @@ -1104,7 +1227,8 @@ void FileCheckPatternContext::createLineVariable() { assert(!LineVariable && "@LINE pseudo numeric variable already created"); StringRef LineName = "@LINE"; - LineVariable = makeNumericVariable(LineName); + LineVariable = makeNumericVariable( + LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); GlobalNumericVariableTable[LineName] = LineVariable; } @@ -1815,20 +1939,19 @@ // to create the necessary class instance. StringRef CmdlineDefExpr = CmdlineDef.substr(1); Optional DefinedNumericVariable; - Expected> ExpressionASTResult = + Expected> ExpressionResult = Pattern::parseNumericSubstitutionBlock( CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM); - if (!ExpressionASTResult) { - Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError()); + if (!ExpressionResult) { + Errs = joinErrors(std::move(Errs), ExpressionResult.takeError()); continue; } - std::unique_ptr ExpressionASTPointer = - std::move(*ExpressionASTResult); + std::unique_ptr Expression = std::move(*ExpressionResult); // Now evaluate the expression whose value this variable should be set // to, since the expression of a command-line variable definition should // only use variables defined earlier on the command-line. If not, this // is an error and we report it. - Expected Value = ExpressionASTPointer->eval(); + Expected Value = Expression->getAST()->eval(); if (!Value) { Errs = joinErrors(std::move(Errs), Value.takeError()); continue; diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -30,6 +30,70 @@ // Numeric substitution handling code. //===----------------------------------------------------------------------===// +/// Type representing the format an expression value should be textualized into +/// for matching. Used to represent both explicit format specifiers as well as +/// implicit format from using numeric variables. +struct ExpressionFormat { + enum class Kind { + /// Denote absence of format. Used for implicit format of literals and + /// empty expressions. + NoFormat, + /// Used when there are several conflicting implicit formats in an + /// expression. + Conflict, + /// Value is an unsigned integer and should be printed as a decimal number. + Unsigned, + /// Value should be printed as an uppercase hex number. + HexUpper, + /// Value should be printed as a lowercase hex number. + HexLower + }; + +private: + Kind Value; + +public: + /// Evaluates a format to true if it can be used in a match. + explicit operator bool() const { + return Value != Kind::NoFormat && Value != Kind::Conflict; + } + + /// Define format equality: formats are equal if neither is NoFormat and + /// their kinds are the same. + bool operator==(const ExpressionFormat &Other) const { + return Value != Kind::NoFormat && Value == Other.Value; + } + + bool operator!=(const ExpressionFormat &other) const { + return !(*this == other); + } + + bool operator==(const Kind OtherValue) const { return Value == OtherValue; } + + bool operator!=(const Kind OtherValue) const { + return !(*this == OtherValue); + } + + ExpressionFormat() : Value(Kind::NoFormat){}; + explicit ExpressionFormat(Kind Value) : Value(Value){}; + + /// \returns a wildcard regular expression StringRef that matches any value + /// in the format represented by this instance, or an error if the format is + /// NoFormat or Conflict. + Expected getWildcardRegex() const; + + /// \returns the string representation of \p Value in the format represented + /// by this instance, or an error if the format is NoFormat or Conflict. + Expected 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; +}; + /// Base class representing the AST of a given expression. class ExpressionAST { public: @@ -38,6 +102,13 @@ /// Evaluates and \returns the value of the expression represented by this /// AST or an error if evaluation fails. virtual Expected eval() const = 0; + + /// \returns either the implicit format of this AST, FormatConflict if + /// implicit formats of the AST's components conflict, or NoFormat if the AST + /// has no implicit format (e.g. AST is made up of a single literal). + virtual ExpressionFormat getImplicitFormat() const { + return ExpressionFormat(); + } }; /// Class representing an unsigned literal in the AST of an expression. @@ -78,12 +149,38 @@ } }; +/// Class representing an expression and its matching format. +class Expression { +private: + /// Pointer to AST of the expression. + std::unique_ptr AST; + + /// Format to use (e.g. hex upper case letters) when matching the value. + ExpressionFormat Format; + +public: + /// Generic constructor for an expression represented by the given \p AST and + /// whose matching format is \p Format. + Expression(std::unique_ptr AST, ExpressionFormat Format) + : AST(std::move(AST)), Format(Format) {} + + /// \returns pointer to AST of the expression. Pointer is guaranteed to be + /// valid as long as this object is. + ExpressionAST *getAST() const { return AST.get(); } + + ExpressionFormat getFormat() const { return Format; } +}; + /// Class representing a numeric variable and its associated current value. class NumericVariable { private: /// Name of the numeric variable. StringRef Name; + /// Format to use for expressions using this variable without an explicit + /// format. + ExpressionFormat ImplicitFormat; + /// Value of numeric variable, if defined, or None otherwise. Optional Value; @@ -93,15 +190,20 @@ Optional DefLineNumber; public: - /// Constructor for a variable \p Name defined at line \p DefLineNumber or - /// defined before input is parsed if \p DefLineNumber is None. - explicit NumericVariable(StringRef Name, + /// Constructor for a variable \p Name with implicit format \p ImplicitFormat + /// defined at line \p DefLineNumber or defined before input is parsed if + /// \p DefLineNumber is None. + explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat, Optional DefLineNumber = None) - : Name(Name), DefLineNumber(DefLineNumber) {} + : Name(Name), ImplicitFormat(ImplicitFormat), + DefLineNumber(DefLineNumber) {} /// \returns name of this numeric variable. StringRef getName() const { return Name; } + /// \returns implicit format of this numeric variable. + ExpressionFormat getImplicitFormat() const { return ImplicitFormat; } + /// \returns this variable's value. Optional getValue() const { return Value; } @@ -133,6 +235,11 @@ /// \returns the value of the variable referenced by this instance. Expected eval() const override; + + /// \returns implicit format of this numeric variable. + ExpressionFormat getImplicitFormat() const override { + return Variable->getImplicitFormat(); + } }; /// Type of functions evaluating a given binary operation. @@ -163,6 +270,11 @@ /// \returns the expression value or an error if an undefined numeric /// variable is used in one of the operands. Expected eval() const override; + + /// \returns the implicit format of this AST, if any, a format conflict if + /// the implicit formats of the AST's components conflict, or no format if + /// the AST has no implicit format (e.g. AST is made of a single literal). + ExpressionFormat getImplicitFormat() const override; }; class FileCheckPatternContext; @@ -218,14 +330,14 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - std::unique_ptr ExpressionASTPointer; + std::unique_ptr ExpressionPointer; public: - NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, - std::unique_ptr ExprAST, size_t InsertIdx) - : Substitution(Context, Expr, InsertIdx) { - ExpressionASTPointer = std::move(ExprAST); - } + NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr, + std::unique_ptr ExpressionPointer, + size_t InsertIdx) + : Substitution(Context, ExpressionStr, InsertIdx), + ExpressionPointer(std::move(ExpressionPointer)) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -270,6 +382,10 @@ /// automatically free them once they are guaranteed to no longer be used. std::vector> NumericVariables; + /// 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; @@ -307,10 +423,9 @@ /// Makes a new numeric substitution and registers it for destruction when /// the context is destroyed. - Substitution * - makeNumericSubstitution(StringRef ExpressionStr, - std::unique_ptr ExpressionAST, - size_t InsertIdx); + Substitution *makeNumericSubstitution(StringRef ExpressionStr, + std::unique_ptr Expression, + size_t InsertIdx); }; /// Class to represent an error holding a diagnostic with location information @@ -388,12 +503,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 NumericVariableMatch { - /// 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. NumericVariable *DefinedNumericVariable; /// Number of the parenthesis group in RegExStr that captures the value of @@ -457,12 +572,12 @@ /// \p IsLegacyLineExpr 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( + /// 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 IsLegacyLineExpr, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); @@ -526,7 +641,8 @@ /// should defining such a variable be invalid. static Expected parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, - Optional LineNumber, const SourceMgr &SM); + Optional LineNumber, ExpressionFormat ImplicitFormat, + const SourceMgr &SM); /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use /// at line \p LineNumber, or before input is parsed if \p LineNumber is /// None. Parameter \p Context points to the class instance holding the live @@ -536,7 +652,7 @@ static Expected> parseNumericVariableUse( StringRef Name, bool IsPseudo, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); - enum class AllowedOperand { LineVar, Literal, Any }; + enum class AllowedOperand { LineVar, LegacyLiteral, Any }; /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or /// before input is parsed if \p LineNumber is None. Accepts both literal /// values and numeric variables, depending on the value of \p AO. Parameter 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 @@ -62,12 +62,19 @@ 62 BAD12: [[#@LINE-1]] NOT HERE 63 ERR12: note: with "@LINE-1" equal to "61" 64 -65 CHECK: [[#@LINE]] CHECK -66 CHECK: [[# @LINE]] CHECK -67 CHECK: [[# @LINE ]] CHECK +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \ +; RUN: | FileCheck --check-prefix ERR13 %s 68 -69 CHECK: [[#@LINE-1]] -70 CHECK: [[# @LINE-1]] CHECK -71 CHECK: [[# @LINE -1]] CHECK -72 CHECK: [[# @LINE - 1]] CHECK -73 CHECK: [[# @LINE - 1 ]] CHECK +69 BAD13: [[@LINE-0xA]] +70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA' +71 +72 CHECK: [[#@LINE]] CHECK +73 CHECK: [[# @LINE]] CHECK +74 CHECK: [[# @LINE ]] CHECK +75 +76 CHECK: [[#@LINE-1]] +77 CHECK: [[# @LINE-1]] CHECK +78 CHECK: [[# @LINE -1]] CHECK +79 CHECK: [[# @LINE - 1]] CHECK +80 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 @@ -3,11 +3,11 @@ ; Invalid variable name: starts with a digit. RUN: %ProtectFileCheckOutput \ RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT +RUN: | FileCheck %s --strict-whitespace --match-full-lines --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: %ProtectFileCheckOutput \ @@ -26,3 +26,11 @@ NUMERRCLITRAIL:Global defines:1:51: error: unexpected characters after numeric variable name 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 --match-full-lines --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,31 +1,33 @@ ; 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-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s +; Tests with default format specifier. +RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' --check-prefixes CHECKNUM1,CHECKNUM2 --input-file %s %s RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1 +RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix CHECKNUM1 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG1 RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2 +RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix CHECKNUM2 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG2 RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1 +RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG1 RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2 -RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s +RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG2 + +RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s Numeric value #1 = 8 Numeric value #2 = 12 CHECKNUM1: Numeric value #1 = [[#NUMVAL1]] CHECKNUM2: Numeric value #2 = [[#NUMVAL2]] -NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]] -NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]] +NUMNOT1-NOT: Numeric value #1 = [[#NUMVAL1]] +NUMNOT1-NOT: Numeric value #2 = [[#NUMVAL2]] NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input NUMERRMSG1: defines.txt:1:1: note: scanning from here @@ -37,10 +39,61 @@ NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8" NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here -NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input +NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8" -NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input +NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12" + +; Tests with explicit format specifiers. +RUN: FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix CHECKNUM3 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG3 + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix CHECKNUM4 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG4 + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG3 + +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG4 + +RUN: FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s + +; Test with explicit and default format specifiers. +RUN: FileCheck -D#NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s +; Test with explicit and implicit format specifiers. +RUN: FileCheck -D#%X,NUMVAL3=8 -D#NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s + +Numeric value #3 = 8 +Numeric value #4 = C +CHECKNUM3: Numeric value #3 = [[#NUMVAL3]] +CHECKNUM4: Numeric value #4 = [[#NUMVAL4]] +NUMNOT2-NOT: Numeric value #3 = [[#NUMVAL3]] +NUMNOT2-NOT: Numeric value #4 = [[#NUMVAL4]] + +NUMERRMSG3: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM3: expected string not found in input +NUMERRMSG3: defines.txt:1:1: note: scanning from here +NUMERRMSG3: defines.txt:1:1: note: with "NUMVAL3" equal to "7" +NUMERRMSG3: defines.txt:[[#@LINE-10]]:1: note: possible intended match here + +NUMERRMSG4: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM4: expected string not found in input +NUMERRMSG4: defines.txt:1:1: note: scanning from here +NUMERRMSG4: defines.txt:1:1: note: with "NUMVAL4" equal to "8" +NUMERRMSG4: defines.txt:[[#@LINE-14]]:1: note: possible intended match here + +NOT-NUMERRMSG3: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input +NOT-NUMERRMSG3: defines.txt:[[#@LINE-18]]:1: note: found here +NOT-NUMERRMSG3: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL3" equal to "8" + +NOT-NUMERRMSG4: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input +NOT-NUMERRMSG4: defines.txt:[[#@LINE-21]]:1: note: found here +NOT-NUMERRMSG4: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL4" 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,18 +2,19 @@ ; 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 without spaces. +DEF DEFAULT FMT NO SPC 10 -CHECK-LABEL: DEF NO SPC +CHECK-LABEL: DEF DEFAULT FMT NO SPC 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 : ]] @@ -24,63 +25,187 @@ CHECK-LABEL: REDEF CHECK-NEXT: [[#VAR1:]] -; 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 +CHECK-LABEL: DEF FMT SPC +CHECK-NEXT: [[# %x , VAR2a : ]] + +; 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 --strict-whitespace %s +RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC2 --input-file %s %s 2>&1 \ +RUN: | FileCheck --check-prefix INVALID-FMT-SPEC-MSG2 --strict-whitespace %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 expressions in explicit matching format and default matching rule using +; variables defined on other lines without spaces. +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]] -; 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 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 ]] +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]] ; Numeric expressions using variables defined on other lines 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 matching format overriding the implicit format of +; 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]] + +; 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: {{^ \^$}} + +; Explicitly specified format can override conflicting implicit formats. +VAR USE IMPL OVERRIDE FMT CONFLICT +23 +CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT +CHECK-NEXT: [[# %u, VAR1 + VAR2]] ; Numeric expressions using more than one variable defined on other lines. USE MULTI VAR 31 42 CHECK-LABEL: USE MULTI VAR -CHECK-NEXT: [[#VAR2:]] -CHECK-NEXT: [[#VAR1+VAR2]] +CHECK-NEXT: [[#VAR4:]] +CHECK-NEXT: [[#VAR1+VAR4]] ; Numeric expression using a variable defined from a numeric expression. DEF EXPR GOOD MATCH @@ -93,8 +218,8 @@ ; 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 variables. RUN: %ProtectFileCheckOutput \ @@ -232,3 +357,17 @@ SAME-LINE-USE-MSG2: numeric-expression.txt:[[#@LINE-1]]:42: error: numeric variable 'VAR2' defined earlier in the same CHECK directive SAME-LINE-USE-MSG2-NEXT: {{S}}AME-LINE-USE2-NEXT: {{\[\[#VAR2:VAR1\+1\]\] \[\[#VAR2\+1\]\]}} SAME-LINE-USE-MSG2-NEXT: {{^}} ^{{$}} + +; Invalid change of format in variable redefinition. +RUN: not FileCheck --check-prefix REDEF-NEW-FMT --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix REDEF-NEW-FMT-MSG %s + +VAR REDEF FMT CHANGE +22 +DC +REDEF-NEW-FMT-LABEL: VAR REDEF FMT CHANGE +REDEF-NEW-FMT-NEXT: [[#VAR1:]] +REDEF-NEW-FMT-NEXT: [[#%X,VAR1:]] +REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition +REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,VAR1:\]\]}} +REDEF-NEW-FMT-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 @@ -27,11 +27,11 @@ ; Invalid variable name: starts with a digit. RUN: %ProtectFileCheckOutput \ RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLIFMT +RUN: | FileCheck %s --strict-whitespace --match-full-lines --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: %ProtectFileCheckOutput \ 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 @@ -8,6 +8,7 @@ #include "llvm/Support/FileCheck.h" #include "../lib/Support/FileCheckImpl.h" +#include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" #include @@ -17,12 +18,105 @@ class FileCheckTest : public ::testing::Test {}; +static StringRef bufferize(SourceMgr &SM, StringRef Str) { + std::unique_ptr Buffer = + MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); + StringRef StrBufferRef = Buffer->getBuffer(); + SM.AddNewSourceBuffer(std::move(Buffer), SMLoc()); + return StrBufferRef; +} + +TEST_F(FileCheckTest, Format) { + SourceMgr SM; + + // Check methods output for unsigned decimal format. + ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); + Expected Wildcard = UnsignedFormat.getWildcardRegex(); + EXPECT_TRUE(bool(Wildcard)); + EXPECT_EQ(*Wildcard, "[0-9]+"); + Expected MatchingString = UnsignedFormat.getMatchingString(42); + EXPECT_TRUE(bool(MatchingString)); + EXPECT_EQ(*MatchingString, "42"); + StringRef ThreeTwo = bufferize(SM, "32"); + Expected Val = UnsignedFormat.valueFromStringRepr(ThreeTwo, SM); + ASSERT_TRUE(bool(Val)); + EXPECT_EQ(*Val, 32U); + StringRef ALower = bufferize(SM, "a"); + EXPECT_THAT_ERROR(UnsignedFormat.valueFromStringRepr(ALower, SM).takeError(), + Failed()); + + // Check methods output for lowercase hex format. + ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower); + Wildcard = HexLowerFormat.getWildcardRegex(); + EXPECT_TRUE(bool(Wildcard)); + EXPECT_EQ(*Wildcard, "[0-9a-f]+"); + MatchingString = HexLowerFormat.getMatchingString(11); + EXPECT_TRUE(bool(MatchingString)); + EXPECT_EQ(*MatchingString, "b"); + Val = HexLowerFormat.valueFromStringRepr(ALower, SM); + ASSERT_TRUE(bool(Val)); + EXPECT_EQ(*Val, 10U); + StringRef GLower = bufferize(SM, "g"); + EXPECT_THAT_ERROR(HexLowerFormat.valueFromStringRepr(GLower, SM).takeError(), + Failed()); + + // Check methods output for uppercase hex format. + ExpressionFormat HexUpperFormat(ExpressionFormat::Kind::HexUpper); + Wildcard = HexUpperFormat.getWildcardRegex(); + EXPECT_TRUE(bool(Wildcard)); + EXPECT_EQ(*Wildcard, "[0-9A-F]+"); + MatchingString = HexUpperFormat.getMatchingString(12); + EXPECT_TRUE(bool(MatchingString)); + EXPECT_EQ(*MatchingString, "C"); + StringRef DUpper = bufferize(SM, "D"); + Val = HexUpperFormat.valueFromStringRepr(DUpper, SM); + ASSERT_TRUE(bool(Val)); + EXPECT_EQ(*Val, 13U); + StringRef HUpper = bufferize(SM, "H"); + EXPECT_THAT_ERROR(HexUpperFormat.valueFromStringRepr(HUpper, SM).takeError(), + Failed()); + + // Check methods output for NoFormat and Conflict. + ExpressionFormat NoneFormat; + EXPECT_THAT_ERROR(NoneFormat.getWildcardRegex().takeError(), Failed()); + EXPECT_THAT_ERROR(NoneFormat.getMatchingString(18).takeError(), Failed()); + ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); + EXPECT_THAT_ERROR(ConflictFormat.getWildcardRegex().takeError(), Failed()); + EXPECT_THAT_ERROR(ConflictFormat.getMatchingString(18).takeError(), Failed()); + + // Check bool operator. + EXPECT_FALSE(bool(NoneFormat)); + EXPECT_FALSE(bool(ConflictFormat)); + EXPECT_TRUE(bool(UnsignedFormat)); + + // Check format equality operators. + EXPECT_TRUE(UnsignedFormat == UnsignedFormat); + EXPECT_FALSE(UnsignedFormat != UnsignedFormat); + EXPECT_FALSE(UnsignedFormat == HexLowerFormat); + EXPECT_TRUE(UnsignedFormat != HexLowerFormat); + EXPECT_FALSE(NoneFormat == NoneFormat); + EXPECT_TRUE(NoneFormat != NoneFormat); + EXPECT_TRUE(ConflictFormat == ConflictFormat); + EXPECT_FALSE(ConflictFormat != ConflictFormat); + + // Check format Vs kind equality operators. + EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned); + EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned); + EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower); + EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower); + EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict); + EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict); + EXPECT_TRUE(NoneFormat == ExpressionFormat::Kind::NoFormat); + EXPECT_FALSE(NoneFormat != ExpressionFormat::Kind::NoFormat); +} + TEST_F(FileCheckTest, Literal) { // Eval returns the literal's value. ExpressionLiteral Ten(10); Expected Value = Ten.eval(); ASSERT_TRUE(bool(Value)); EXPECT_EQ(10U, *Value); + EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat); // Max value can be correctly represented. ExpressionLiteral Max(std::numeric_limits::max()); @@ -57,14 +151,27 @@ expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err)); } +TEST_F(FileCheckTest, Expression) { + std::unique_ptr Ten = + std::make_unique(10); + ExpressionLiteral *TenPtr = Ten.get(); + Expression Expr(std::move(Ten), + ExpressionFormat(ExpressionFormat::Kind::HexLower)); + EXPECT_EQ(Expr.getAST(), TenPtr); + EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower); +} + uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: getValue and eval fail, error returned by eval holds // the name of the undefined variable. - NumericVariable FooVar("FOO", 1); + NumericVariable FooVar("FOO", + ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); EXPECT_EQ("FOO", FooVar.getName()); + EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); NumericVariableUse FooVarUse("FOO", &FooVar); + EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(FooVar.getValue()); Expected EvalResult = FooVarUse.eval(); ASSERT_FALSE(EvalResult); @@ -90,20 +197,23 @@ } TEST_F(FileCheckTest, Binop) { - NumericVariable FooVar("FOO", 1); + NumericVariable FooVar("FOO", + ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); FooVar.setValue(42); std::unique_ptr FooVarUse = std::make_unique("FOO", &FooVar); - NumericVariable BarVar("BAR", 2); + NumericVariable BarVar("BAR", + ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); BarVar.setValue(18); std::unique_ptr BarVarUse = std::make_unique("BAR", &BarVar); BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse)); - // Defined variable: eval returns right value. + // Defined variables: eval returns right value. Expected Value = Binop.eval(); ASSERT_TRUE(bool(Value)); EXPECT_EQ(60U, *Value); + EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); // 1 undefined variable: eval fails, error contains name of undefined // variable. @@ -118,6 +228,26 @@ Value = Binop.eval(); ASSERT_FALSE(Value); expectUndefErrors({"FOO", "BAR"}, Value.takeError()); + + // Literal + Variable has format of variable. + FooVarUse = std::make_unique("FOO", &FooVar); + std::unique_ptr Eighteen = + std::make_unique(18); + Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen)); + EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); + FooVarUse = std::make_unique("FOO", &FooVar); + Eighteen = std::make_unique(18); + Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse)); + EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); + + // Variables with different implicit format conflict. + NumericVariable BazVar("BAZ", + ExpressionFormat(ExpressionFormat::Kind::HexLower), 3); + FooVarUse = std::make_unique("BAZ", &FooVar); + std::unique_ptr BazVarUse = + std::make_unique("BAZ", &BazVar); + Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse)); + EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict); } TEST_F(FileCheckTest, ValidVarNameStart) { @@ -132,14 +262,6 @@ EXPECT_FALSE(Pattern::isValidVarNameStart(':')); } -static StringRef bufferize(SourceMgr &SM, StringRef Str) { - std::unique_ptr Buffer = - MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); - StringRef StrBufferRef = Buffer->getBuffer(); - SM.AddNewSourceBuffer(std::move(Buffer), SMLoc()); - return StrBufferRef; -} - TEST_F(FileCheckTest, ParseVar) { SourceMgr SM; StringRef OrigVarName = bufferize(SM, "GoodVar42"); @@ -215,7 +337,7 @@ SourceMgr SM; FileCheckRequest Req; FileCheckPatternContext Context; - Pattern P{Check::CheckPlain, &Context, LineNumber++}; + Pattern P{Check::CheckPlain, &Context, LineNumber}; public: PatternTester() { @@ -236,15 +358,17 @@ } void initNextPattern() { - P = Pattern(Check::CheckPlain, &Context, LineNumber++); + P = Pattern(Check::CheckPlain, &Context, ++LineNumber); } + size_t getLineNumber() const { return LineNumber; } + bool parseSubstExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(SM, Expr); Optional DefinedNumericVariable; return errorToBool( P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable, - false, LineNumber - 1, &Context, SM) + false, LineNumber, &Context, SM) .takeError()); } @@ -276,12 +400,24 @@ // Garbage after name of variable being defined. EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:")); + // Change of format. + EXPECT_TRUE(Tester.parseSubstExpect("%X,FOO:")); + + // Invalid format. + EXPECT_TRUE(Tester.parseSubstExpect("X,VAR1:")); + EXPECT_TRUE(Tester.parseSubstExpect("%F,VAR1:")); + EXPECT_TRUE(Tester.parseSubstExpect("%X a,VAR1:")); + // Acceptable variable definition. EXPECT_FALSE(Tester.parseSubstExpect("VAR1:")); EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:")); EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :")); EXPECT_FALSE(Tester.parseSubstExpect("VAR3: ")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%u, VAR_UNSIGNED:]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%x, VAR_LOWER_HEX:]]")); + EXPECT_FALSE(Tester.parseSubstExpect("%X, VAR_UPPER_HEX:")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOOBAR: FOO+1]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%x,FOOBAZ: FOO+1]]")); // Numeric expression. @@ -310,20 +446,40 @@ ASSERT_FALSE(Tester.parsePatternExpect("[[#LINE2VAR:]]")); EXPECT_TRUE(Tester.parseSubstExpect("LINE2VAR")); + // Invalid format. + EXPECT_TRUE(Tester.parseSubstExpect("X,FOO")); + EXPECT_TRUE(Tester.parseSubstExpect("%F,FOO")); + EXPECT_TRUE(Tester.parseSubstExpect("%X a,FOO")); + // Unsupported operator. EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2")); // Missing offset operand. EXPECT_TRUE(Tester.parseSubstExpect("@LINE+")); + // Invalid expression in legacy @LINE expression. + EXPECT_TRUE(Tester.parsePatternExpect("[[@LINE+FOO]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[2+@LINE]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[@LINE+0xC]]")); + // Valid expression. EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5")); EXPECT_FALSE(Tester.parseSubstExpect("FOO+4")); + EXPECT_FALSE(Tester.parseSubstExpect("FOO+0xC")); + EXPECT_FALSE(Tester.parseSubstExpect("%u, FOO")); + EXPECT_FALSE(Tester.parseSubstExpect("%x, FOO")); + EXPECT_FALSE(Tester.parseSubstExpect("%X, FOO")); Tester.initNextPattern(); + EXPECT_FALSE(Tester.parseSubstExpect("VAR_LOWER_HEX")); EXPECT_FALSE(Tester.parseSubstExpect("FOOBAR")); EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+VAR_UNSIGNED]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%X,FOO+VAR_LOWER_HEX]]")); + + // Implicit format conflict. + EXPECT_TRUE(Tester.parsePatternExpect("[[#FOO+VAR_LOWER_HEX]]")); } TEST_F(FileCheckTest, ParsePattern) { @@ -356,7 +512,9 @@ // Valid numeric expressions and numeric variable definition. EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]")); EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE+2]]")); - EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR:]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%X,FOO+0xc]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR1:]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#%X,NUMVAR2:]]")); } TEST_F(FileCheckTest, Match) { @@ -367,34 +525,70 @@ EXPECT_TRUE(Tester.matchExpect("FAIL")); EXPECT_FALSE(Tester.matchExpect("18")); - // Check matching a definition only matches a number. + // Check matching a definition only matches a number with the right format. Tester.initNextPattern(); Tester.parsePatternExpect("[[#NUMVAR:]]"); EXPECT_TRUE(Tester.matchExpect("FAIL")); EXPECT_TRUE(Tester.matchExpect("")); EXPECT_FALSE(Tester.matchExpect("18")); + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#%u,NUMVAR_UNSIGNED:]]"); + EXPECT_TRUE(Tester.matchExpect("C")); + EXPECT_FALSE(Tester.matchExpect("20")); + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#%x,NUMVAR_LOWER_HEX:]]"); + EXPECT_TRUE(Tester.matchExpect("g")); + EXPECT_FALSE(Tester.matchExpect("c")); + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#%X,NUMVAR_UPPER_HEX:]]"); + EXPECT_TRUE(Tester.matchExpect("H")); + EXPECT_FALSE(Tester.matchExpect("B")); + + // Check matching a defined variable matches the right value. + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#NUMVAR]]"); + EXPECT_FALSE(Tester.matchExpect("18")); + + // Check matching expressions with no explicit format matches the values in + // the right format. + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#NUMVAR_UNSIGNED-5]]"); + EXPECT_TRUE(Tester.matchExpect("f")); + EXPECT_TRUE(Tester.matchExpect("F")); + EXPECT_FALSE(Tester.matchExpect("15")); + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#NUMVAR_LOWER_HEX+1]]"); + EXPECT_TRUE(Tester.matchExpect("13")); + EXPECT_TRUE(Tester.matchExpect("D")); + EXPECT_FALSE(Tester.matchExpect("d")); + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#NUMVAR_UPPER_HEX+1]]"); + EXPECT_TRUE(Tester.matchExpect("12")); + EXPECT_TRUE(Tester.matchExpect("c")); + EXPECT_FALSE(Tester.matchExpect("C")); - // Check matching the variable defined matches the correct number only + // Check matching several substitutions does not match them independently. Tester.initNextPattern(); Tester.parsePatternExpect("[[#NUMVAR]] [[#NUMVAR+2]]"); EXPECT_TRUE(Tester.matchExpect("19 21")); EXPECT_TRUE(Tester.matchExpect("18 21")); EXPECT_FALSE(Tester.matchExpect("18 20")); - // Check matching a numeric expression using @LINE after match failure uses + // Check matching a numeric expression using @LINE after a match failure uses // the correct value for @LINE. Tester.initNextPattern(); EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]")); - // Ok, @LINE is 5 now. - EXPECT_FALSE(Tester.matchExpect("5")); + // Ok, @LINE matches the current line number. + EXPECT_FALSE(Tester.matchExpect(std::to_string(Tester.getLineNumber()))); + std::cout << std::to_string(Tester.getLineNumber()) << std::endl; Tester.initNextPattern(); - // @LINE is now 6, match with substitution failure. + // Match with substitution failure. EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]")); EXPECT_TRUE(Tester.matchExpect("FOO")); Tester.initNextPattern(); - // Check that @LINE is 7 as expected. + // Check that @LINE still matches the current line number. EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]")); - EXPECT_FALSE(Tester.matchExpect("7")); + EXPECT_FALSE(Tester.matchExpect(std::to_string(Tester.getLineNumber()))); } TEST_F(FileCheckTest, Substitution) { @@ -413,21 +607,28 @@ // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. - NumericVariable LineVar("@LINE", 1); - NumericVariable NVar("N", 1); + NumericVariable LineVar( + "@LINE", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); + NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), + 1); LineVar.setValue(42); NVar.setValue(10); auto LineVarUse = std::make_unique("@LINE", &LineVar); auto NVarUse = std::make_unique("N", &NVar); - NumericSubstitution SubstitutionLine(&Context, "@LINE", std::move(LineVarUse), - 12); - NumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse), 30); + auto ExpressionLine = std::make_unique( + std::move(LineVarUse), + ExpressionFormat(ExpressionFormat::Kind::Unsigned)); + auto ExpressionN = std::make_unique( + std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); + NumericSubstitution SubstitutionLine(&Context, "@LINE", + std::move(ExpressionLine), 12); + NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN), 30); SubstValue = SubstitutionLine.getResult(); ASSERT_TRUE(bool(SubstValue)); EXPECT_EQ("42", *SubstValue); SubstValue = SubstitutionN.getResult(); ASSERT_TRUE(bool(SubstValue)); - EXPECT_EQ("10", *SubstValue); + EXPECT_EQ("A", *SubstValue); // Substitution of an undefined numeric variable fails, error holds name of // undefined variable. @@ -502,19 +703,21 @@ GlobalDefines.emplace_back(std::string("LocalVar=FOO")); GlobalDefines.emplace_back(std::string("EmptyVar=")); GlobalDefines.emplace_back(std::string("#LocalNumVar1=18")); - GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2")); + GlobalDefines.emplace_back(std::string("#%x,LocalNumVar2=LocalNumVar1+2")); + GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc")); ASSERT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM))); // Check defined variables are present and undefined is absent. StringRef LocalVarStr = "LocalVar"; StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1"); StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2"); + StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3"); StringRef EmptyVarStr = "EmptyVar"; StringRef UnknownVarStr = "UnknownVar"; Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); Pattern P(Check::CheckPlain, &Cxt, 1); Optional DefinedNumericVariable; - Expected> ExpressionASTPointer = + Expected> ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/1, &Cxt, SM); @@ -522,18 +725,26 @@ EXPECT_EQ(*LocalVar, "FOO"); Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); - ASSERT_TRUE(bool(ExpressionASTPointer)); - Expected ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_TRUE(bool(ExpressionPointer)); + Expected ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 18U); - ExpressionASTPointer = + ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/1, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionASTPointer)); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_TRUE(bool(ExpressionPointer)); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 20U); + ExpressionPointer = + P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable, + /*IsLegacyLineExpr=*/false, + /*LineNumber=*/1, &Cxt, SM); + ASSERT_TRUE(bool(ExpressionPointer)); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); + ASSERT_TRUE(bool(ExpressionVal)); + EXPECT_EQ(*ExpressionVal, 12U); ASSERT_TRUE(bool(EmptyVar)); EXPECT_EQ(*EmptyVar, ""); EXPECT_TRUE(errorToBool(UnknownVar.takeError())); @@ -546,22 +757,14 @@ // 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((*ExpressionASTPointer)->eval().takeError())); + EXPECT_TRUE(errorToBool((*ExpressionPointer)->getAST()->eval().takeError())); P = Pattern(Check::CheckPlain, &Cxt, 2); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/2, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionASTPointer)); - ExpressionVal = (*ExpressionASTPointer)->eval(); - EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( - LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, - /*LineNumber=*/2, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionASTPointer)); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_TRUE(bool(ExpressionPointer)); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); - EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - EXPECT_TRUE(errorToBool(EmptyVar.takeError())); // Clear again because parseNumericSubstitutionBlock would have created a // dummy variable and stored it in GlobalNumericVariableTable. Cxt.clearLocalVars(); @@ -576,11 +779,11 @@ ASSERT_TRUE(bool(GlobalVar)); EXPECT_EQ(*GlobalVar, "BAR"); P = Pattern(Check::CheckPlain, &Cxt, 3); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/3, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionASTPointer)); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_TRUE(bool(ExpressionPointer)); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); @@ -588,11 +791,11 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = Pattern(Check::CheckPlain, &Cxt, 4); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/4, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionASTPointer)); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_TRUE(bool(ExpressionPointer)); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); }