diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -105,10 +105,11 @@ Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be used in ``CHECK:`` lines. -.. option:: -D#= +.. option:: -D#,= - Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating - ```` that can be used in ``CHECK:`` lines. See section + Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to + the result of evaluating ```` that can be used in + ``CHECK:`` lines. See section ``FileCheck Numeric Variables and Expressions`` for details on supported numeric expressions. @@ -575,22 +576,40 @@ variables. 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 ``NUMVAR`` -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``. + +* ``NUMVAR`` is the name of the numeric variable to define to the matching + value. For example: .. code-block:: llvm - ; CHECK: mov r[[#REG:]], 42 + ; CHECK: mov r[[#REG:]], 0x[[#%X,IMM:]] + +would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM`` +to the value ``61680``. + +The syntax to check a numeric expression constraint is +``[[#%:]]`` where: -would match ``mov r5, 42`` and set ``REG`` to the value ``5``. +* ``%`` is the same matching format specifier as for defining numeric + variables but acting as a printf-style format to indicate how a numeric + expression value should be matched against. If absent, the format specifier + is inferred from the matching format of the numeric variable(s) used by the + expression constraint if any and they are all the same, and default to ``%u`` + if no numeric variable is used. In case of conflict between matching formats + of several numeric variables the format specifier is mandatory. -The syntax to check a numeric expression constraint is ``[[#]]`` where -``>`` is a numeric expression whose operands are either numeric -variables previously defined or integer literals. Currently supported numeric -operations are ``+`` and ``-``. A single numeric variable or integer literal is -also accepted. +* ``>`` is a numeric expression whose operands are either numeric + variables previously defined or integer immediates. Currently supported + numeric operations are ``+`` and ``-``. A single numeric variable or + integer literal is also accepted. For example: @@ -598,6 +617,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 line: @@ -605,6 +625,7 @@ load r5, [r0] load r6, [r1] + Loading from 0xa0463440 to 0xa0463447 but would not match the line: @@ -612,8 +633,10 @@ load r5, [r0] load r7, [r1] + Loading from 0xa0463440 to 0xa0463443 -due to ``7`` being unequal to ``5 + 1``. +Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to +``a0463440 + 7``. A numeric variable can also be defined to the result of a numeric expression, in which case the numeric expression constraint is checked and if verified the diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -40,6 +40,53 @@ // Numeric substitution handling code. //===----------------------------------------------------------------------===// +/// Bitfield representing the format a numeric expression value should be +/// printed into for matching. Used to represent both explicit format +/// specifiers as well as implicit format from using numeric variables. +struct FileCheckNumExprFmt { + /// Value should be printed as hex number. + unsigned Hex : 1; + /// Value should be printed using upper case letters, only used for hex + /// numbers. + unsigned Cap : 1; + + /// When unset, denote absence of format and thus that all of the other + /// fields are to be ignored. Used for implicit format of literals and empty + /// expressions. + unsigned Valid : 1; + + /// If set, there are several conflicting implicit formats in an expression. + unsigned Conflict : 1; + + /// Define format equality: formats are equal if all bits are identical. + bool operator==(const FileCheckNumExprFmt &other); + bool operator!=(const FileCheckNumExprFmt &other) { + return !(*this == other); + } + + /// Return wildcard regexp StringRef to match any value in the format + /// represented by this instance. + StringRef getWildcardRegex() const; + + /// Return the string representation of \p Value in the format represented by + /// this instance. + std::string getMatchingString(uint64_t Value) const; + + /// Return the value corresponding to string representation \p StrVal + /// according to the matching format represented by this instance or nothing + /// if \p StrVal does not correspond to a valid and representable value. + llvm::Optional valueFromStringRepr(StringRef StrVal) const; +}; + +/// Initializer for numeric expression without format. +const FileCheckNumExprFmt FmtNone = {0, 0, 0, 0}; +/// Initializer for numeric expression matched as unsigned value. +const FileCheckNumExprFmt FmtUnsigned = {0, 0, 1, 0}; +/// Initializer for numeric expression matched as lower case hex value. +const FileCheckNumExprFmt FmtLowHex = {1, 0, 1, 0}; +/// Initializer for numeric expression matched as capital case hex value. +const FileCheckNumExprFmt FmtCapHex = {1, 1, 1, 0}; + /// Base class representing the AST of a given numeric expression. class FileCheckNumExprAST { public: @@ -49,6 +96,11 @@ /// AST. virtual llvm::Optional eval() const = 0; + /// \returns implicit format of this AST, FmtConflict if implicit formats of + /// the AST's components conflict and Fmt none if the AST has no implicit + /// format (e.g. AST is made of a single literal). + virtual FileCheckNumExprFmt getImplicitFmt() const = 0; + /// Appends names of undefined variables used in the expression represented /// by this AST. Must be overriden in any subclass representing an expression /// that can contain a variable. @@ -69,6 +121,9 @@ /// Evaluates and returns the value of the expression represented by this /// AST. Therefore, \returns the literal's value. llvm::Optional eval() const { return Value; } + + /// Return implicit format of this AST, therefore FmtNone. + FileCheckNumExprFmt getImplicitFmt() const { return FmtNone; } }; /// Class representing a numeric expression and its matching format. @@ -77,14 +132,26 @@ /// Pointer to AST of the numeric expression. std::shared_ptr AST; + /// Matching format, i.e format (e.g. hex upper case letters) to use for the + /// value when matching it. + FileCheckNumExprFmt Fmt; + public: /// Generic constructor for a numeric expression whose equality constraint is - /// represented by \p AST. - FileCheckNumExpr(std::shared_ptr AST) : AST(AST) {} + /// represented by \p AST and matching format is \p Fmt. If matching format + /// is unset (ie. no explicit or implicit matching format), set it to default + /// one (unsigned decimal integer). + FileCheckNumExpr(std::shared_ptr AST, + FileCheckNumExprFmt Fmt); /// \returns pointer to AST of the numeric expression. Pointer is guaranteed /// to be valid as long as this object is. FileCheckNumExprAST *getAST() const { return AST.get(); } + + /// \returns effective format of this numeric expression, ie (i) its explicit + /// format if any, (ii) its implicit format if any or (iii) the default + /// format. + FileCheckNumExprFmt getEffectiveFmt() { return Fmt; } }; /// Class representing a numeric variable with a given value in the AST of a @@ -137,6 +204,9 @@ /// performing the substitutions. bool isMatchTimeKnown() const; + /// \returns implicit format of this numeric variable. + FileCheckNumExprFmt getImplicitFmt() const; + /// Appends numeric variable's name to UndefVarNames if undefined. void appendUndefVarNames(std::vector &UndefVarNames) const; @@ -180,6 +250,11 @@ /// expression value otherwise. llvm::Optional eval() const; + /// \returns implicit format of this AST, FmtConflict if implicit formats of + /// the AST's components conflict and Fmt none if the AST has no implicit + /// format (e.g. AST is made of a single literal). + FileCheckNumExprFmt getImplicitFmt() const; + /// Appends names of undefined variables used in any of the operands of this /// binary operation. void appendUndefVarNames(std::vector &UndefVarNames) const; @@ -293,10 +368,11 @@ /// Structure representing the definition of a numeric variable in a pattern. /// It holds the parenthesized capture number and the pointer to the class -/// representing the numeric variable whose value is being defined. +/// instance holding the value and matching format of the numeric variable +/// being defined. struct FileCheckNumExprMatch { - /// 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; /// Parenthesized capture number for this numeric variable definition. @@ -354,7 +430,8 @@ private: /// Makes a new numeric expression instance and registers it for destruction /// when the context is destroyed. - FileCheckNumExpr *makeNumExpr(std::shared_ptr AST); + FileCheckNumExpr *makeNumExpr(std::shared_ptr AST, + FileCheckNumExprFmt Fmt); }; class FileCheckPattern { diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -24,6 +24,49 @@ using namespace llvm; +bool FileCheckNumExprFmt::operator==(const FileCheckNumExprFmt &other) { + return Valid == other.Valid && Hex == other.Hex && Cap == other.Cap; +} + +StringRef FileCheckNumExprFmt::getWildcardRegex() const { + assert(Valid && !Conflict && "Trying to match value with invalid format"); + if (Hex) { + if (Cap) + return StringRef("[[:digit:]A-F]+"); + else + return StringRef("[[:digit:]a-f]+"); + } else + return StringRef("[[:digit:]]+"); +} + +std::string FileCheckNumExprFmt::getMatchingString(uint64_t Value) const { + assert(Valid && !Conflict && "Trying to match value with invalid format"); + if (Hex) + return utohexstr(Value, !Cap); + else + return utostr(Value); +} + +llvm::Optional +FileCheckNumExprFmt::valueFromStringRepr(StringRef StrVal) const { + unsigned Radix = Hex ? 16 : 10; + uint64_t Value; + + if (StrVal.getAsInteger(Radix, Value)) + return llvm::None; + + return Value; +} + +FileCheckNumExpr::FileCheckNumExpr(std::shared_ptr AST, + FileCheckNumExprFmt Fmt) + : AST(AST) { + if (Fmt.Valid) + this->Fmt = Fmt; + else + this->Fmt = FmtUnsigned; +} + llvm::Optional FileCheckNumericVariable::eval() const { if (Value) return Value; @@ -41,6 +84,13 @@ return NumExpr != nullptr && NumExpr->getAST() != nullptr; } +FileCheckNumExprFmt FileCheckNumericVariable::getImplicitFmt() const { + if (NumExpr == nullptr) + return FmtNone; + + return NumExpr->getEffectiveFmt(); +} + void FileCheckNumericVariable::appendUndefVarNames( std::vector &UndefVarNames) const { if (!Value) @@ -73,6 +123,19 @@ return EvalBinop(*LeftOp, *RightOp); } +FileCheckNumExprFmt FileCheckASTBinop::getImplicitFmt() const { + FileCheckNumExprFmt LeftFmt = LeftOp->getImplicitFmt(); + FileCheckNumExprFmt RightFmt = RightOp->getImplicitFmt(); + + FileCheckNumExprFmt Fmt = (LeftFmt.Valid) ? LeftFmt : RightFmt; + if (LeftFmt.Valid && RightFmt.Valid && LeftFmt != RightFmt) + Fmt.Conflict = 1; + + return Fmt; +} + +/// Append names of undefined variables used in any of the operands of this +/// binary operation. void FileCheckASTBinop::appendUndefVarNames( std::vector &UndefVarNames) const { LeftOp->appendUndefVarNames(UndefVarNames); @@ -86,7 +149,8 @@ llvm::Optional EvaluatedValue = NumExpr->getAST()->eval(); if (!EvaluatedValue) return llvm::None; - return utostr(*EvaluatedValue); + FileCheckNumExprFmt Fmt = NumExpr->getEffectiveFmt(); + return Fmt.getMatchingString(*EvaluatedValue); } // Look up the value and escape it so that we can put it into the regex. @@ -144,6 +208,15 @@ // FileCheck input canonicalization. StringRef SpaceChars = " \t"; +// Parsing helper function that strips the string in \p SkipStr from S. Returns +// true if string SkipStr was not in s and Optional was false. Returns false +// otherwise. +static bool stripFront(StringRef &S, const StringRef &SkipStr, bool Optional) { + if (!S.consume_front(SkipStr) && !Optional) + return true; + return false; +} + // Parsing helper function that strips the first character in S and returns it. static char popFront(StringRef &S) { char C = S.front(); @@ -236,8 +309,9 @@ // Otherwise, parse it as a literal. if (AO != LegacyLiteral && AO != Any) return nullptr; + unsigned Radix = (AO == LegacyLiteral) ? 10 : 0; uint64_t LiteralValue; - if (Expr.consumeInteger(10, LiteralValue)) + if (Expr.consumeInteger(Radix, LiteralValue)) return nullptr; return std::make_shared(LiteralValue); } @@ -301,6 +375,46 @@ bool Legacy, const SourceMgr &SM) const { std::shared_ptr NumExprAST; StringRef DefExpr = StringRef(); + FileCheckNumExprFmt ExplicitFmt = FmtNone; + + // Parse format specifier. + size_t FmtSpecEnd = Expr.find(','); + if (FmtSpecEnd != StringRef::npos) { + Expr = Expr.ltrim(SpaceChars); + if (stripFront(Expr, "%", false /*Optional*/)) { + SM.PrintMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid matching format specification in numeric expression"); + return nullptr; + } + + // Check for unknown matching format specifier and set matching format in + // class instance representing this numeric expression. + SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); + switch (popFront(Expr)) { + case 'u': + ExplicitFmt = FmtUnsigned; + break; + case 'x': + ExplicitFmt = FmtLowHex; + break; + case 'X': + ExplicitFmt = FmtCapHex; + break; + default: + SM.PrintMessage(fmtloc, SourceMgr::DK_Error, + "invalid format specifier in numeric expression"); + return nullptr; + } + + Expr = Expr.ltrim(SpaceChars); + if (stripFront(Expr, ",", false /*Optional*/)) { + SM.PrintMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid matching format specification in numeric expression"); + return nullptr; + } + } DefinedNumericVariable = nullptr; // Save variable definition expression if any. @@ -312,6 +426,7 @@ // Parse numeric expression itself. Expr = Expr.ltrim(SpaceChars); + StringRef UseExpr = Expr; if (!Expr.empty()) { // First operand in legacy numeric expression is the @LINE pseudo variable. enum AllowedOperand AO = Legacy ? LegacyVar : Any; @@ -333,7 +448,21 @@ } } - FileCheckNumExpr *NumExpr = Context->makeNumExpr(NumExprAST); + FileCheckNumExprFmt Fmt, + ImplicitFmt = NumExprAST ? NumExprAST->getImplicitFmt() : FmtNone; + // Select explicit matching format if any, implicit one otherwise. Error out + // in case of conflicting implicit format without explicit format. + if (ExplicitFmt.Valid) + Fmt = ExplicitFmt; + else if (ImplicitFmt.Conflict) { + SM.PrintMessage( + SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, + "variables with conflicting format specifier: need an explicit one"); + return nullptr; + } else + Fmt = ImplicitFmt; + + FileCheckNumExpr *NumExpr = Context->makeNumExpr(NumExprAST, Fmt); // Parse numeric variable definition. if (!DefExpr.empty()) { @@ -548,8 +677,10 @@ DefName = DefinedNumericVariable->getName(); if (SubstNeeded) SubstStr = MatchStr; - else - MatchRegexp = StringRef("[0-9]+"); + else { + FileCheckNumExprFmt Fmt = NumExpr->getEffectiveFmt(); + MatchRegexp = Fmt.getWildcardRegex(); + } } // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. @@ -718,12 +849,15 @@ NumericVariableDef.second.DefinedNumericVariable; StringRef MatchedValue = MatchInfo[CaptureParen]; - uint64_t Val; - if (MatchedValue.getAsInteger(10, Val)) { + assert(DefinedNumericVariable->getNumExpr() != nullptr); + FileCheckNumExprFmt Fmt = + DefinedNumericVariable->getNumExpr()->getEffectiveFmt(); + llvm::Optional Value = Fmt.valueFromStringRepr(MatchedValue); + if (!Value) { SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()), SourceMgr::DK_Error, "Unable to represent numeric value"); } - if (DefinedNumericVariable->setValue(Val)) + if (DefinedNumericVariable->setValue(*Value)) assert(false && "Numeric variable redefined"); } @@ -869,8 +1003,9 @@ } FileCheckNumExpr * -FileCheckPatternContext::makeNumExpr(std::shared_ptr AST) { - NumExprs.push_back(llvm::make_unique(AST)); +FileCheckPatternContext::makeNumExpr(std::shared_ptr AST, + FileCheckNumExprFmt Fmt) { + NumExprs.push_back(llvm::make_unique(AST, Fmt)); return NumExprs.back().get(); } diff --git a/llvm/test/FileCheck/line-count.txt b/llvm/test/FileCheck/line-count.txt --- a/llvm/test/FileCheck/line-count.txt +++ b/llvm/test/FileCheck/line-count.txt @@ -10,57 +10,61 @@ ; RUN: not FileCheck -check-prefix BAD9 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR9 %s ; RUN: not FileCheck -check-prefix BAD10 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR10 %s ; RUN: not FileCheck -check-prefix BAD11 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR11 %s -13 -14 aaa -15 bbb -16 ccc -17 CHECK: [[@LINE-3]] {{a}}aa -18 CHECK: [[@LINE-3]] {{b}}bb -19 CHECK: [[@LINE-3]] {{c}}cc -20 foobar -21 CHECK: [[@LINE-1]] {{foo}}bar -22 -23 arst CHECK: [[@LINE]] {{a}}rst -24 -25 BAD1: [[@LINE:cant-have-regex]] -26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in pattern variable definition -27 -28 BAD2: [[ @LINE]] -29 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace -30 -31 BAD3: [[@LINE ]] -32 ERR3: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace -33 -34 BAD4: [[ @LINE-1]] -35 ERR4: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace -36 -37 BAD5: [[@LINE -1]] -38 ERR5: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace -39 -40 BAD6: [[@LINE- 1]] -41 ERR6: line-count.txt:[[#@LINE-1]]:18: error: unexpected whitespace -42 -43 BAD7: [[@LINE-1 ]] -44 ERR7: line-count.txt:[[#@LINE-1]]:19: error: unexpected whitespace -45 -46 BAD8: [[@LIN]] -47 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN' -48 -49 BAD9: [[@LINE*2]] -50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported numeric operation '*' -51 -52 BAD10: [[@LINE-x]] -53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x' -54 -55 BAD11: [[@LINE-1x]] -56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of numeric expression 'x' -57 -58 CHECK: [[#@LINE]] CHECK -59 CHECK: [[# @LINE]] CHECK -60 CHECK: [[# @LINE ]] CHECK +; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR12 %s +14 +15 aaa +16 bbb +17 ccc +18 CHECK: [[@LINE-3]] {{a}}aa +19 CHECK: [[@LINE-3]] {{b}}bb +20 CHECK: [[@LINE-3]] {{c}}cc +21 foobar +22 CHECK: [[@LINE-1]] {{foo}}bar +23 +24 arst CHECK: [[@LINE]] {{a}}rst +25 +26 BAD1: [[@LINE:cant-have-regex]] +27 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in pattern variable definition +28 +29 BAD2: [[ @LINE]] +30 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace +31 +32 BAD3: [[@LINE ]] +33 ERR3: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace +34 +35 BAD4: [[ @LINE-1]] +36 ERR4: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace +37 +38 BAD5: [[@LINE -1]] +39 ERR5: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace +40 +41 BAD6: [[@LINE- 1]] +42 ERR6: line-count.txt:[[#@LINE-1]]:18: error: unexpected whitespace +43 +44 BAD7: [[@LINE-1 ]] +45 ERR7: line-count.txt:[[#@LINE-1]]:19: error: unexpected whitespace +46 +47 BAD8: [[@LIN]] +48 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN' +49 +50 BAD9: [[@LINE*2]] +51 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported numeric operation '*' +52 +53 BAD10: [[@LINE-x]] +54 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x' +55 +56 BAD11: [[@LINE-1x]] +57 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of numeric expression 'x' +58 +59 BAD12: [[@LINE-0xA]] +60 ERR12: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of numeric expression 'xA' 61 -62 CHECK: [[#@LINE-1]] -63 CHECK: [[# @LINE-1]] CHECK -64 CHECK: [[# @LINE -1]] CHECK -65 CHECK: [[# @LINE - 1]] CHECK -66 CHECK: [[# @LINE - 1 ]] CHECK +62 CHECK: [[#@LINE]] CHECK +63 CHECK: [[# @LINE]] CHECK +64 CHECK: [[# @LINE ]] CHECK +65 +66 CHECK: [[#@LINE-1]] +67 CHECK: [[# @LINE-1]] CHECK +68 CHECK: [[# @LINE -1]] CHECK +69 CHECK: [[# @LINE - 1]] CHECK +70 CHECK: [[# @LINE - 1 ]] CHECK diff --git a/llvm/test/FileCheck/numeric-defines-diagnostics.txt b/llvm/test/FileCheck/numeric-defines-diagnostics.txt --- a/llvm/test/FileCheck/numeric-defines-diagnostics.txt +++ b/llvm/test/FileCheck/numeric-defines-diagnostics.txt @@ -1,12 +1,12 @@ ; Test incorrect syntax for -D# option is correctly diagnosed. ; Invalid variable name: starts with a digit. -RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT +RUN: not FileCheck -D#10VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRCLINAME -NUMERRCLIFMT: Global defines:1:46: error: invalid variable name -NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) -NUMERRCLIFMT-NEXT: {{^ \^$}} +NUMERRCLINAME: Global defines:1:46: error: invalid variable name +NUMERRCLINAME-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) +NUMERRCLINAME-NEXT: {{^ \^$}} ; Invalid definition of pseudo variable. RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \ @@ -23,3 +23,11 @@ NUMERRCLITRAIL: Global defines:1:51: error: invalid numeric variable definition NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}}) NUMERRCLITRAIL-NEXT: {{^ \^$}} + +; Invalid format for variable. +RUN: not FileCheck -D#,VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRCLIFMT + +NUMERRCLIFMT: Global defines:1:45: error: invalid matching format specification in numeric expression +NUMERRCLIFMT-NEXT: Global define #1: #,VALUE=10 (parsed as: {{\[\[#,VALUE:10\]\]}}) +NUMERRCLIFMT-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/numeric-defines.txt b/llvm/test/FileCheck/numeric-defines.txt --- a/llvm/test/FileCheck/numeric-defines.txt +++ b/llvm/test/FileCheck/numeric-defines.txt @@ -1,22 +1,22 @@ ; Test functionality of -D# option: numeric variables are defined to the right ; value and CHECK directives using them match as expected given the value set. -RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefix CHECKNUM -input-file %s %s -RUN: not FileCheck -D#NUMVAL2=8 -check-prefix CHECKNUM -input-file %s %s 2>&1 \ +RUN: FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefix CHECKNUM -input-file %s %s +RUN: not FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+6' -check-prefix CHECKNUM -input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG -RUN: not FileCheck -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \ +RUN: not FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+4' -check-prefix NUMNOT -input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace -check-prefix NOT-NUMERRMSG -RUN: FileCheck -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s +RUN: FileCheck -D#%X,NUMVAL1=8 -D#NUMVAL2='NUMVAL1+6' -check-prefixes NUMNOT -input-file %s %s -Numeric value #2 = 12 +Numeric value #2 = C CHECKNUM: Numeric value #2 = [[#NUMVAL2]] NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]] NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input NUMERRMSG: defines.txt:1:1: note: scanning from here -NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL2" equal to "8" +NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL2" equal to "E" NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input -NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here -NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with numeric expression "NUMVAL2" equal to "12" +NOT-NUMERRMSG: defines.txt:[[#NUMVALLINE:@LINE-10]]:1: note: found here +NOT-NUMERRMSG: defines.txt:[[#NUMVALLINE]]:1: note: with numeric expression "NUMVAL2" equal to "C" diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -2,79 +2,192 @@ ; We use CHECK-NEXT directives to force a match on all lines with digits. -; Numeric variable definition without spaces -DEF NO SPC +; Numeric variable definition with default matching format +DEF DEFAULT FMT 11 -CHECK-LABEL: DEF NO SPC +CHECK-LABEL: DEF DEFAULT FMT CHECK-NEXT: [[#VAR1:]] -; Numeric variable definition in alternate spacing -DEF ALT SPC +;; Numeric variable definition with default matching format in alternate spacing +DEF DEFAULT FMT ALT SPC 11 11 11 -CHECK-LABEL: DEF ALT SPC +CHECK-LABEL: DEF DEFAULT FMT ALT SPC CHECK-NEXT: [[# VAR1a:]] CHECK-NEXT: [[# VAR1b :]] CHECK-NEXT: [[# VAR1c : ]] -; Numeric expressions using variables defined on other lines without spaces -USE NO SPC +; Numeric variable definition with explicit matching format +DEF FMT +c +D +CHECK-LABEL: DEF FMT +CHECK-NEXT: [[#%x,VAR2:]] +CHECK-NEXT: [[#%X,VAR3:]] + +; Numeric variable definition with explicit matching format in alternate spacing +DEF FMT ALT SPC +c +c +c +c +c +CHECK-LABEL: DEF FMT ALT SPC +CHECK-NEXT: [[#%x, VAR2a:]] +CHECK-NEXT: [[# %x, VAR2b:]] +CHECK-NEXT: [[# %x , VAR2c:]] +CHECK-NEXT: [[# %x , VAR2d :]] +CHECK-NEXT: [[# %x , VAR2e : ]] + +; Numeric expressions in explicit matching format and default matching rule using +; variables defined on other lines +USE DEF FMT IMPL MATCH 11 12 10 -CHECK-LABEL: USE -CHECK-NEXT: [[#VAR1]] -CHECK-NEXT: [[#VAR1+1]] -CHECK-NEXT: [[#VAR1-1]] +c +d +b +1a +D +E +C +1B +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]] -; Numeric expressions using variables defined on other lines in alternate -; spacing -USE ALT SPC +; Numeric expressions in explicit matching format and default matching rule using +; variables defined on other lines in alternate spacing +USE EXPL FMT IMPL MATCH ALT SPC +11 11 11 12 12 12 12 +12 +12 +10 10 10 10 10 -CHECK-LABEL: USE ALT SPC -CHECK-NEXT: [[# VAR1]] -CHECK-NEXT: [[# VAR1 ]] -CHECK-NEXT: [[# VAR1+1]] -CHECK-NEXT: [[# VAR1 +1]] -CHECK-NEXT: [[# VAR1 + 1]] -CHECK-NEXT: [[# VAR1 + 1 ]] -CHECK-NEXT: [[# VAR1-1]] -CHECK-NEXT: [[# VAR1 -1]] -CHECK-NEXT: [[# VAR1 - 1]] -CHECK-NEXT: [[# VAR1 - 1 ]] +10 +CHECK-LABEL: USE EXPL FMT IMPL MATCH ALT SPC +CHECK-NEXT: [[#%u, VAR1]] +CHECK-NEXT: [[# %u, VAR1]] +CHECK-NEXT: [[# %u, VAR1 ]] +CHECK-NEXT: [[#%u, VAR1+1]] +CHECK-NEXT: [[# %u, VAR1+1]] +CHECK-NEXT: [[# %u , VAR1+1]] +CHECK-NEXT: [[# %u , VAR1 +1]] +CHECK-NEXT: [[# %u , VAR1 + 1]] +CHECK-NEXT: [[# %u , VAR1 + 1 ]] +CHECK-NEXT: [[#%u, VAR1-1]] +CHECK-NEXT: [[# %u, VAR1-1]] +CHECK-NEXT: [[# %u , VAR1-1]] +CHECK-NEXT: [[# %u , VAR1 -1]] +CHECK-NEXT: [[# %u , VAR1 - 1]] +CHECK-NEXT: [[# %u , VAR1 - 1 ]] + +; Numeric expressions in implicit matching format and default matching rule using +; variables defined on other lines +USE IMPL FMT IMPL MATCH +11 +12 +10 +c +d +b +1a +D +E +C +1B +CHECK-LABEL: USE IMPL FMT IMPL MATCH +CHECK-NEXT: [[#VAR1]] +CHECK-NEXT: [[#VAR1+1]] +CHECK-NEXT: [[#VAR1-1]] +CHECK-NEXT: [[#VAR2]] +CHECK-NEXT: [[#VAR2+1]] +CHECK-NEXT: [[#VAR2-1]] +CHECK-NEXT: [[#VAR2+14]] +CHECK-NEXT: [[#VAR3]] +CHECK-NEXT: [[#VAR3+1]] +CHECK-NEXT: [[#VAR3-1]] +CHECK-NEXT: [[#VAR3+14]] + +; Explicit format override implicit format conflicts +VAR USE IMPL OVERRIDE FMT CONFLICT +23 +CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT +CHECK-NEXT: [[# %u, VAR1 + VAR2]] ; Numeric expressions using variables defined on the command-line and an ; immediate interpreted as an unsigned value ; Note: 9223372036854775819 = 0x8000000000000000 + 11 -; 9223372036854775808 = 0x8000000000000000 -USE UNSIGNED IMM +USE IMPL FMT IMPL MATCH UNSIGNED IMM 9223372036854775819 -CHECK-LABEL: USE UNSIGNED IMM -CHECK-NEXT: [[#VAR1+9223372036854775808]] +CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM +CHECK-NEXT: [[#VAR1+0x8000000000000000]] + +; Numeric expressions with conversion matching format and implicit matching rule +; using variables defined on other lines +USE CONV FMT IMPL MATCH +b +B +12 +13 +CHECK-LABEL: USE CONV FMT IMPL MATCH +CHECK-NEXT: [[# %x, VAR1]] +CHECK-NEXT: [[# %X, VAR1]] +CHECK-NEXT: [[# %u, VAR2]] +CHECK-NEXT: [[# %u, VAR3]] + +; Numeric variable definition with unsupported matching format +RUN: not FileCheck -check-prefixes ERR,INVALID-FMT-SPEC1 -input-file %s %s 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-FMT-SPEC-MSG1 %s +RUN: not FileCheck -check-prefixes ERR,INVALID-FMT-SPEC2 -input-file %s %s 2>&1 \ +RUN: | FileCheck -check-prefix INVALID-FMT-SPEC-MSG2 %s + +DEF INVALID FMT +INVVAR1=a +INVVAR2=11 +ERR-LABEL: DEF INVALID FMT +INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]] +INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]] +INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in numeric 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 numeric expression +INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}} +INVALID-FMT-SPEC-MSG2-NEXT: {{^ \^$}} ; Numeric expression using a variable defined from a numeric expression DEF EXPR GOOD MATCH 42 41 43 -; CHECK-LABEL: DEF EXPR GOOD MATCH -; CHECK-NEXT: [[# VAR42:VAR1+31]] -; CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]] +CHECK-LABEL: DEF EXPR GOOD MATCH +CHECK-NEXT: [[# VAR42:VAR1+31]] +CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]] ; Empty numeric expression EMPTY NUM EXPR foo 104 bar -; CHECK-LABEL: EMPTY NUM EXPR -; CHECK-NEXT: foo [[#]] bar +CHECK-LABEL: EMPTY NUM EXPR +CHECK-NEXT: foo [[#]] bar ; Numeric expression using undefined variable RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ @@ -147,3 +260,15 @@ DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH DEF-EXPR-FAIL-NEXT: [[# VAR20:]] DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]] + +; Conflicting implicit format +RUN: not FileCheck -check-prefixes CHECK,FMT-CONFLICT -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix FMT-CONFLICT-MSG %s + +VAR USE IMPL FMT CONFLICT +23 +FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT +FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]] +FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one +FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} +FMT-CONFLICT-MSG-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/pattern-defines-diagnostics.txt b/llvm/test/FileCheck/pattern-defines-diagnostics.txt --- a/llvm/test/FileCheck/pattern-defines-diagnostics.txt +++ b/llvm/test/FileCheck/pattern-defines-diagnostics.txt @@ -25,12 +25,12 @@ ERRCLIVAR2: Missing pattern variable name in command-line definition '-D=' ; Invalid variable name: starts with a digit. -RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT +RUN: not FileCheck -D10VALUE=10 -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix ERRCLINAME -ERRCLIFMT: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE' -ERRCLIFMT-NEXT: Global define #1: 10VALUE=10 -ERRCLIFMT-NEXT: {{^ \^$}} +ERRCLINAME: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE' +ERRCLINAME-NEXT: Global define #1: 10VALUE=10 +ERRCLINAME-NEXT: {{^ \^$}} ; Invalid definition of pseudo variable. RUN: not FileCheck -D@VALUE=10 --input-file %s %s 2>&1 \ diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -31,7 +31,7 @@ TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: eval and clearValue fails, appendUndefVarNames returns // the variable and setValue works. - auto NumVarExpr = FileCheckNumExpr(nullptr); + auto NumVarExpr = FileCheckNumExpr(nullptr, FmtUnsigned); FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", &NumVarExpr, 1U); EXPECT_EQ("FOO", FooVar.getName()); @@ -74,7 +74,7 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, Binop) { - auto DefNumExpr = FileCheckNumExpr(nullptr); + auto DefNumExpr = FileCheckNumExpr(nullptr, FmtUnsigned); auto FooVar = std::make_shared("FOO", &DefNumExpr, 1); FooVar->setValue(42); @@ -370,14 +370,14 @@ // Substitution of defined numeric variable returns the right value, // getUndefVarNames does not return any variable. - auto DefNumExpr = FileCheckNumExpr(nullptr); + auto DefNumExpr = FileCheckNumExpr(nullptr, FmtUnsigned); auto LineVar = std::make_shared("@LINE", &DefNumExpr, 1); auto NVar = std::make_shared("N", &DefNumExpr, 1); LineVar->setValue(42); NVar->setValue(10); - FileCheckNumExpr NumExprLine = FileCheckNumExpr(LineVar); - FileCheckNumExpr NumExprN = FileCheckNumExpr(NVar); + FileCheckNumExpr NumExprLine = FileCheckNumExpr(LineVar, FmtUnsigned); + FileCheckNumExpr NumExprN = FileCheckNumExpr(NVar, FmtUnsigned); FileCheckPatternSubstitution SubstitutionLine = FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12); FileCheckPatternSubstitution SubstitutionN = @@ -453,7 +453,7 @@ std::make_shared("@LINE", (uint64_t)42); auto Zero = std::make_shared(0); auto Binop = std::make_shared(doAdd, LineVar, Zero); - FileCheckNumExpr NumExpr = FileCheckNumExpr(Binop); + FileCheckNumExpr NumExpr = FileCheckNumExpr(Binop, FmtUnsigned); Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12); UndefVarNames.clear(); Substitution.getUndefVarNames(UndefVarNames);