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 @@ -773,8 +773,11 @@ * ``:`` is an optional definition of variable ```` from the captured value. -The syntax of ```` is: ``.`` where: +The syntax of ```` is: ``#.`` where: +* ``#`` is an optional flag available for hex values (see + ```` below) which require the value matched to be + prefixed by ``0x``. * ``.`` 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. diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp --- a/llvm/lib/FileCheck/FileCheck.cpp +++ b/llvm/lib/FileCheck/FileCheck.cpp @@ -45,8 +45,12 @@ } Expected ExpressionFormat::getWildcardRegex() const { - auto CreatePrecisionRegex = [this](StringRef S) { - return (S + Twine('{') + Twine(Precision) + "}").str(); + StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef(); + + auto CreatePrecisionRegex = [&](StringRef S) { + return (Twine(AlternateFormPrefix) + S + Twine('{') + Twine(Precision) + + "}") + .str(); }; switch (Value) { @@ -61,11 +65,11 @@ case Kind::HexUpper: if (Precision) return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]"); - return std::string("[0-9A-F]+"); + return (Twine(AlternateFormPrefix) + Twine("[0-9A-F]+")).str(); case Kind::HexLower: if (Precision) return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]"); - return std::string("[0-9a-f]+"); + return (Twine(AlternateFormPrefix) + Twine("[0-9a-f]+")).str(); default: return createStringError(std::errc::invalid_argument, "trying to match value with invalid format"); @@ -107,14 +111,17 @@ "trying to match value with invalid format"); } + StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef(); + if (Precision > AbsoluteValueStr.size()) { unsigned LeadingZeros = Precision - AbsoluteValueStr.size(); - return (Twine(SignPrefix) + std::string(LeadingZeros, '0') + - AbsoluteValueStr) + return (Twine(SignPrefix) + Twine(AlternateFormPrefix) + + std::string(LeadingZeros, '0') + AbsoluteValueStr) .str(); } - return (Twine(SignPrefix) + AbsoluteValueStr).str(); + return (Twine(SignPrefix) + Twine(AlternateFormPrefix) + AbsoluteValueStr) + .str(); } Expected @@ -122,6 +129,7 @@ const SourceMgr &SM) const { bool ValueIsSigned = Value == Kind::Signed; StringRef OverflowErrorStr = "unable to represent numeric value"; + if (ValueIsSigned) { int64_t SignedValue; @@ -133,9 +141,16 @@ bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; uint64_t UnsignedValue; + bool MissingFormPrefix = AlternateForm && !StrVal.consume_front("0x"); if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + // Error out for prefix only if failing to parse integer because that + // indicates a more serious issue with the input. It also avoid giving a + // prefix error for e.g. -0x18. + if (MissingFormPrefix) + return ErrorDiagnostic::get(SM, StrVal, "missing alternate form prefix"); + return ExpressionValue(UnsignedValue); } @@ -767,6 +782,10 @@ SM, FormatExpr, "invalid matching format specification in expression"); + // Parse alternate form flag. + SMLoc AlternateFormFlagLoc = SMLoc::getFromPointer(FormatExpr.data()); + bool AlternateForm = FormatExpr.consume_front("#"); + // Parse precision. if (FormatExpr.consume_front(".")) { if (FormatExpr.consumeInteger(10, Precision)) @@ -788,12 +807,12 @@ ExpressionFormat(ExpressionFormat::Kind::Signed, Precision); break; case 'x': - ExplicitFormat = - ExpressionFormat(ExpressionFormat::Kind::HexLower, Precision); + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower, + Precision, AlternateForm); break; case 'X': - ExplicitFormat = - ExpressionFormat(ExpressionFormat::Kind::HexUpper, Precision); + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper, + Precision, AlternateForm); break; default: return ErrorDiagnostic::get(SM, FmtLoc, @@ -801,6 +820,12 @@ } } + if (AlternateForm && ExplicitFormat != ExpressionFormat::Kind::HexLower && + ExplicitFormat != ExpressionFormat::Kind::HexUpper) + return ErrorDiagnostic::get( + SM, AlternateFormFlagLoc, + "alternate form only supported for hex values"); + FormatExpr = FormatExpr.ltrim(SpaceChars); if (!FormatExpr.empty()) return ErrorDiagnostic::get( diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h --- a/llvm/lib/FileCheck/FileCheckImpl.h +++ b/llvm/lib/FileCheck/FileCheckImpl.h @@ -54,6 +54,7 @@ private: Kind Value; unsigned Precision = 0; + bool AlternateForm = false; public: /// Evaluates a format to true if it can be used in a match. @@ -63,7 +64,7 @@ /// their kinds and precision are the same. bool operator==(const ExpressionFormat &Other) const { return Value != Kind::NoFormat && Value == Other.Value && - Precision == Other.Precision; + Precision == Other.Precision && AlternateForm == Other.AlternateForm; } bool operator!=(const ExpressionFormat &Other) const { @@ -81,6 +82,8 @@ explicit ExpressionFormat(Kind Value) : Value(Value), Precision(0){}; explicit ExpressionFormat(Kind Value, unsigned Precision) : Value(Value), Precision(Precision){}; + explicit ExpressionFormat(Kind Value, unsigned Precision, bool AlternateForm) + : Value(Value), Precision(Precision), AlternateForm(AlternateForm){}; /// \returns a wildcard regular expression string that matches any value in /// the format represented by this instance and no other value, or an error 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,6 +49,18 @@ INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}} INVALID-FMT-SPEC-MSG2-NEXT: {{^}} ^{{$}} +; Numeric variable definition of hex value with 0x prefix. +DEF ALT FORM // CHECK-LABEL: DEF ALT FORM +0xf // CHECK-NEXT: {{^}}[[#%#x,PREFIXED_LHEX:]] +0xE // CHECK-NEXT: {{^}}[[#%#X,PREFIXED_UHEX:]] + +; Numeric variable definition of hex value with missing 0x prefix. +RUN: FileCheck --check-prefix INVALID-HEX-PREFIX-DEF --input-file %s %s + +FAIL DEF ALT FORM // INVALID-HEX-PREFIX-DEF-LABEL: FAIL DEF ALT FORM +INVALID_PREFIXED_LHEX: xf // INVALID-HEX-PREFIX-DEF-NOT: {{^}}INVALID_PREFIXED_LHEX: [[#%#x,INVALID_PREFIXED_LHEX:]] +INVALID_PREFIXED_UHEX: 0E // INVALID-HEX-PREFIX-DEF-NOT: {{^}}INVALID_PREFIXED_UHEX: [[#%#X,INVALID_PREFIXED_UHEX:]] + ; Numeric variable definition with precision specifier. DEF PREC FMT // CHECK-LABEL: DEF PREC FMT 00000022 // CHECK-NEXT: {{^}}[[#%.8,PADDED_UNSI:]] @@ -144,17 +156,21 @@ 13 // CHECK-NEXT: {{^}}[[# %u , add (UNSI,2)]] 104 // CHECK-NEXT: {{^}}[[# %u , UNSI + sub( add (100 , UNSI+ 1 ), 20) +1 ]] -; Numeric expressions with explicit matching format, precision, and default -; matching rule using variables defined on other lines without spaces. +; Numeric expressions with explicit matching format, precision, form 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]] +11 // CHECK-NEXT: {{^}}[[#%.1u,UNSI]] +00000011 // CHECK-NEXT: {{^}}[[#%.8u,UNSI]] +1c // CHECK-NEXT: {{^}}[[#%.1x,LHEX+16]] +0x1c // CHECK-NEXT: {{^}}[[#%#.1x,LHEX+16]] +0000000c // CHECK-NEXT: {{^}}[[#%.8x,LHEX]] +0x0000000c // CHECK-NEXT: {{^}}[[#%#.8x,LHEX]] +1D // CHECK-NEXT: {{^}}[[#%.1X,UHEX+16]] +0x1D // CHECK-NEXT: {{^}}[[#%#.1X,UHEX+16]] +0000000D // CHECK-NEXT: {{^}}[[#%.8X,UHEX]] +0x0000000D // 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 diff --git a/llvm/unittests/FileCheck/FileCheckTest.cpp b/llvm/unittests/FileCheck/FileCheckTest.cpp --- a/llvm/unittests/FileCheck/FileCheckTest.cpp +++ b/llvm/unittests/FileCheck/FileCheckTest.cpp @@ -87,7 +87,8 @@ struct ExpressionFormatParameterisedFixture : public ::testing::TestWithParam< - std::pair> { + std::tuple> { + bool AlternateForm; unsigned Precision; bool Signed; bool AllowHex; @@ -108,12 +109,12 @@ void SetUp() override { ExpressionFormat::Kind Kind; - std::tie(Kind, Precision) = GetParam(); + std::tie(Kind, Precision, AlternateForm) = GetParam(); AllowHex = Kind == ExpressionFormat::Kind::HexLower || Kind == ExpressionFormat::Kind::HexUpper; AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper; Signed = Kind == ExpressionFormat::Kind::Signed; - Format = ExpressionFormat(Kind, Precision); + Format = ExpressionFormat(Kind, Precision, AlternateForm); if (!AllowHex) { MaxUint64Str = std::to_string(MaxUint64); @@ -138,10 +139,13 @@ void checkWildcardRegexMatch(StringRef Input, unsigned TrailExtendTo = 0) const { + ASSERT_TRUE(TrailExtendTo == 0 || AllowHex); SmallVector Matches; std::string ExtendedInput = Input.str(); - if (TrailExtendTo > Input.size()) { - ExtendedInput.append(TrailExtendTo - Input.size(), Input[0]); + size_t PrefixSize = AlternateForm ? 2 : 0; + if (TrailExtendTo > Input.size() - PrefixSize) { + size_t ExtensionSize = PrefixSize + TrailExtendTo - Input.size(); + ExtendedInput.append(ExtensionSize, Input[PrefixSize]); } ASSERT_TRUE(WildcardRegex.match(ExtendedInput, &Matches)) << "Wildcard regex does not match " << ExtendedInput; @@ -152,9 +156,16 @@ EXPECT_FALSE(WildcardRegex.match(Input)); } + std::string addBasePrefix(StringRef Num) const { + StringRef Prefix = AlternateForm ? "0x" : ""; + return (Twine(Prefix) + Twine(Num)).str(); + } + void checkWildcardRegexCharMatchFailure(StringRef Chars) const { - for (auto C : Chars) - EXPECT_FALSE(WildcardRegex.match(StringRef(&C, 1))); + for (auto C : Chars) { + std::string Str = addBasePrefix(StringRef(&C, 1)); + EXPECT_FALSE(WildcardRegex.match(Str)); + } } std::string padWithLeadingZeros(StringRef NumStr) const { @@ -207,10 +218,10 @@ static_cast(ExpectedVal)); } - void checkValueFromStringReprFailure(StringRef Str) { - StringRef OverflowErrorStr = "unable to represent numeric value"; + void checkValueFromStringReprFailure( + StringRef Str, StringRef ErrorStr = "unable to represent numeric value") { Expected ResultValue = getValueFromStringReprFailure(Str); - expectDiagnosticError(OverflowErrorStr, ResultValue.takeError()); + expectDiagnosticError(ErrorStr, ResultValue.takeError()); } }; @@ -224,9 +235,17 @@ // Does not match empty string. checkWildcardRegexMatchFailure(""); - // Matches all decimal digits and matches several of them. + // Matches all decimal digits, matches several of them and match 0x prefix + // if and only if AlternateForm is true. StringRef LongNumber = "12345678901234567890"; - checkWildcardRegexMatch(LongNumber); + StringRef PrefixedLongNumber = "0x12345678901234567890"; + if (AlternateForm) { + checkWildcardRegexMatch(PrefixedLongNumber); + checkWildcardRegexMatchFailure(LongNumber); + } else { + checkWildcardRegexMatch(LongNumber); + checkWildcardRegexMatchFailure(PrefixedLongNumber); + } // Matches negative digits. LongNumber = "-12345678901234567890"; @@ -236,27 +255,37 @@ checkWildcardRegexMatchFailure(LongNumber); // Check non digits or digits with wrong casing are not matched. + std::string LongNumberStr; if (AllowHex) { - checkWildcardRegexMatch(AcceptedHexOnlyDigits, 16); + LongNumberStr = addBasePrefix(AcceptedHexOnlyDigits); + checkWildcardRegexMatch(LongNumberStr, 16); + LongNumberStr = addBasePrefix(RefusedHexOnlyDigits); checkWildcardRegexCharMatchFailure(RefusedHexOnlyDigits); } + LongNumberStr = addBasePrefix(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); + LongNumberStr = addBasePrefix(LongNumber.take_front(Precision)); + checkWildcardRegexMatch(LongNumberStr); + LongNumberStr = addBasePrefix(LongNumber.take_front(Precision - 1)); + checkWildcardRegexMatchFailure(LongNumberStr); + if (Precision < LongNumber.size()) { + LongNumberStr = addBasePrefix(LongNumber.take_front(Precision + 1)); + checkWildcardRegexMatchFailure(LongNumberStr); + } + } else { + LongNumberStr = addBasePrefix(LongNumber); + checkWildcardRegexMatch(LongNumberStr); + } } TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) { - checkMatchingString(0, padWithLeadingZeros("0")); - checkMatchingString(9, padWithLeadingZeros("9")); + checkMatchingString(0, addBasePrefix(padWithLeadingZeros("0"))); + checkMatchingString(9, addBasePrefix(padWithLeadingZeros("9"))); if (Signed) { checkMatchingString(-5, padWithLeadingZeros("-5")); @@ -265,33 +294,38 @@ checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str)); } else { checkMatchingStringFailure(-5); - checkMatchingString(MaxUint64, padWithLeadingZeros(MaxUint64Str)); - checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str)); + checkMatchingString(MaxUint64, + addBasePrefix(padWithLeadingZeros(MaxUint64Str))); + checkMatchingString(MaxInt64, + addBasePrefix(padWithLeadingZeros(MaxInt64Str))); checkMatchingStringFailure(MinInt64); } - checkMatchingString(10, padWithLeadingZeros(TenStr)); - checkMatchingString(15, padWithLeadingZeros(FifteenStr)); + checkMatchingString(10, addBasePrefix(padWithLeadingZeros(TenStr))); + checkMatchingString(15, addBasePrefix(padWithLeadingZeros(FifteenStr))); } TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) { - checkValueFromStringRepr("0", 0); - checkValueFromStringRepr("9", 9); + checkValueFromStringRepr(addBasePrefix("0"), 0); + checkValueFromStringRepr(addBasePrefix("9"), 9); if (Signed) { checkValueFromStringRepr("-5", -5); checkValueFromStringReprFailure(MaxUint64Str); } else { - checkValueFromStringReprFailure("-5"); - checkValueFromStringRepr(MaxUint64Str, MaxUint64); + checkValueFromStringReprFailure("-" + addBasePrefix("5")); + checkValueFromStringRepr(addBasePrefix(MaxUint64Str), MaxUint64); } - checkValueFromStringRepr(TenStr, 10); - checkValueFromStringRepr(FifteenStr, 15); + checkValueFromStringRepr(addBasePrefix(TenStr), 10); + checkValueFromStringRepr(addBasePrefix(FifteenStr), 15); // Wrong casing is not tested because valueFromStringRepr() relies on // StringRef's getAsInteger() which does not allow to restrict casing. - checkValueFromStringReprFailure("G"); + checkValueFromStringReprFailure(addBasePrefix("G")); + + if (AlternateForm) + checkValueFromStringReprFailure("9", "missing alternate form prefix"); } TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) { @@ -300,23 +334,30 @@ 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)), ); + ::testing::Values( + std::make_tuple(ExpressionFormat::Kind::Unsigned, 0, false), + std::make_tuple(ExpressionFormat::Kind::Signed, 0, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 0, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 0, true), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, false), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, true), + + std::make_tuple(ExpressionFormat::Kind::Unsigned, 1, false), + std::make_tuple(ExpressionFormat::Kind::Signed, 1, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 1, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 1, true), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, false), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, true), + + std::make_tuple(ExpressionFormat::Kind::Unsigned, 16, false), + std::make_tuple(ExpressionFormat::Kind::Signed, 16, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 16, false), + std::make_tuple(ExpressionFormat::Kind::HexLower, 16, true), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, false), + std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, true), + + std::make_tuple(ExpressionFormat::Kind::Unsigned, 20, false), + std::make_tuple(ExpressionFormat::Kind::Signed, 20, false)), ); TEST_F(FileCheckTest, NoFormatProperties) { ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); @@ -1043,6 +1084,16 @@ EXPECT_FALSE(Tester.parsePattern("[[#%.8X, PADDED_ADDR:]]")); EXPECT_FALSE(Tester.parsePattern("[[#%.8, PADDED_NUM:]]")); + // Acceptable variable definition in alternate form. + EXPECT_THAT_EXPECTED(Tester.parseSubst("%#x, PREFIXED_ADDR:"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%#X, PREFIXED_ADDR:"), Succeeded()); + + // Acceptable variable definition in alternate form. + expectDiagnosticError("alternate form only supported for hex values", + Tester.parseSubst("%#u, PREFIXED_UNSI:").takeError()); + expectDiagnosticError("alternate form only supported for hex values", + Tester.parseSubst("%#d, PREFIXED_UNSI:").takeError()); + // Acceptable variable definition from a numeric expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());