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 FileCheckErrorDiagnostic::get(SM, StrVal, + "Unable to represent numeric value"); + + return IntegerValue; +} + Expected FileCheckNumericVariableUse::eval() const { Optional Value = NumericVariable->getValue(); if (Value) @@ -51,11 +92,27 @@ return EvalBinop(*LeftOp, *RightOp); } +ExpressionFormat FileCheckASTBinop::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 FileCheckNumericSubstitution::getResult() const { - Expected EvaluatedValue = ExpressionAST->eval(); + assert(Expression->getAST() != nullptr && "Substituting empty expression"); + Expected EvaluatedValue = Expression->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); - return utostr(*EvaluatedValue); + ExpressionFormat Format = Expression->getFormat(); + return Format.getMatchingString(*EvaluatedValue); } Expected FileCheckStringSubstitution::getResult() const { @@ -116,7 +173,8 @@ Expected FileCheckPattern::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(); @@ -140,10 +198,14 @@ FileCheckNumericVariable *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 FileCheckErrorDiagnostic::get( + SM, Expr, "format different from previous variable definition"); + } else + DefinedNumericVariable = + Context->makeNumericVariable(Name, ImplicitFormat, LineNumber); return DefinedNumericVariable; } @@ -170,7 +232,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; } @@ -205,7 +268,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 FileCheckErrorDiagnostic::get(SM, Expr, @@ -252,7 +316,7 @@ "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) @@ -260,10 +324,10 @@ Expr = Expr.ltrim(SpaceChars); return std::make_unique(EvalBinop, std::move(LeftOp), - std::move(*RightOpResult)); + std::move(*RightOpResult)); } -Expected> +Expected> FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional &DefinedNumericVariable, @@ -272,6 +336,40 @@ std::unique_ptr ExpressionAST = 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 FileCheckErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + + // Check for unknown matching format specifier and set matching format in + // class instance representing this expression. + SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); + switch (popFront(Expr)) { + case 'u': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + break; + case 'x': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); + break; + case 'X': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper); + break; + default: + return FileCheckErrorDiagnostic::get( + SM, fmtloc, "invalid format specifier in expression"); + } + + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front(",")) + return FileCheckErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + } + // Save variable definition expression if any. size_t DefEnd = Expr.find(':'); if (DefEnd != StringRef::npos) { @@ -281,6 +379,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. @@ -298,22 +397,45 @@ "unexpected characters at end of expression '" + Expr + "'"); } if (!ParseResult) - return ParseResult; + return ParseResult.takeError(); ExpressionAST = 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 = ExpressionAST + ? ExpressionAST->getImplicitFormat() + : ExpressionFormat(ExpressionFormat::Kind::NoFormat); + if (bool(ExplicitFormat)) + Format = ExplicitFormat; + else if (ImplicitFormat == ExpressionFormat::Kind::Conflict) + return FileCheckErrorDiagnostic::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 Expression = + std::make_unique(std::move(ExpressionAST), Format); + // Parse the numeric variable definition. if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); Expected ParseResult = - parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM); + parseNumericVariableDefinition(DefExpr, Context, LineNumber, + Expression->getFormat(), SM); if (!ParseResult) return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; } - return std::move(ExpressionAST); + return std::move(Expression); } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -487,10 +609,10 @@ } // Parse numeric substitution block. - std::unique_ptr ExpressionAST; + std::unique_ptr Expression; Optional DefinedNumericVariable; if (IsNumBlock) { - Expected> ParseResult = + Expected> ParseResult = parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, IsLegacyLineExpr, LineNumber, Context, SM); @@ -498,16 +620,18 @@ logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - ExpressionAST = std::move(*ParseResult); - SubstNeeded = ExpressionAST != nullptr; + Expression = std::move(*ParseResult); + SubstNeeded = Expression->getAST() != nullptr; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); } if (SubstNeeded) SubstStr = MatchStr; - else - MatchRegexp = "[0-9]+"; + else { + ExpressionFormat Format = Expression->getFormat(); + MatchRegexp = cantFail(Format.getWildcardRegex()); + } } // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. @@ -565,7 +689,7 @@ FileCheckSubstitution *Substitution = IsNumBlock ? Context->makeNumericSubstitution( - SubstStr, std::move(ExpressionAST), SubstInsertIdx) + SubstStr, std::move(Expression), SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -686,11 +810,11 @@ NumericVariableMatch.DefinedNumericVariable; StringRef MatchedValue = MatchInfo[CaptureParenGroup]; - uint64_t Val; - if (MatchedValue.getAsInteger(10, Val)) - return FileCheckErrorDiagnostic::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 @@ -851,10 +975,10 @@ } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, - std::unique_ptr ExpressionAST, size_t InsertIdx) { + StringRef ExpressionStr, std::unique_ptr Expression, + size_t InsertIdx) { Substitutions.push_back(std::make_unique( - this, ExpressionStr, std::move(ExpressionAST), InsertIdx)); + this, ExpressionStr, std::move(Expression), InsertIdx)); return Substitutions.back().get(); } @@ -1118,7 +1242,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; } @@ -1831,20 +1956,20 @@ // to create the necessary class instance. StringRef CmdlineDefExpr = CmdlineDef.substr(1); Optional DefinedNumericVariable; - Expected> ExpressionASTResult = + Expected> ExpressionResult = FileCheckPattern::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 ExpressionAST = - 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 = ExpressionAST->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,66 @@ // 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() { + 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) { + return Value != Kind::NoFormat && Value == Other.Value; + } + + bool operator!=(const ExpressionFormat &other) { return !(*this == other); } + + bool operator==(Kind OtherValue) { return Value == OtherValue; } + + bool operator!=(Kind OtherValue) { 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 FileCheckExpressionAST { public: @@ -38,6 +98,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 +145,39 @@ } }; +/// Class representing an expression and its matching format. +class FileCheckExpression { +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. + FileCheckExpression(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. + FileCheckExpressionAST *getAST() const { return AST.get(); } + + ExpressionFormat getFormat() const { return Format; } +}; + /// Class representing a numeric variable and its associated current value. class FileCheckNumericVariable { 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 +187,21 @@ 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. + /// 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 FileCheckNumericVariable(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; } @@ -134,6 +234,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 NumericVariable->getImplicitFormat(); + } }; /// Type of functions evaluating a given binary operation. @@ -165,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; @@ -220,15 +330,15 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - std::unique_ptr ExpressionAST; + std::unique_ptr Expression; public: - FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, - std::unique_ptr ExprAST, + FileCheckNumericSubstitution(FileCheckPatternContext *Context, + StringRef ExpressionStr, + std::unique_ptr Expression, size_t InsertIdx) - : FileCheckSubstitution(Context, Expr, InsertIdx) { - ExpressionAST = std::move(ExprAST); - } + : FileCheckSubstitution(Context, ExpressionStr, InsertIdx), + Expression(std::move(Expression)) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -274,6 +384,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; @@ -315,7 +429,7 @@ /// the context is destroyed. FileCheckSubstitution * makeNumericSubstitution(StringRef ExpressionStr, - std::unique_ptr ExpressionAST, + std::unique_ptr Expression, size_t InsertIdx); }; @@ -394,12 +508,12 @@ std::map VariableDefs; /// Structure representing the definition of a numeric variable in a pattern. - /// It holds the pointer to the class representing the numeric variable whose - /// value is being defined and the number of the parenthesis group in - /// RegExStr to capture that value. + /// It holds the pointer to the class instance holding the value and matching + /// format of the numeric variable whose value is being defined and the + /// number of the parenthesis group in RegExStr to capture that value. struct FileCheckNumericVariableMatch { - /// Pointer to class representing the numeric variable whose value is being - /// defined. + /// Pointer to class instance holding the value and matching format of the + /// numeric variable being defined. FileCheckNumericVariable *DefinedNumericVariable; /// Number of the parenthesis group in RegExStr that captures the value of @@ -464,12 +578,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> + /// 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, @@ -535,7 +649,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 @@ -547,7 +662,7 @@ 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 @@ -17,6 +17,58 @@ 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; + + ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); + 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_TRUE( + errorToBool(UnsignedFormat.valueFromStringRepr(ALower, SM).takeError())); + + ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower); + 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_TRUE( + errorToBool(HexLowerFormat.valueFromStringRepr(GLower, SM).takeError())); + + ExpressionFormat HexUpperFormat(ExpressionFormat::Kind::HexUpper); + 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_TRUE( + errorToBool(HexUpperFormat.valueFromStringRepr(HUpper, SM).takeError())); + + ExpressionFormat NoneFormat; + ASSERT_TRUE(errorToBool(NoneFormat.getMatchingString(18).takeError())); + ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); + ASSERT_TRUE(errorToBool(ConflictFormat.getMatchingString(18).takeError())); +} + TEST_F(FileCheckTest, Literal) { // Eval returns the literal's value. FileCheckExpressionLiteral Ten(10); @@ -62,7 +114,8 @@ TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: getValue and eval fail, error returned by eval holds // the name of the undefined variable. - FileCheckNumericVariable FooVar("FOO", 1); + FileCheckNumericVariable FooVar( + "FOO", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); EXPECT_EQ("FOO", FooVar.getName()); FileCheckNumericVariableUse FooVarUse("FOO", &FooVar); EXPECT_FALSE(FooVar.getValue()); @@ -90,11 +143,13 @@ } TEST_F(FileCheckTest, Binop) { - FileCheckNumericVariable FooVar("FOO", 1); + FileCheckNumericVariable FooVar( + "FOO", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); FooVar.setValue(42); std::unique_ptr FooVarUse = std::make_unique("FOO", &FooVar); - FileCheckNumericVariable BarVar("BAR", 2); + FileCheckNumericVariable BarVar( + "BAR", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); BarVar.setValue(18); std::unique_ptr BarVarUse = std::make_unique("BAR", &BarVar); @@ -132,14 +187,6 @@ EXPECT_FALSE(FileCheckPattern::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"); @@ -276,6 +323,9 @@ // Garbage after name of variable being defined. EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:")); + // Change of format. + EXPECT_TRUE(Tester.parseSubstExpect("%X,FOO:")); + // Acceptable variable definition. EXPECT_FALSE(Tester.parseSubstExpect("VAR1:")); EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:")); @@ -413,17 +463,24 @@ // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. - FileCheckNumericVariable LineVar("@LINE", 1); - FileCheckNumericVariable NVar("N", 1); + FileCheckNumericVariable LineVar( + "@LINE", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); + FileCheckNumericVariable 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); + auto ExpressionLine = std::make_unique( + std::move(LineVarUse), + ExpressionFormat(ExpressionFormat::Kind::Unsigned)); + auto ExpressionN = std::make_unique( + std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::Unsigned)); FileCheckNumericSubstitution SubstitutionLine(&Context, "@LINE", - std::move(LineVarUse), 12); - FileCheckNumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse), - 30); + std::move(ExpressionLine), 12); + FileCheckNumericSubstitution SubstitutionN(&Context, "N", + std::move(ExpressionN), 30); SubstValue = SubstitutionLine.getResult(); ASSERT_TRUE(bool(SubstValue)); EXPECT_EQ("42", *SubstValue); @@ -516,7 +573,7 @@ Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P(Check::CheckPlain, &Cxt, 1); Optional DefinedNumericVariable; - Expected> ExpressionAST = + Expected> Expression = P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/1, &Cxt, SM); @@ -524,16 +581,16 @@ EXPECT_EQ(*LocalVar, "FOO"); Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); - ASSERT_TRUE(bool(ExpressionAST)); - Expected ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + Expected ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 18U); - ExpressionAST = + Expression = P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/1, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 20U); ASSERT_TRUE(bool(EmptyVar)); @@ -548,19 +605,19 @@ // local variables, if it was created before. This is important because local // variable clearing due to --enable-var-scope happens after numeric // expressions are linked to the numeric variables they use. - EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError())); + EXPECT_TRUE(errorToBool((*Expression)->getAST()->eval().takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/2, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/2, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); EXPECT_TRUE(errorToBool(ExpressionVal.takeError())); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_TRUE(errorToBool(EmptyVar.takeError())); @@ -578,11 +635,11 @@ ASSERT_TRUE(bool(GlobalVar)); EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/3, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); @@ -590,11 +647,11 @@ Cxt.clearLocalVars(); EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError())); P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); - ExpressionAST = P.parseNumericSubstitutionBlock( + Expression = P.parseNumericSubstitutionBlock( GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, /*LineNumber=*/4, &Cxt, SM); - ASSERT_TRUE(bool(ExpressionAST)); - ExpressionVal = (*ExpressionAST)->eval(); + ASSERT_TRUE(bool(Expression)); + ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); EXPECT_EQ(*ExpressionVal, 36U); }