[llvm] 998709b - [FileCheck] Add precision to format specifier

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 30 11:40:47 PDT 2020


Author: Thomas Preud'homme
Date: 2020-08-30T19:40:57+01:00
New Revision: 998709b7d553e89c4ff89725d3fa646468b7b437

URL: https://github.com/llvm/llvm-project/commit/998709b7d553e89c4ff89725d3fa646468b7b437
DIFF: https://github.com/llvm/llvm-project/commit/998709b7d553e89c4ff89725d3fa646468b7b437.diff

LOG: [FileCheck] Add precision to format specifier

Add printf-style precision specifier to pad numbers to a given number of
digits when matching them if the value is smaller than the given
precision. This works on both empty numeric expression (e.g. variable
definition from input) and when matching a numeric expression. The
syntax is as follows:

[[#%.<precision><format specifier>, ...]

where <format specifier> is optional and ... can be a variable
definition or not with an empty expression or not. In the absence of a
precision specifier, a variable definition will accept leading zeros.

Reviewed By: jhenderson, grimar

Differential Revision: https://reviews.llvm.org/D81667

Added: 
    

Modified: 
    llvm/docs/CommandGuide/FileCheck.rst
    llvm/lib/Support/FileCheck.cpp
    llvm/lib/Support/FileCheckImpl.h
    llvm/test/FileCheck/numeric-expression.txt
    llvm/unittests/Support/FileCheckTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index 088c141f8977..5e82e00415a1 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -730,35 +730,60 @@ numeric expression constraint based on those variables via a numeric
 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 ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
+The syntax to capture a numeric value is
+``[[#%<fmtspec>,<NUMVAR>:]]`` where:
 
-* ``%<fmtspec>`` 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``.
+* ``%<fmtspec>,`` is an optional format specifier to indicate what number
+  format to match and the minimum number of digits to expect.
+
+* ``<NUMVAR>:`` is an optional definition of variable ``<NUMVAR>`` from the
+  captured value.
+
+The syntax of ``<fmtspec>`` is: ``.<precision><conversion specifier>`` where:
+
+* ``.<precision>`` is an optional printf-style precision specifier in which
+ ``<precision>`` indicates the minimum number of digits that the value matched
+ must have, expecting leading zeros if needed.
+
+*  ``<conversion specifier>`` 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``.
 
-* ``<NUMVAR>`` 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
-``[[#%<fmtspec>: <constraint> <expr>]]`` 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:
 
-* ``%<fmtspec>`` 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
+``[[#%<fmtspec>, <constraint> <expr>]]`` where:
+
+* ``<fmtspec>`` 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.
 
 * ``<constraint>`` 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 @@ but would not match the text:
 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
 ``[[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]]`` 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
index 59d8b6bbc217..0cc3a2d73925 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -43,16 +43,28 @@ StringRef ExpressionFormat::toString() const {
   llvm_unreachable("unknown expression format");
 }
 
-Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
+Expected<std::string> 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<StringRef> ExpressionFormat::getWildcardRegex() const {
 
 Expected<std::string>
 ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const {
+  uint64_t AbsoluteValue;
+  StringRef SignPrefix = IntegerValue.isNegative() ? "-" : "";
+
   if (Value == Kind::Signed) {
     Expected<int64_t> SignedValue = IntegerValue.getSignedValue();
     if (!SignedValue)
       return SignedValue.takeError();
-    return itostr(*SignedValue);
+    if (*SignedValue < 0)
+      AbsoluteValue = cantFail(IntegerValue.getAbsolute().getUnsignedValue());
+    else
+      AbsoluteValue = *SignedValue;
+  } else {
+    Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue();
+    if (!UnsignedValue)
+      return UnsignedValue.takeError();
+    AbsoluteValue = *UnsignedValue;
   }
 
-  Expected<uint64_t> 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<ExpressionValue>
@@ -720,41 +752,59 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
   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 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
     Format = *ImplicitFormat;
   }
   if (!Format)
-    Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+    Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);
 
   std::unique_ptr<Expression> ExpressionPointer =
       std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
@@ -948,7 +998,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
       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 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
             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
index edb0c2548308..7cdeb7c766fd 100644
--- a/llvm/lib/Support/FileCheckImpl.h
+++ b/llvm/lib/Support/FileCheckImpl.h
@@ -53,15 +53,17 @@ struct ExpressionFormat {
 
 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 @@ struct ExpressionFormat {
   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<StringRef> 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<std::string> 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
index fd4a2e5b4b45..7969ca966a99 100644
--- a/llvm/test/FileCheck/numeric-expression.txt
+++ b/llvm/test/FileCheck/numeric-expression.txt
@@ -49,8 +49,32 @@ INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid fo
 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 @@ B   // CHECK-NEXT: {{^}}[[#%X,sub(UHEX,2)]]
 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 
diff erent spacing.
+; Numeric expressions with explicit matching format and default matching rule
+; using variables defined on other lines with 
diff erent 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 @@ USE EXPL FMT IMPL MATCH SPC  // CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
 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 @@ A   // CHECK-NEXT: {{^}}[[#min(UHEX,10)]]
 -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
index 80a43f1e5888..e986c434c551 100644
--- a/llvm/unittests/Support/FileCheckTest.cpp
+++ b/llvm/unittests/Support/FileCheckTest.cpp
@@ -86,7 +86,9 @@ constexpr uint64_t AbsoluteMinInt64 =
 constexpr uint64_t AbsoluteMaxInt64 = static_cast<uint64_t>(MaxInt64);
 
 struct ExpressionFormatParameterisedFixture
-    : public ::testing::TestWithParam<ExpressionFormat::Kind> {
+    : public ::testing::TestWithParam<
+          std::pair<ExpressionFormat::Kind, unsigned>> {
+  unsigned Precision;
   bool Signed;
   bool AllowHex;
   bool AllowUpperHex;
@@ -105,12 +107,13 @@ struct ExpressionFormatParameterisedFixture
   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 @@ struct ExpressionFormatParameterisedFixture
     FirstInvalidCharDigits = "gG";
   }
 
-  void checkWildcardRegexMatch(StringRef Input) {
+  void checkWildcardRegexMatch(StringRef Input,
+                               unsigned TrailExtendTo = 0) const {
     SmallVector<StringRef, 4> 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 <class T> void checkMatchingString(T Val, StringRef ExpectedStr) {
     Expected<std::string> MatchingString =
         Format.getMatchingString(ExpressionValue(Val));
@@ -188,49 +216,62 @@ struct ExpressionFormatParameterisedFixture
 
 TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) {
   // Wildcard regex is valid.
-  Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
+  Expected<std::string> 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 @@ TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) {
   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 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   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 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   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());


        


More information about the llvm-commits mailing list