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,68 @@ // 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==(Kind OtherValue) const { return Value == OtherValue; } + + bool operator!=(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 +100,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 +147,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 +188,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 +233,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 +268,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 +328,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 +380,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 +421,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 +501,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 +570,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 +639,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 +650,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 \ @@ -187,7 +312,7 @@ NUMVAR: 10000000000000000000000 BIGVAL-LABEL: BIG VALUE BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]] -BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value +BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 BIGVAL-MSG-NEXT: {{^}} ^{{$}} @@ -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,8 +8,10 @@ #include "llvm/Support/FileCheck.h" #include "../lib/Support/FileCheckImpl.h" +#include "llvm/Support/Regex.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" +#include #include using namespace llvm; @@ -18,12 +20,216 @@ 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; +} + +template +static void expectError(StringRef ExpectedMsg, Error Err) { + bool ErrorHandled = false; + EXPECT_THAT_ERROR(handleErrors(std::move(Err), + [&](const ErrorT &E) { + EXPECT_NE( + E.message().find(ExpectedMsg.str()), + std::string::npos); + ErrorHandled = true; + }), + Succeeded()); + EXPECT_TRUE(ErrorHandled); +} + +static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) { + expectError(ExpectedMsg, std::move(Err)); +} + +struct ExpressionFormatParameterisedFixture + : public ::testing::TestWithParam< + std::tuple> { + void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); } + + ExpressionFormat::Kind Kind; + bool AllowHex; + bool AllowUpperHex; +}; + +TEST_P(ExpressionFormatParameterisedFixture, Format) { + SourceMgr SM; + ExpressionFormat Format(Kind); + + Expected WildcardPattern = Format.getWildcardRegex(); + ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded()); + Regex WildcardRegex(*WildcardPattern); + ASSERT_TRUE(WildcardRegex.isValid()); + // Does not match empty string. + EXPECT_FALSE(WildcardRegex.match("")); + // Matches all decimal digits and matches several of them. + SmallVector Matches; + StringRef DecimalDigits = "0123456789"; + ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches)); + EXPECT_EQ(Matches[0], DecimalDigits); + // Check non digits or digits with wrong casing are not matched. + if (AllowHex) { + StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"}; + StringRef AcceptedHexOnlyDigits = + AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0]; + StringRef RefusedHexOnlyDigits = + AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1]; + ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches)); + EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits); + EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits)); + + EXPECT_FALSE(WildcardRegex.match("g")); + EXPECT_FALSE(WildcardRegex.match("G")); + } else { + EXPECT_FALSE(WildcardRegex.match("a")); + EXPECT_FALSE(WildcardRegex.match("A")); + } + + Expected MatchingString = Format.getMatchingString(0U); + ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); + EXPECT_EQ(*MatchingString, "0"); + MatchingString = Format.getMatchingString(9U); + ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); + EXPECT_EQ(*MatchingString, "9"); + Expected TenMatchingString = Format.getMatchingString(10U); + ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded()); + Expected FifteenMatchingString = Format.getMatchingString(15U); + ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded()); + StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString; + if (AllowHex) { + if (AllowUpperHex) { + ExpectedTenMatchingString = "A"; + ExpectedFifteenMatchingString = "F"; + } else { + ExpectedTenMatchingString = "a"; + ExpectedFifteenMatchingString = "f"; + } + } else { + ExpectedTenMatchingString = "10"; + ExpectedFifteenMatchingString = "15"; + } + EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString); + EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString); + + StringRef BufferizedValidValueStr = bufferize(SM, "0"); + Expected Val = + Format.valueFromStringRepr(BufferizedValidValueStr, SM); + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(*Val, 0U); + BufferizedValidValueStr = bufferize(SM, "9"); + Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM); + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(*Val, 9U); + StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr; + StringRef TenStr, FifteenStr, InvalidTenStr; + if (AllowHex) { + if (AllowUpperHex) { + TenStr = "A"; + FifteenStr = "F"; + InvalidTenStr = "a"; + } else { + TenStr = "a"; + FifteenStr = "f"; + InvalidTenStr = "A"; + } + } else { + TenStr = "10"; + FifteenStr = "15"; + InvalidTenStr = "A"; + } + BufferizedTenStr = bufferize(SM, TenStr); + Val = Format.valueFromStringRepr(BufferizedTenStr, SM); + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(*Val, 10U); + BufferizedFifteenStr = bufferize(SM, FifteenStr); + Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM); + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(*Val, 15U); + // Wrong casing is not tested because valueFromStringRepr() relies on + // StringRef's getAsInteger() which does not allow to restrict casing. + BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr); + expectDiagnosticError( + "unable to represent numeric value", + Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError()); + + // Check boolean operator. + EXPECT_TRUE(bool(Format)); +} + +INSTANTIATE_TEST_CASE_P( + AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture, + ::testing::Values( + std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false, + /*AllowUpperHex=*/false), + std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true, + /*AllowUpperHex=*/false), + std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true, + /*AllowUpperHex=*/true)), ); + +TEST_F(FileCheckTest, NoFormatProperties) { + ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); + expectError("trying to match value with invalid format", + NoFormat.getWildcardRegex().takeError()); + expectError("trying to match value with invalid format", + NoFormat.getMatchingString(18).takeError()); + EXPECT_FALSE(bool(NoFormat)); +} + +TEST_F(FileCheckTest, ConflictFormatProperties) { + ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); + expectError("trying to match value with invalid format", + ConflictFormat.getWildcardRegex().takeError()); + expectError("trying to match value with invalid format", + ConflictFormat.getMatchingString(18).takeError()); + EXPECT_FALSE(bool(ConflictFormat)); +} + +TEST_F(FileCheckTest, FormatEqualityOperators) { + ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); + ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned); + EXPECT_TRUE(UnsignedFormat == UnsignedFormat2); + EXPECT_FALSE(UnsignedFormat != UnsignedFormat2); + + ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower); + EXPECT_FALSE(UnsignedFormat == HexLowerFormat); + EXPECT_TRUE(UnsignedFormat != HexLowerFormat); + + ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); + ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat); + EXPECT_FALSE(NoFormat == NoFormat2); + EXPECT_TRUE(NoFormat != NoFormat2); + + ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); + ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict); + EXPECT_TRUE(ConflictFormat == ConflictFormat2); + EXPECT_FALSE(ConflictFormat != ConflictFormat2); +} + +TEST_F(FileCheckTest, FormatKindEqualityOperators) { + ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); + EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned); + EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned); + EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower); + EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower); + ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); + EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict); + EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict); + ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); + EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat); + EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat); +} + TEST_F(FileCheckTest, Literal) { // Eval returns the literal's value. ExpressionLiteral Ten(10); Expected Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(10U, *Value); + EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat); // Max value can be correctly represented. ExpressionLiteral Max(std::numeric_limits::max()); @@ -43,6 +249,16 @@ return Str; } +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); +} + static void expectUndefErrors(std::unordered_set ExpectedUndefVarNames, Error Err) { @@ -60,9 +276,12 @@ 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(); expectUndefErrors({"FOO"}, EvalResult.takeError()); @@ -86,20 +305,24 @@ } 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; implicit format is as + // expected. Expected Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(60U, *Value); + EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); // 1 undefined variable: eval fails, error contains name of undefined // variable. @@ -112,6 +335,26 @@ BarVar.clearValue(); Value = Binop.eval(); 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) { @@ -126,32 +369,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; -} - -template -static void expectError(StringRef ExpectedMsg, Error Err) { - bool ErrorHandled = false; - EXPECT_THAT_ERROR(handleErrors(std::move(Err), - [&](const ErrorT &E) { - EXPECT_NE( - E.message().find(ExpectedMsg.str()), - std::string::npos); - ErrorHandled = true; - }), - Succeeded()); - EXPECT_TRUE(ErrorHandled); -} - -static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) { - expectError(ExpectedMsg, std::move(Err)); -} - TEST_F(FileCheckTest, ParseVar) { SourceMgr SM; StringRef OrigVarName = bufferize(SM, "GoodVar42"); @@ -257,7 +474,7 @@ size_t getLineNumber() const { return LineNumber; } - Expected> + Expected> parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) { StringRef ExprBufferRef = bufferize(SM, Expr); Optional DefinedNumericVariable; @@ -295,13 +512,36 @@ expectDiagnosticError("unexpected characters after numeric variable name", Tester.parseSubst("VAR GARBAGE:").takeError()); + // Change of format. + expectDiagnosticError("format different from previous variable definition", + Tester.parseSubst("%X,FOO:").takeError()); + + // Invalid format. + expectDiagnosticError("invalid matching format specification in expression", + Tester.parseSubst("X,VAR1:").takeError()); + expectDiagnosticError("invalid format specifier in expression", + Tester.parseSubst("%F,VAR1:").takeError()); + expectDiagnosticError("invalid matching format specification in expression", + Tester.parseSubst("%X a,VAR1:").takeError()); + + // Acceptable variable definition. EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3: "), Succeeded()); + + // Acceptable variable definition with format specifier. Use parsePattern for + // variables whose definition needs to be visible for later checks. + EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]")); + EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]")); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded()); + + // Acceptable variable definition from a numeric expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded()); - // Numeric expression. + // Numeric expression. Switch to next line to make above valid definition + // available in expressions. + Tester.initNextPattern(); // Invalid variable name. expectDiagnosticError("invalid operand format '%VAR'", @@ -342,8 +582,17 @@ // Valid single operand expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded()); + // Invalid format. + expectDiagnosticError("invalid matching format specification in expression", + Tester.parseSubst("X,FOO:").takeError()); + expectDiagnosticError("invalid format specifier in expression", + Tester.parseSubst("%F,FOO").takeError()); + expectDiagnosticError("invalid matching format specification in expression", + Tester.parseSubst("%X a,FOO").takeError()); + // Valid expression with 2 or more operands. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded()); expectDiagnosticError("unsupported operation '/'", @@ -367,6 +616,16 @@ "invalid variable name", Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError()); + // Invalid hex literal in legacy @LINE expression. + expectDiagnosticError( + "unexpected characters at end of expression 'xC'", + Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError()); + + // Valid expression with format specifier. + EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded()); + // Valid legacy @LINE expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true), Succeeded()); @@ -378,6 +637,18 @@ expectDiagnosticError( "unexpected characters at end of expression '+2'", Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError()); + + // Valid expression with several variables when their implicit formats do not + // conflict. + EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded()); + + // Valid implicit format conflict in presence of explicit formats. + EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded()); + + // Implicit format conflict. + expectDiagnosticError( + "variables with conflicting format specifier: need an explicit one", + Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError()); } TEST_F(FileCheckTest, ParsePattern) { @@ -407,6 +678,12 @@ // Valid numeric substitution. EXPECT_FALSE(Tester.parsePattern("[[#FOO]]")); + + // Valid legacy @LINE expression. + EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]")); + + // Invalid legacy @LINE expression with non decimal literal. + EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]")); } TEST_F(FileCheckTest, Match) { @@ -417,12 +694,44 @@ expectNotFoundError(Tester.match("FAIL").takeError()); EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded()); - // Check matching a definition only matches a number. + // Check matching a definition only matches a number with the right format. Tester.initNextPattern(); ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]")); expectNotFoundError(Tester.match("FAIL").takeError()); expectNotFoundError(Tester.match("").takeError()); EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded()); + Tester.initNextPattern(); + Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]"); + expectNotFoundError(Tester.match("C").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded()); + Tester.initNextPattern(); + Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]"); + expectNotFoundError(Tester.match("g").takeError()); + expectNotFoundError(Tester.match("C").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded()); + Tester.initNextPattern(); + Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]"); + expectNotFoundError(Tester.match("H").takeError()); + expectNotFoundError(Tester.match("b").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded()); + + // Check matching expressions with no explicit format matches the values in + // the right format. + Tester.initNextPattern(); + Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]"); + expectNotFoundError(Tester.match("f").takeError()); + expectNotFoundError(Tester.match("F").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded()); + Tester.initNextPattern(); + Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]"); + expectNotFoundError(Tester.match("13").takeError()); + expectNotFoundError(Tester.match("D").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded()); + Tester.initNextPattern(); + Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]"); + expectNotFoundError(Tester.match("12").takeError()); + expectNotFoundError(Tester.match("c").takeError()); + EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded()); // Check matching an undefined variable returns a NotFound error. Tester.initNextPattern(); @@ -441,7 +750,7 @@ expectNotFoundError(Tester.match("18 21").takeError()); EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded()); - // 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(); ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]")); @@ -476,14 +785,17 @@ // Numeric substitution blocks constituted of defined numeric variables are // substituted for the variable's value. - NumericVariable NVar("N", 1); + NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), + 1); NVar.setValue(10); auto NVarUse = std::make_unique("N", &NVar); - NumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse), + auto ExpressionN = std::make_unique( + std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); + NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN), /*InsertIdx=*/30); SubstValue = SubstitutionN.getResult(); ASSERT_THAT_EXPECTED(SubstValue, Succeeded()); - EXPECT_EQ("10", *SubstValue); + EXPECT_EQ("A", *SubstValue); // Substitution of an undefined numeric variable fails, error holds name of // undefined variable. @@ -565,7 +877,8 @@ 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_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded()); // Create @LINE pseudo numeric variable and check it is present by matching @@ -588,12 +901,13 @@ 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); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); Optional DefinedNumericVariable; - Expected> ExpressionASTPointer = + Expected> ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); @@ -601,17 +915,25 @@ EXPECT_EQ(*LocalVar, "FOO"); Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - Expected ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + Expected ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); EXPECT_EQ(*ExpressionVal, 18U); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); EXPECT_EQ(*ExpressionVal, 20U); + ExpressionPointer = + P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable, + /*IsLegacyLineExpr=*/false, + /*LineNumber=*/1, &Cxt, SM); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); + ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); + EXPECT_EQ(*ExpressionVal, 12U); ASSERT_THAT_EXPECTED(EmptyVar, Succeeded()); EXPECT_EQ(*EmptyVar, ""); expectUndefErrors({UnknownVarStr}, UnknownVar.takeError()); @@ -624,20 +946,20 @@ // 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. - expectUndefErrors({"LocalNumVar2"}, - (*ExpressionASTPointer)->eval().takeError()); + expectUndefErrors({"LocalNumVar3"}, + (*ExpressionPointer)->getAST()->eval().takeError()); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError()); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError()); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); expectUndefErrors({"EmptyVar"}, EmptyVar.takeError()); @@ -655,11 +977,11 @@ ASSERT_THAT_EXPECTED(GlobalVar, Succeeded()); EXPECT_EQ(*GlobalVar, "BAR"); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); EXPECT_EQ(*ExpressionVal, 36U); @@ -667,11 +989,11 @@ Cxt.clearLocalVars(); EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded()); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); - ExpressionASTPointer = P.parseNumericSubstitutionBlock( + ExpressionPointer = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); - ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded()); - ExpressionVal = (*ExpressionASTPointer)->eval(); + ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); + ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); EXPECT_EQ(*ExpressionVal, 36U); }