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 @@ -730,35 +730,60 @@ 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: +The syntax to capture a numeric value is +``[[#%,:]]`` where: -* ``%`` is an optional scanf-style matching format specifier to - indicate what number format to match (e.g. hex number). Currently accepted - format specifiers are ``%u``, ``%d``, ``%x`` and ``%X``. If absent, the - format specifier defaults to ``%u``. +* ``%,`` is an optional format specifier to indicate what number + format to match and the minimum number of digits to expect. + +* ``:`` is an optional definition of variable ```` from the + captured value. + +The syntax of ```` is: ``.`` where: + +* ``.`` is an optional printf-style precision specifier in which + ```` indicates the minimum number of digits that the value matched + must have, expecting leading zeros if needed. + +* ```` is an optional scanf-style conversion specifier + to indicate what number format to match (e.g. hex number). Currently + accepted format specifiers are ``%u``, ``%d``, ``%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:]], 0x[[#%X,IMM:]] + ; CHECK: mov r[[#REG:]], 0x[[#%.8X,ADDR:]] -would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM`` -to the value ``0xF0F0``. +would match ``mov r5, 0x0000FEFE`` and set ``REG`` to the value ``5`` and +``ADDR`` to the value ``0xFEFE``. Note that due to the precision it would fail +to match ``mov r5, 0xFEFE``. -The syntax of a numeric substitution is -``[[#%: ]]`` where: +As a result of the numeric variable definition being optional, it is possible +to only check that a numeric value is present in a given format. This can be +useful when the value itself is not useful, for instance: -* ``%`` 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. +.. code-block:: gas + + ; CHECK-NOT: mov r0, r[[#]] + +to check that a value is synthesized rather than moved around. + + +The syntax of a numeric substitution is +``[[#%, ]]`` where: + +* ```` is the same format specifier as for defining a variable but + in this context indicating how a numeric expression value should be matched + against. If absent, both components of the format specifier are 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, + denoting that the value should be unsigned with no leading zeros. In case of + conflict between format specifiers of several numeric variables, the + conversion specifier becomes mandatory but the precision specifier remains + optional. * ```` is the constraint describing how the value to match must relate to the value of the numeric expression. The only currently accepted @@ -824,20 +849,11 @@ 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 -does not matter: - -.. code-block:: gas - - ; CHECK-NOT: mov r0, r[[#]] - -to check that a value is synthesized rather than moved around. 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 -variable is assigned to the value. The unified syntax for both defining numeric -variables and checking a numeric expression is thus +variable is assigned to the value. The unified syntax for both checking a +numeric expression and capturing its value into a numeric variable 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: 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 @@ -43,16 +43,28 @@ llvm_unreachable("unknown expression format"); } -Expected ExpressionFormat::getWildcardRegex() const { +Expected ExpressionFormat::getWildcardRegex() const { + auto CreatePrecisionRegex = [this](StringRef S) { + return (S + Twine('{') + Twine(Precision) + "}").str(); + }; + switch (Value) { case Kind::Unsigned: - return StringRef("[0-9]+"); + if (Precision) + return CreatePrecisionRegex("([1-9][0-9]*)?[0-9]"); + return std::string("[0-9]+"); case Kind::Signed: - return StringRef("-?[0-9]+"); + if (Precision) + return CreatePrecisionRegex("-?([1-9][0-9]*)?[0-9]"); + return std::string("-?[0-9]+"); case Kind::HexUpper: - return StringRef("[0-9A-F]+"); + if (Precision) + return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]"); + return std::string("[0-9A-F]+"); case Kind::HexLower: - return StringRef("[0-9a-f]+"); + if (Precision) + return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]"); + return std::string("[0-9a-f]+"); default: return createStringError(std::errc::invalid_argument, "trying to match value with invalid format"); @@ -61,27 +73,47 @@ Expected ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { + uint64_t AbsoluteValue; + StringRef SignPrefix = IntegerValue.isNegative() ? "-" : ""; + if (Value == Kind::Signed) { Expected SignedValue = IntegerValue.getSignedValue(); if (!SignedValue) return SignedValue.takeError(); - return itostr(*SignedValue); + if (*SignedValue < 0) + AbsoluteValue = cantFail(IntegerValue.getAbsolute().getUnsignedValue()); + else + AbsoluteValue = *SignedValue; + } else { + Expected UnsignedValue = IntegerValue.getUnsignedValue(); + if (!UnsignedValue) + return UnsignedValue.takeError(); + AbsoluteValue = *UnsignedValue; } - Expected UnsignedValue = IntegerValue.getUnsignedValue(); - if (!UnsignedValue) - return UnsignedValue.takeError(); + std::string AbsoluteValueStr; switch (Value) { case Kind::Unsigned: - return utostr(*UnsignedValue); + case Kind::Signed: + AbsoluteValueStr = utostr(AbsoluteValue); + break; case Kind::HexUpper: - return utohexstr(*UnsignedValue, /*LowerCase=*/false); case Kind::HexLower: - return utohexstr(*UnsignedValue, /*LowerCase=*/true); + AbsoluteValueStr = utohexstr(AbsoluteValue, Value == Kind::HexLower); + break; default: return createStringError(std::errc::invalid_argument, "trying to match value with invalid format"); } + + if (Precision > AbsoluteValueStr.size()) { + unsigned LeadingZeros = Precision - AbsoluteValueStr.size(); + return (Twine(SignPrefix) + std::string(LeadingZeros, '0') + + AbsoluteValueStr) + .str(); + } + + return (Twine(SignPrefix) + AbsoluteValueStr).str(); } Expected @@ -720,41 +752,59 @@ StringRef DefExpr = StringRef(); DefinedNumericVariable = None; ExpressionFormat ExplicitFormat = ExpressionFormat(); + unsigned Precision = 0; // Parse format specifier (NOTE: ',' is also an argument seperator). size_t FormatSpecEnd = Expr.find(','); size_t FunctionStart = Expr.find('('); if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) { - Expr = Expr.ltrim(SpaceChars); - if (!Expr.consume_front("%")) + StringRef FormatExpr = Expr.take_front(FormatSpecEnd); + Expr = Expr.drop_front(FormatSpecEnd + 1); + FormatExpr = FormatExpr.trim(SpaceChars); + if (!FormatExpr.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 'd': - ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed); - 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"); + SM, FormatExpr, + "invalid matching format specification in expression"); + + // Parse precision. + if (FormatExpr.consume_front(".")) { + if (FormatExpr.consumeInteger(10, Precision)) + return ErrorDiagnostic::get(SM, FormatExpr, + "invalid precision in format specifier"); } - Expr = Expr.ltrim(SpaceChars); - if (!Expr.consume_front(",")) + if (!FormatExpr.empty()) { + // Check for unknown matching format specifier and set matching format in + // class instance representing this expression. + SMLoc FmtLoc = SMLoc::getFromPointer(FormatExpr.data()); + switch (popFront(FormatExpr)) { + case 'u': + ExplicitFormat = + ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision); + break; + case 'd': + ExplicitFormat = + ExpressionFormat(ExpressionFormat::Kind::Signed, Precision); + break; + case 'x': + ExplicitFormat = + ExpressionFormat(ExpressionFormat::Kind::HexLower, Precision); + break; + case 'X': + ExplicitFormat = + ExpressionFormat(ExpressionFormat::Kind::HexUpper, Precision); + break; + default: + return ErrorDiagnostic::get(SM, FmtLoc, + "invalid format specifier in expression"); + } + } + + FormatExpr = FormatExpr.ltrim(SpaceChars); + if (!FormatExpr.empty()) return ErrorDiagnostic::get( - SM, Expr, "invalid matching format specification in expression"); + SM, FormatExpr, + "invalid matching format specification in expression"); } // Save variable definition expression if any. @@ -814,7 +864,7 @@ Format = *ImplicitFormat; } if (!Format) - Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision); std::unique_ptr ExpressionPointer = std::make_unique(std::move(ExpressionASTPointer), Format); @@ -948,7 +998,7 @@ bool IsLegacyLineExpr = false; StringRef DefName; StringRef SubstStr; - StringRef MatchRegexp; + std::string MatchRegexp; size_t SubstInsertIdx = RegExStr.size(); // Parse string variable or legacy @LINE expression. @@ -992,7 +1042,7 @@ return true; } DefName = Name; - MatchRegexp = MatchStr; + MatchRegexp = MatchStr.str(); } else { if (IsPseudo) { MatchStr = OrigMatchStr; 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 @@ -53,15 +53,17 @@ private: Kind Value; + unsigned Precision = 0; public: /// Evaluates a format to true if it can be used in a match. explicit operator bool() const { return Value != Kind::NoFormat; } /// Define format equality: formats are equal if neither is NoFormat and - /// their kinds are the same. + /// their kinds and precision are the same. bool operator==(const ExpressionFormat &Other) const { - return Value != Kind::NoFormat && Value == Other.Value; + return Value != Kind::NoFormat && Value == Other.Value && + Precision == Other.Precision; } bool operator!=(const ExpressionFormat &Other) const { @@ -76,12 +78,14 @@ StringRef toString() const; 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. - Expected getWildcardRegex() const; + explicit ExpressionFormat(Kind Value) : Value(Value), Precision(0){}; + explicit ExpressionFormat(Kind Value, unsigned Precision) + : Value(Value), Precision(Precision){}; + + /// \returns a wildcard regular expression string that matches any value in + /// the format represented by this instance and no other value, or an error + /// if the format is NoFormat. + Expected getWildcardRegex() const; /// \returns the string representation of \p Value in the format represented /// by this instance, or an error if conversion to this format failed or the 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 @@ -49,8 +49,32 @@ 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. +; Numeric variable definition with precision specifier. +DEF PREC FMT // CHECK-LABEL: DEF PREC FMT +00000022 // CHECK-NEXT: {{^}}[[#%.8,PADDED_UNSI:]] +323232323 // CHECK-NEXT: {{^}}[[#%.8,PADDED_UNSI2:]] +00000018 // CHECK-NEXT: {{^}}[[#%.8u,PADDED_UNSI3:]] +181818181 // CHECK-NEXT: {{^}}[[#%.8u,PADDED_UNSI4:]] +0000000f // CHECK-NEXT: {{^}}[[#%.8x,PADDED_LHEX:]] +fffffffff // CHECK-NEXT: {{^}}[[#%.8x,PADDED_LHEX2:]] +0000000E // CHECK-NEXT: {{^}}[[#%.8X,PADDED_UHEX:]] +EEEEEEEEE // CHECK-NEXT: {{^}}[[#%.8X,PADDED_UHEX2:]] +-00000055 // CHECK-NEXT: {{^}}[[#%.8d,PADDED_SIGN:]] +-555555555 // CHECK-NEXT: {{^}}[[#%.8d,PADDED_SIGN2:]] + +; Numeric variable definition with precision specifier with value not padded +; enough. +RUN: FileCheck --check-prefix INVALID-PADDING-DEF --input-file %s %s + +FAIL DEF PREC FMT // INVALID-PADDING-DEF-LABEL: FAIL DEF PREC FMT +INVALID_PADDED_UNSI: 0000022 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UNSI: [[#%.8,INVALID_PADDED_UNSI:]] +INVALID_PADDED_UNSI2: 0000018 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UNSI2: [[#%.8u,INVALID_PADDED_UNSI2:]] +INVALID_PADDED_LHEX: 000000f // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_LHEX: [[#%.8x,INVALID_PADDED_LHEX:]] +INVALID_PADDED_UHEX: 000000E // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_UHEX: [[#%.8X,INVALID_PADDED_UHEX:]] +INVALID_PADDED_SIGN: -0000055 // INVALID-PADDING-DEF-NOT: {{^}}INVALID_PADDED_SIGN: [[#%.8d,INVALID_PADDED_SIGN:]] + +; Numeric expressions with explicit matching format and default matching rule +; using variables defined on other lines without spaces. USE EXPL FMT IMPL MATCH // CHECK-LABEL: USE EXPL FMT IMPL MATCH 11 // CHECK-NEXT: {{^}}[[#%u,UNSI]] 12 // CHECK-NEXT: {{^}}[[#%u,UNSI+1]] @@ -92,8 +116,8 @@ 11 // CHECK-NEXT: {{^}}[[#%u,UNSIc]] c // CHECK-NEXT: {{^}}[[#%x,LHEXa]] -; Numeric expressions in explicit matching format and default matching rule using -; variables defined on other lines with different spacing. +; Numeric expressions with explicit matching format and default matching rule +; using variables defined on other lines with different spacing. USE EXPL FMT IMPL MATCH SPC // CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC 11 // CHECK-NEXT: {{^}}[[#%u, UNSI]] 11 // CHECK-NEXT: {{^}}[[# %u, UNSI]] @@ -120,8 +144,35 @@ 13 // CHECK-NEXT: {{^}}[[# %u , add (UNSI,2)]] 104 // CHECK-NEXT: {{^}}[[# %u , UNSI + sub( add (100 , UNSI+ 1 ), 20) +1 ]] -; Numeric expressions in implicit matching format and default matching rule using -; variables defined on other lines. +; Numeric expressions with explicit matching format, precision, and default +; matching rule using variables defined on other lines without spaces. +USE EXPL FMT WITH PREC IMPL MATCH // CHECK-LABEL: USE EXPL FMT WITH PREC IMPL MATCH +11 // CHECK-NEXT: {{^}}[[#%.1u,UNSI]] +00000011 // CHECK-NEXT: {{^}}[[#%.8u,UNSI]] +1c // CHECK-NEXT: {{^}}[[#%.1x,LHEX+16]] +0000000c // CHECK-NEXT: {{^}}[[#%.8x,LHEX]] +1D // CHECK-NEXT: {{^}}[[#%.1X,UHEX+16]] +0000000D // CHECK-NEXT: {{^}}[[#%.8X,UHEX]] +-30 // CHECK-NEXT: {{^}}[[#%.1d,SIGN]] +-00000030 // CHECK-NEXT: {{^}}[[#%.8d,SIGN]] + +; Numeric expressions with explicit matching format, precision and wrong +; padding, and default matching rule using variables defined on other lines +; without spaces. +RUN: FileCheck --check-prefixes CHECK,INVALID-PADDING-EXPL-USE --input-file %s %s + +FAIL USE IMPL FMT WITH PREC EXPL MATCH // INVALID-PADDING-EXPL-USE-LABEL: FAIL USE IMPL FMT WITH PREC IMPL MATCH +INVALID UNSI+1: 0000012 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UNSI+1: [[#%.8u,UNSI+1]] +INVALID UNSI-1: 000000010 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UNSI-1: [[#%.8u,UNSI-1]] +INVALID LHEX+1: 000000d // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID LHEX+1: [[#%.8x,LHEX+1]] +INVALID LHEX-1: 00000000b // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID LHEX-1: [[#%.8x,LHEX-1]] +INVALID UHEX+1: 000000E // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UHEX+1: [[#%.8X,UHEX+1]] +INVALID UHEX-1: 00000000C // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID UHEX-1: [[#%.8X,UHEX-1]] +INVALID SIGN+1: -0000029 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID SIGN+1: [[#%.8d,SIGN+1]] +INVALID SIGN-1: -000000031 // INVALID-PADDING-EXPL-USE-NOT: {{^}}INVALID SIGN-1: [[#%.8d,SIGN-1]] + +; Numeric expressions with implicit matching format and default matching rule +; using variables defined on other lines. USE IMPL FMT IMPL MATCH // CHECK-LABEL: USE IMPL FMT IMPL MATCH 11 // CHECK-NEXT: {{^}}[[#UNSI]] 12 // CHECK-NEXT: {{^}}[[#UNSI+1]] @@ -146,6 +197,36 @@ -29 // CHECK-NEXT: {{^}}[[#SIGN+1]] -31 // CHECK-NEXT: {{^}}[[#SIGN-1]] +; Numeric expressions with implicit matching format, precision, and default +; matching rule using variables defined on other lines. +USE IMPL FMT WITH PREC IMPL MATCH // CHECK-LABEL: USE IMPL FMT WITH PREC IMPL MATCH +00000023 // CHECK-NEXT: {{^}}[[#PADDED_UNSI+1]] +323232324 // CHECK-NEXT: {{^}}[[#PADDED_UNSI2+1]] +00000019 // CHECK-NEXT: {{^}}[[#PADDED_UNSI3+1]] +181818182 // CHECK-NEXT: {{^}}[[#PADDED_UNSI4+1]] +00000010 // CHECK-NEXT: {{^}}[[#PADDED_LHEX+1]] +1000000000 // CHECK-NEXT: {{^}}[[#PADDED_LHEX2+1]] +0000000F // CHECK-NEXT: {{^}}[[#PADDED_UHEX+1]] +EEEEEEEEF // CHECK-NEXT: {{^}}[[#PADDED_UHEX2+1]] +-00000054 // CHECK-NEXT: {{^}}[[#PADDED_SIGN+1]] +-555555554 // CHECK-NEXT: {{^}}[[#PADDED_SIGN2+1]] + +; Numeric expression with implicit matching format, precision and wrong amount +; of padding, and default matching rule using variables defined on other lines. +RUN: FileCheck --check-prefixes CHECK,INVALID-PADDING-IMPL-USE --input-file %s %s + +FAIL USE IMPL FMT WITH PREC IMPL MATCH // INVALID-PADDING-IMPL-USE-LABEL: FAIL USE IMPL FMT WITH PREC IMPL MATCH +INVALID PADDED_UNSI+1: 0000023 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI+1: [[#PADDED_UNSI+1]] +INVALID PADDED_UNSI-1: 000000021 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI-1: [[#PADDED_UNSI-1]] +INVALID PADDED_UNSI3+1: 0000019 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI3+1: [[#PADDED_UNSI3+1]] +INVALID PADDED_UNSI3-1: 000000017 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UNSI3-1: [[#PADDED_UNSI3-1]] +INVALID PADDED_LHEX+1: 0000010 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_LHEX+1: [[#PADDED_LHEX+1]] +INVALID PADDED_LHEX-1: 00000000e // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_LHEX-1: [[#PADDED_LHEX-1]] +INVALID PADDED_UHEX+1: 000000F // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UHEX+1: [[#PADDED_UHEX+1]] +INVALID PADDED_UHEX-1: 00000000D // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_UHEX-1: [[#PADDED_UHEX-1]] +INVALID PADDED_SIGN+1: -0000054 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_SIGN+1: [[#PADDED_SIGN+1]] +INVALID PADDED_SIGN-1: -000000056 // INVALID-PADDING-IMPL-USE-NOT: {{^}}INVALID PADDED_SIGN-1: [[#PADDED_SIGN-1]] + ; Numeric expressions using variables defined on other lines and an immediate ; interpreted as an unsigned value. ; Note: 9223372036854775819 = 0x8000000000000000 + 11 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 @@ -86,7 +86,9 @@ constexpr uint64_t AbsoluteMaxInt64 = static_cast(MaxInt64); struct ExpressionFormatParameterisedFixture - : public ::testing::TestWithParam { + : public ::testing::TestWithParam< + std::pair> { + unsigned Precision; bool Signed; bool AllowHex; bool AllowUpperHex; @@ -105,12 +107,13 @@ SourceMgr SM; void SetUp() override { - ExpressionFormat::Kind Kind = GetParam(); + ExpressionFormat::Kind Kind; + std::tie(Kind, Precision) = GetParam(); AllowHex = Kind == ExpressionFormat::Kind::HexLower || Kind == ExpressionFormat::Kind::HexUpper; AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper; Signed = Kind == ExpressionFormat::Kind::Signed; - Format = ExpressionFormat(Kind); + Format = ExpressionFormat(Kind, Precision); if (!AllowHex) { MaxUint64Str = std::to_string(MaxUint64); @@ -133,17 +136,42 @@ FirstInvalidCharDigits = "gG"; } - void checkWildcardRegexMatch(StringRef Input) { + void checkWildcardRegexMatch(StringRef Input, + unsigned TrailExtendTo = 0) const { SmallVector Matches; - ASSERT_TRUE(WildcardRegex.match(Input, &Matches)) - << "Wildcard regex does not match " << Input; - EXPECT_EQ(Matches[0], Input); + std::string ExtendedInput = Input.str(); + if (TrailExtendTo > Input.size()) { + ExtendedInput.append(TrailExtendTo - Input.size(), Input[0]); + } + ASSERT_TRUE(WildcardRegex.match(ExtendedInput, &Matches)) + << "Wildcard regex does not match " << ExtendedInput; + EXPECT_EQ(Matches[0], ExtendedInput); } - void checkWildcardRegexMatchFailure(StringRef Input) { + void checkWildcardRegexMatchFailure(StringRef Input) const { EXPECT_FALSE(WildcardRegex.match(Input)); } + void checkWildcardRegexCharMatchFailure(StringRef Chars) const { + for (auto C : Chars) + EXPECT_FALSE(WildcardRegex.match(StringRef(&C, 1))); + } + + std::string padWithLeadingZeros(StringRef NumStr) const { + bool Negative = NumStr.startswith("-"); + if (NumStr.size() - unsigned(Negative) >= Precision) + return NumStr.str(); + + std::string PaddedStr; + if (Negative) { + PaddedStr = "-"; + NumStr = NumStr.drop_front(); + } + PaddedStr.append(Precision - NumStr.size(), '0'); + PaddedStr.append(NumStr.str()); + return PaddedStr; + } + template void checkMatchingString(T Val, StringRef ExpectedStr) { Expected MatchingString = Format.getMatchingString(ExpressionValue(Val)); @@ -188,49 +216,62 @@ TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) { // Wildcard regex is valid. - Expected WildcardPattern = Format.getWildcardRegex(); + Expected WildcardPattern = Format.getWildcardRegex(); ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded()); - WildcardRegex = Regex((Twine("^") + *WildcardPattern).str()); + WildcardRegex = Regex((Twine("^") + *WildcardPattern + "$").str()); ASSERT_TRUE(WildcardRegex.isValid()); // Does not match empty string. checkWildcardRegexMatchFailure(""); // Matches all decimal digits and matches several of them. - checkWildcardRegexMatch("0123456789"); + StringRef LongNumber = "12345678901234567890"; + checkWildcardRegexMatch(LongNumber); // Matches negative digits. + LongNumber = "-12345678901234567890"; if (Signed) - checkWildcardRegexMatch("-42"); + checkWildcardRegexMatch(LongNumber); else - checkWildcardRegexMatchFailure("-42"); + checkWildcardRegexMatchFailure(LongNumber); // Check non digits or digits with wrong casing are not matched. if (AllowHex) { - checkWildcardRegexMatch(AcceptedHexOnlyDigits); - checkWildcardRegexMatchFailure(RefusedHexOnlyDigits); + checkWildcardRegexMatch(AcceptedHexOnlyDigits, 16); + checkWildcardRegexCharMatchFailure(RefusedHexOnlyDigits); } - checkWildcardRegexMatchFailure(FirstInvalidCharDigits); + checkWildcardRegexCharMatchFailure(FirstInvalidCharDigits); + + // Check leading zeros are only accepted if number of digits is less than the + // precision. + LongNumber = "01234567890123456789"; + if (Precision) { + checkWildcardRegexMatch(LongNumber.take_front(Precision)); + checkWildcardRegexMatchFailure(LongNumber.take_front(Precision - 1)); + if (Precision < LongNumber.size()) + checkWildcardRegexMatchFailure(LongNumber.take_front(Precision + 1)); + } else + checkWildcardRegexMatch(LongNumber); } TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) { - checkMatchingString(0, "0"); - checkMatchingString(9, "9"); + checkMatchingString(0, padWithLeadingZeros("0")); + checkMatchingString(9, padWithLeadingZeros("9")); if (Signed) { - checkMatchingString(-5, "-5"); + checkMatchingString(-5, padWithLeadingZeros("-5")); checkMatchingStringFailure(MaxUint64); - checkMatchingString(MaxInt64, MaxInt64Str); - checkMatchingString(MinInt64, MinInt64Str); + checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str)); + checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str)); } else { checkMatchingStringFailure(-5); - checkMatchingString(MaxUint64, MaxUint64Str); - checkMatchingString(MaxInt64, MaxInt64Str); + checkMatchingString(MaxUint64, padWithLeadingZeros(MaxUint64Str)); + checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str)); checkMatchingStringFailure(MinInt64); } - checkMatchingString(10, TenStr); - checkMatchingString(15, FifteenStr); + checkMatchingString(10, padWithLeadingZeros(TenStr)); + checkMatchingString(15, padWithLeadingZeros(FifteenStr)); } TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) { @@ -257,12 +298,25 @@ EXPECT_TRUE(bool(Format)); } -INSTANTIATE_TEST_CASE_P(AllowedExplicitExpressionFormat, - ExpressionFormatParameterisedFixture, - ::testing::Values(ExpressionFormat::Kind::Unsigned, - ExpressionFormat::Kind::Signed, - ExpressionFormat::Kind::HexLower, - ExpressionFormat::Kind::HexUpper), ); +INSTANTIATE_TEST_CASE_P( + AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture, + ::testing::Values(std::make_pair(ExpressionFormat::Kind::Unsigned, 0), + std::make_pair(ExpressionFormat::Kind::Signed, 0), + std::make_pair(ExpressionFormat::Kind::HexLower, 0), + std::make_pair(ExpressionFormat::Kind::HexUpper, 0), + + std::make_pair(ExpressionFormat::Kind::Unsigned, 1), + std::make_pair(ExpressionFormat::Kind::Signed, 1), + std::make_pair(ExpressionFormat::Kind::HexLower, 1), + std::make_pair(ExpressionFormat::Kind::HexUpper, 1), + + std::make_pair(ExpressionFormat::Kind::Unsigned, 16), + std::make_pair(ExpressionFormat::Kind::Signed, 16), + std::make_pair(ExpressionFormat::Kind::HexLower, 16), + std::make_pair(ExpressionFormat::Kind::HexUpper, 16), + + std::make_pair(ExpressionFormat::Kind::Unsigned, 20), + std::make_pair(ExpressionFormat::Kind::Signed, 20)), ); TEST_F(FileCheckTest, NoFormatProperties) { ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); @@ -985,6 +1039,10 @@ EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]")); EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded()); + // Acceptable variable definition with precision specifier. + EXPECT_FALSE(Tester.parsePattern("[[#%.8X, PADDED_ADDR:]]")); + EXPECT_FALSE(Tester.parsePattern("[[#%.8, PADDED_NUM:]]")); + // Acceptable variable definition from a numeric expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded()); @@ -1094,6 +1152,10 @@ EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded()); + // Valid expression with precision specifier. + EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8u, FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8, FOO"), Succeeded()); + // Valid legacy @LINE expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true), Succeeded());