OSDN Git Service

FileCheck [9/12]: Add support for matching formats
authorThomas Preud'homme <thomasp@graphcore.ai>
Tue, 5 Mar 2019 23:20:29 +0000 (23:20 +0000)
committerThomas Preud'homme <thomasp@graphcore.ai>
Fri, 24 Jan 2020 14:15:28 +0000 (14:15 +0000)
Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch adds support for selecting a
matching format to match a numeric value against (ie. decimal, hex lower
case letters or hex upper case letters).

This commit allows to select what format a numeric value should be
matched against. The following formats are supported: decimal value,
lower case hex value and upper case hex value. Matching formats impact
both the format of numeric value to be matched as well as the format of
accepted numbers in a definition with empty numeric expression
constraint.

Default for absence of format is decimal value unless the numeric
expression constraint is non null and use a variable in which case the
format is the one used to define that variable. Conclict of format in
case of several variable being used is diagnosed and forces the user to
select a matching format explicitely.

This commit also enables immediates in numeric expressions to be in any
radix known to StringRef's GetAsInteger method, except for legacy
numeric expressions (ie [[@LINE+<offset>]] which only support decimal
immediates.

Copyright:
    - Linaro (changes up to diff 183612 of revision D55940)
    - GraphCore (changes in later versions of revision D55940 and
                 in new revision created off D55940)

Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar, arichardson

Reviewed By: jhenderson, arichardson

Subscribers: daltenty, MaskRay, hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

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

llvm/docs/CommandGuide/FileCheck.rst
llvm/lib/Support/FileCheck.cpp
llvm/lib/Support/FileCheckImpl.h
llvm/test/FileCheck/line-count.txt
llvm/test/FileCheck/numeric-defines-diagnostics.txt
llvm/test/FileCheck/numeric-defines.txt
llvm/test/FileCheck/numeric-expression.txt
llvm/test/FileCheck/string-defines-diagnostics.txt
llvm/unittests/Support/FileCheckTest.cpp

index 0072c9c..49db571 100644 (file)
@@ -112,10 +112,11 @@ and from the command line.
   Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
   used in ``CHECK:`` lines.
 
-.. option:: -D#<NUMVAR>=<NUMERIC EXPRESSION>
+.. option:: -D#<FMT>,<NUMVAR>=<NUMERIC EXPRESSION>
 
-  Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
-  ``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
+  Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to
+  the result of evaluating ``<NUMERIC EXPRESSION>`` that can be used in
+  ``CHECK:`` lines.  See section
   ``FileCheck Numeric Variables and Expressions`` for details on supported
   numeric expressions.
 
@@ -588,27 +589,44 @@ 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 ``[[#<NUMVAR>:]]`` where
-``<NUMVAR>`` is the name of the numeric variable to define to the matching
-value.
+The syntax to define a numeric variable 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``, ``%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 ``0xF0F0``.
 
-would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
+The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
 
-The syntax of a numeric substitution is ``[[#<expr>]]`` where ``<expr>`` is an
-expression. An expression is recursively defined as:
+* ``%<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.
 
-* a numeric operand, or
-* an expression followed by an operator and a numeric operand.
+* ``<expr>`` is an expression. An expression is in turn recursively defined
+  as:
 
-A numeric operand is a previously defined numeric variable, or an integer
-literal. The supported operators are ``+`` and ``-``. Spaces are accepted
-before, after and between any of these elements.
+  * a numeric operand, or
+  * an expression followed by an operator and a numeric operand.
+
+  A numeric operand is a previously defined numeric variable, or an integer
+  literal. The supported operators are ``+`` and ``-``. Spaces are accepted
+  before, after and between any of these elements.
 
 For example:
 
@@ -616,6 +634,7 @@ For example:
 
     ; CHECK: load r[[#REG:]], [r0]
     ; CHECK: load r[[#REG+1]], [r1]
+    ; CHECK: Loading from 0x[[#%x,ADDR:] to 0x[[#ADDR + 7]]
 
 The above example would match the text:
 
@@ -623,6 +642,7 @@ The above example would match the text:
 
     load r5, [r0]
     load r6, [r1]
+    Loading from 0xa0463440 to 0xa0463447
 
 but would not match the text:
 
@@ -630,8 +650,10 @@ but would not match the text:
 
     load r5, [r0]
     load r7, [r1]
+    Loading from 0xa0463440 to 0xa0463443
 
-due to ``7`` being unequal to ``5 + 1``.
+Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to
+``a0463440 + 7``.
 
 The syntax also supports an empty expression, equivalent to writing {{[0-9]+}},
 for cases where the input must contain a numeric value but the value itself
@@ -646,8 +668,21 @@ 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 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 ``[[#<NUMVAR>: <expr>]]`` with each
-element as described previously.
+and checking a numeric expression is thus ``[[#%<fmtspec>,<NUMVAR>: <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:
+
+.. code-block:: gas
+
+    ; CHECK: mov r[[#REG_OFFSET:]], 0x[[#%X,FIELD_OFFSET:12]]
+    ; CHECK-NEXT: load r[[#]], [r[[#REG_BASE:]], r[[#REG_OFFSET]]]
+
+which would match:
+
+.. code-block:: gas
+
+    mov r4, 0xC
+    load r6, [r5, r4]
 
 The ``--enable-var-scope`` option has the same effect on numeric variables as
 on string variables.
index 2261ecc..d6351e8 100644 (file)
 
 using namespace llvm;
 
+Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
+  switch (Value) {
+  case Kind::Unsigned:
+    return StringRef("[0-9]+");
+  case Kind::HexUpper:
+    return StringRef("[0-9A-F]+");
+  case Kind::HexLower:
+    return StringRef("[0-9a-f]+");
+  default:
+    return createStringError(std::errc::invalid_argument,
+                             "trying to match value with invalid format");
+  }
+}
+
+Expected<std::string>
+ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
+  switch (Value) {
+  case Kind::Unsigned:
+    return utostr(IntegerValue);
+  case Kind::HexUpper:
+    return utohexstr(IntegerValue, /*LowerCase=*/false);
+  case Kind::HexLower:
+    return utohexstr(IntegerValue, /*LowerCase=*/true);
+  default:
+    return createStringError(std::errc::invalid_argument,
+                             "trying to match value with invalid format");
+  }
+}
+
+Expected<uint64_t>
+ExpressionFormat::valueFromStringRepr(StringRef StrVal,
+                                      const SourceMgr &SM) const {
+  bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
+  uint64_t IntegerValue;
+  if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
+    return ErrorDiagnostic::get(SM, StrVal,
+                                "unable to represent numeric value");
+
+  return IntegerValue;
+}
+
 Expected<uint64_t> NumericVariableUse::eval() const {
   Optional<uint64_t> Value = Variable->getValue();
   if (Value)
@@ -51,11 +92,28 @@ Expected<uint64_t> BinaryOperation::eval() const {
   return EvalBinop(*LeftOp, *RightOp);
 }
 
+ExpressionFormat BinaryOperation::getImplicitFormat() const {
+  ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
+  ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
+
+  ExpressionFormat Format =
+      LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
+  if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
+      RightFormat != ExpressionFormat::Kind::NoFormat &&
+      LeftFormat != RightFormat)
+    Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
+
+  return Format;
+}
+
 Expected<std::string> NumericSubstitution::getResult() const {
-  Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval();
+  assert(ExpressionPointer->getAST() != nullptr &&
+         "Substituting empty expression");
+  Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
   if (!EvaluatedValue)
     return EvaluatedValue.takeError();
-  return utostr(*EvaluatedValue);
+  ExpressionFormat Format = ExpressionPointer->getFormat();
+  return Format.getMatchingString(*EvaluatedValue);
 }
 
 Expected<std::string> StringSubstitution::getResult() const {
@@ -113,7 +171,8 @@ char NotFoundError::ID = 0;
 
 Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
     StringRef &Expr, FileCheckPatternContext *Context,
-    Optional<size_t> LineNumber, const SourceMgr &SM) {
+    Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+    const SourceMgr &SM) {
   Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
   if (!ParseVarResult)
     return ParseVarResult.takeError();
@@ -137,10 +196,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
 
   NumericVariable *DefinedNumericVariable;
   auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
-  if (VarTableIter != Context->GlobalNumericVariableTable.end())
+  if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
     DefinedNumericVariable = VarTableIter->second;
-  else
-    DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
+    if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
+      return ErrorDiagnostic::get(
+          SM, Expr, "format different from previous variable definition");
+  } else
+    DefinedNumericVariable =
+        Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
 
   return DefinedNumericVariable;
 }
@@ -165,7 +228,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
   if (VarTableIter != Context->GlobalNumericVariableTable.end())
     NumericVariable = VarTableIter->second;
   else {
-    NumericVariable = Context->makeNumericVariable(Name);
+    NumericVariable = Context->makeNumericVariable(
+        Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
     Context->GlobalNumericVariableTable[Name] = NumericVariable;
   }
 
@@ -198,7 +262,8 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
 
   // Otherwise, parse it as a literal.
   uint64_t LiteralValue;
-  if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
+  if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
+                           LiteralValue))
     return std::make_unique<ExpressionLiteral>(LiteralValue);
 
   return ErrorDiagnostic::get(SM, Expr,
@@ -244,7 +309,7 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
     return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
   // The second operand in a legacy @LINE expression is always a literal.
   AllowedOperand AO =
-      IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
+      IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
   Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
       parseNumericOperand(Expr, AO, LineNumber, Context, SM);
   if (!RightOpResult)
@@ -255,13 +320,47 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
                                            std::move(*RightOpResult));
 }
 
-Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
+Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
     StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
     bool IsLegacyLineExpr, Optional<size_t> LineNumber,
     FileCheckPatternContext *Context, const SourceMgr &SM) {
   std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
   StringRef DefExpr = StringRef();
   DefinedNumericVariable = None;
+  ExpressionFormat ExplicitFormat = ExpressionFormat();
+
+  // Parse format specifier.
+  size_t FormatSpecEnd = Expr.find(',');
+  if (FormatSpecEnd != StringRef::npos) {
+    Expr = Expr.ltrim(SpaceChars);
+    if (!Expr.consume_front("%"))
+      return 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 '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");
+    }
+
+    Expr = Expr.ltrim(SpaceChars);
+    if (!Expr.consume_front(","))
+      return ErrorDiagnostic::get(
+          SM, Expr, "invalid matching format specification in expression");
+  }
+
   // Save variable definition expression if any.
   size_t DefEnd = Expr.find(':');
   if (DefEnd != StringRef::npos) {
@@ -271,6 +370,7 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
 
   // Parse the expression itself.
   Expr = Expr.ltrim(SpaceChars);
+  StringRef UseExpr = Expr;
   if (!Expr.empty()) {
     // The first operand in a legacy @LINE expression is always the @LINE
     // pseudo variable.
@@ -288,22 +388,44 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
             "unexpected characters at end of expression '" + Expr + "'");
     }
     if (!ParseResult)
-      return ParseResult;
+      return ParseResult.takeError();
     ExpressionASTPointer = std::move(*ParseResult);
   }
 
+  // Select format of the expression, i.e. (i) its explicit format, if any,
+  // otherwise (ii) its implicit format, if any, otherwise (iii) the default
+  // format (unsigned). Error out in case of conflicting implicit format
+  // without explicit format.
+  ExpressionFormat Format,
+      ImplicitFormat = ExpressionASTPointer
+                           ? ExpressionASTPointer->getImplicitFormat()
+                           : ExpressionFormat(ExpressionFormat::Kind::NoFormat);
+  if (bool(ExplicitFormat))
+    Format = ExplicitFormat;
+  else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
+    return ErrorDiagnostic::get(
+        SM, UseExpr,
+        "variables with conflicting format specifier: need an explicit one");
+  else if (bool(ImplicitFormat))
+    Format = ImplicitFormat;
+  else
+    Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+
+  std::unique_ptr<Expression> ExpressionPointer =
+      std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
+
   // Parse the numeric variable definition.
   if (DefEnd != StringRef::npos) {
     DefExpr = DefExpr.ltrim(SpaceChars);
-    Expected<NumericVariable *> ParseResult =
-        parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
+    Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
+        DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
 
     if (!ParseResult)
       return ParseResult.takeError();
     DefinedNumericVariable = *ParseResult;
   }
 
-  return std::move(ExpressionASTPointer);
+  return std::move(ExpressionPointer);
 }
 
 bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@@ -476,10 +598,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
       }
 
       // Parse numeric substitution block.
-      std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+      std::unique_ptr<Expression> ExpressionPointer;
       Optional<NumericVariable *> DefinedNumericVariable;
       if (IsNumBlock) {
-        Expected<std::unique_ptr<ExpressionAST>> ParseResult =
+        Expected<std::unique_ptr<Expression>> ParseResult =
             parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
                                           IsLegacyLineExpr, LineNumber, Context,
                                           SM);
@@ -487,16 +609,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
           logAllUnhandledErrors(ParseResult.takeError(), errs());
           return true;
         }
-        ExpressionASTPointer = std::move(*ParseResult);
-        SubstNeeded = ExpressionASTPointer != nullptr;
+        ExpressionPointer = std::move(*ParseResult);
+        SubstNeeded = ExpressionPointer->getAST() != nullptr;
         if (DefinedNumericVariable) {
           IsDefinition = true;
           DefName = (*DefinedNumericVariable)->getName();
         }
         if (SubstNeeded)
           SubstStr = MatchStr;
-        else
-          MatchRegexp = "[0-9]+";
+        else {
+          ExpressionFormat Format = ExpressionPointer->getFormat();
+          MatchRegexp = cantFail(Format.getWildcardRegex());
+        }
       }
 
       // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
@@ -554,8 +678,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
           Substitution *Substitution =
               IsNumBlock
                   ? Context->makeNumericSubstitution(
-                        SubstStr, std::move(ExpressionASTPointer),
-                        SubstInsertIdx)
+                        SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
                   : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
           Substitutions.push_back(Substitution);
         }
@@ -676,11 +799,11 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
         NumericVariableMatch.DefinedNumericVariable;
 
     StringRef MatchedValue = MatchInfo[CaptureParenGroup];
-    uint64_t Val;
-    if (MatchedValue.getAsInteger(10, Val))
-      return ErrorDiagnostic::get(SM, MatchedValue,
-                                  "Unable to represent numeric value");
-    DefinedNumericVariable->setValue(Val);
+    ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
+    Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
+    if (!Value)
+      return Value.takeError();
+    DefinedNumericVariable->setValue(*Value);
   }
 
   // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
@@ -837,10 +960,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
 }
 
 Substitution *FileCheckPatternContext::makeNumericSubstitution(
-    StringRef ExpressionStr,
-    std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) {
+    StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
+    size_t InsertIdx) {
   Substitutions.push_back(std::make_unique<NumericSubstitution>(
-      this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx));
+      this, ExpressionStr, std::move(Expression), InsertIdx));
   return Substitutions.back().get();
 }
 
@@ -1104,7 +1227,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
 void FileCheckPatternContext::createLineVariable() {
   assert(!LineVariable && "@LINE pseudo numeric variable already created");
   StringRef LineName = "@LINE";
-  LineVariable = makeNumericVariable(LineName);
+  LineVariable = makeNumericVariable(
+      LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
   GlobalNumericVariableTable[LineName] = LineVariable;
 }
 
@@ -1815,20 +1939,19 @@ Error FileCheckPatternContext::defineCmdlineVariables(
       // to create the necessary class instance.
       StringRef CmdlineDefExpr = CmdlineDef.substr(1);
       Optional<NumericVariable *> DefinedNumericVariable;
-      Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult =
+      Expected<std::unique_ptr<Expression>> ExpressionResult =
           Pattern::parseNumericSubstitutionBlock(
               CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
-      if (!ExpressionASTResult) {
-        Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
+      if (!ExpressionResult) {
+        Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
         continue;
       }
-      std::unique_ptr<ExpressionAST> ExpressionASTPointer =
-          std::move(*ExpressionASTResult);
+      std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
       // Now evaluate the expression whose value this variable should be set
       // to, since the expression of a command-line variable definition should
       // only use variables defined earlier on the command-line. If not, this
       // is an error and we report it.
-      Expected<uint64_t> Value = ExpressionASTPointer->eval();
+      Expected<uint64_t> Value = Expression->getAST()->eval();
       if (!Value) {
         Errs = joinErrors(std::move(Errs), Value.takeError());
         continue;
index dc07d22..a75530c 100644 (file)
@@ -30,6 +30,68 @@ namespace llvm {
 // Numeric substitution handling code.
 //===----------------------------------------------------------------------===//
 
+/// Type representing the format an expression value should be textualized into
+/// for matching. Used to represent both explicit format specifiers as well as
+/// implicit format from using numeric variables.
+struct ExpressionFormat {
+  enum class Kind {
+    /// Denote absence of format. Used for implicit format of literals and
+    /// empty expressions.
+    NoFormat,
+    /// Used when there are several conflicting implicit formats in an
+    /// expression.
+    Conflict,
+    /// Value is an unsigned integer and should be printed as a decimal number.
+    Unsigned,
+    /// Value should be printed as an uppercase hex number.
+    HexUpper,
+    /// Value should be printed as a lowercase hex number.
+    HexLower
+  };
+
+private:
+  Kind Value;
+
+public:
+  /// Evaluates a format to true if it can be used in a match.
+  explicit operator bool() const {
+    return Value != Kind::NoFormat && Value != Kind::Conflict;
+  }
+
+  /// Define format equality: formats are equal if neither is NoFormat and
+  /// their kinds are the same.
+  bool operator==(const ExpressionFormat &Other) const {
+    return Value != Kind::NoFormat && Value == Other.Value;
+  }
+
+  bool operator!=(const ExpressionFormat &other) const {
+    return !(*this == other);
+  }
+
+  bool operator==(Kind OtherValue) const { return Value == OtherValue; }
+
+  bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); }
+
+  ExpressionFormat() : Value(Kind::NoFormat){};
+  explicit ExpressionFormat(Kind Value) : Value(Value){};
+
+  /// \returns a wildcard regular expression StringRef that matches any value
+  /// in the format represented by this instance, or an error if the format is
+  /// NoFormat or Conflict.
+  Expected<StringRef> getWildcardRegex() const;
+
+  /// \returns the string representation of \p Value in the format represented
+  /// by this instance, or an error if the format is NoFormat or Conflict.
+  Expected<std::string> getMatchingString(uint64_t Value) const;
+
+  /// \returns the value corresponding to string representation \p StrVal
+  /// according to the matching format represented by this instance or an error
+  /// with diagnostic against \p SM if \p StrVal does not correspond to a valid
+  /// and representable value.
+  Expected<uint64_t> valueFromStringRepr(StringRef StrVal,
+                                         const SourceMgr &SM) const;
+};
+
 /// Base class representing the AST of a given expression.
 class ExpressionAST {
 public:
@@ -38,6 +100,13 @@ public:
   /// Evaluates and \returns the value of the expression represented by this
   /// AST or an error if evaluation fails.
   virtual Expected<uint64_t> eval() const = 0;
+
+  /// \returns either the implicit format of this AST, FormatConflict if
+  /// implicit formats of the AST's components conflict, or NoFormat if the AST
+  /// has no implicit format (e.g. AST is made up of a single literal).
+  virtual ExpressionFormat getImplicitFormat() const {
+    return ExpressionFormat();
+  }
 };
 
 /// Class representing an unsigned literal in the AST of an expression.
@@ -78,12 +147,38 @@ public:
   }
 };
 
+/// Class representing an expression and its matching format.
+class Expression {
+private:
+  /// Pointer to AST of the expression.
+  std::unique_ptr<ExpressionAST> AST;
+
+  /// Format to use (e.g. hex upper case letters) when matching the value.
+  ExpressionFormat Format;
+
+public:
+  /// Generic constructor for an expression represented by the given \p AST and
+  /// whose matching format is \p Format.
+  Expression(std::unique_ptr<ExpressionAST> AST, ExpressionFormat Format)
+      : AST(std::move(AST)), Format(Format) {}
+
+  /// \returns pointer to AST of the expression. Pointer is guaranteed to be
+  /// valid as long as this object is.
+  ExpressionAST *getAST() const { return AST.get(); }
+
+  ExpressionFormat getFormat() const { return Format; }
+};
+
 /// Class representing a numeric variable and its associated current value.
 class NumericVariable {
 private:
   /// Name of the numeric variable.
   StringRef Name;
 
+  /// Format to use for expressions using this variable without an explicit
+  /// format.
+  ExpressionFormat ImplicitFormat;
+
   /// Value of numeric variable, if defined, or None otherwise.
   Optional<uint64_t> Value;
 
@@ -93,15 +188,20 @@ private:
   Optional<size_t> DefLineNumber;
 
 public:
-  /// Constructor for a variable \p Name defined at line \p DefLineNumber or
-  /// defined before input is parsed if \p DefLineNumber is None.
-  explicit NumericVariable(StringRef Name,
+  /// Constructor for a variable \p Name with implicit format \p ImplicitFormat
+  /// defined at line \p DefLineNumber or defined before input is parsed if
+  /// \p DefLineNumber is None.
+  explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat,
                            Optional<size_t> DefLineNumber = None)
-      : Name(Name), DefLineNumber(DefLineNumber) {}
+      : Name(Name), ImplicitFormat(ImplicitFormat),
+        DefLineNumber(DefLineNumber) {}
 
   /// \returns name of this numeric variable.
   StringRef getName() const { return Name; }
 
+  /// \returns implicit format of this numeric variable.
+  ExpressionFormat getImplicitFormat() const { return ImplicitFormat; }
+
   /// \returns this variable's value.
   Optional<uint64_t> getValue() const { return Value; }
 
@@ -133,6 +233,11 @@ public:
 
   /// \returns the value of the variable referenced by this instance.
   Expected<uint64_t> eval() const override;
+
+  /// \returns implicit format of this numeric variable.
+  ExpressionFormat getImplicitFormat() const override {
+    return Variable->getImplicitFormat();
+  }
 };
 
 /// Type of functions evaluating a given binary operation.
@@ -163,6 +268,11 @@ public:
   /// \returns the expression value or an error if an undefined numeric
   /// variable is used in one of the operands.
   Expected<uint64_t> eval() const override;
+
+  /// \returns the implicit format of this AST, if any, a format conflict if
+  /// the implicit formats of the AST's components conflict, or no format if
+  /// the AST has no implicit format (e.g. AST is made of a single literal).
+  ExpressionFormat getImplicitFormat() const override;
 };
 
 class FileCheckPatternContext;
@@ -218,14 +328,14 @@ class NumericSubstitution : public Substitution {
 private:
   /// Pointer to the class representing the expression whose value is to be
   /// substituted.
-  std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+  std::unique_ptr<Expression> ExpressionPointer;
 
 public:
-  NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
-                      std::unique_ptr<ExpressionAST> ExprAST, size_t InsertIdx)
-      : Substitution(Context, Expr, InsertIdx) {
-    ExpressionASTPointer = std::move(ExprAST);
-  }
+  NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr,
+                      std::unique_ptr<Expression> ExpressionPointer,
+                      size_t InsertIdx)
+      : Substitution(Context, ExpressionStr, InsertIdx),
+        ExpressionPointer(std::move(ExpressionPointer)) {}
 
   /// \returns a string containing the result of evaluating the expression in
   /// this substitution, or an error if evaluation failed.
@@ -270,6 +380,10 @@ private:
   /// automatically free them once they are guaranteed to no longer be used.
   std::vector<std::unique_ptr<NumericVariable>> NumericVariables;
 
+  /// Vector holding pointers to all parsed expressions. Used to automatically
+  /// free the expressions once they are guaranteed to no longer be used.
+  std::vector<std::unique_ptr<Expression>> Expressions;
+
   /// Vector holding pointers to all substitutions. Used to automatically free
   /// them once they are guaranteed to no longer be used.
   std::vector<std::unique_ptr<Substitution>> Substitutions;
@@ -307,10 +421,9 @@ private:
 
   /// Makes a new numeric substitution and registers it for destruction when
   /// the context is destroyed.
-  Substitution *
-  makeNumericSubstitution(StringRef ExpressionStr,
-                          std::unique_ptr<ExpressionAST> ExpressionAST,
-                          size_t InsertIdx);
+  Substitution *makeNumericSubstitution(StringRef ExpressionStr,
+                                        std::unique_ptr<Expression> Expression,
+                                        size_t InsertIdx);
 };
 
 /// Class to represent an error holding a diagnostic with location information
@@ -388,12 +501,12 @@ class Pattern {
   std::map<StringRef, unsigned> VariableDefs;
 
   /// Structure representing the definition of a numeric variable in a pattern.
-  /// It holds the pointer to the class representing the numeric variable whose
-  /// value is being defined and the number of the parenthesis group in
-  /// RegExStr to capture that value.
+  /// It holds the pointer to the class instance holding the value and matching
+  /// format of the numeric variable whose value is being defined and the
+  /// number of the parenthesis group in RegExStr to capture that value.
   struct NumericVariableMatch {
-    /// 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.
     NumericVariable *DefinedNumericVariable;
 
     /// Number of the parenthesis group in RegExStr that captures the value of
@@ -457,12 +570,12 @@ public:
   /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
   /// expression and \p Context points to the class instance holding the live
   /// string and numeric variables. \returns a pointer to the class instance
-  /// representing the AST of the expression whose value must be substitued, or
-  /// an error holding a diagnostic against \p SM if parsing fails. If
-  /// substitution was successful, sets \p DefinedNumericVariable to point to
-  /// the class representing the numeric variable defined in this numeric
-  /// substitution block, or None if this block does not define any variable.
-  static Expected<std::unique_ptr<ExpressionAST>> parseNumericSubstitutionBlock(
+  /// representing the expression whose value must be substitued, or an error
+  /// holding a diagnostic against \p SM if parsing fails. If substitution was
+  /// successful, sets \p DefinedNumericVariable to point to the class
+  /// representing the numeric variable defined in this numeric substitution
+  /// block, or None if this block does not define any variable.
+  static Expected<std::unique_ptr<Expression>> parseNumericSubstitutionBlock(
       StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
       bool IsLegacyLineExpr, Optional<size_t> LineNumber,
       FileCheckPatternContext *Context, const SourceMgr &SM);
@@ -526,7 +639,8 @@ private:
   /// should defining such a variable be invalid.
   static Expected<NumericVariable *> parseNumericVariableDefinition(
       StringRef &Expr, FileCheckPatternContext *Context,
-      Optional<size_t> LineNumber, const SourceMgr &SM);
+      Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+      const SourceMgr &SM);
   /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use
   /// at line \p LineNumber, or before input is parsed if \p LineNumber is
   /// None. Parameter \p Context points to the class instance holding the live
@@ -536,7 +650,7 @@ private:
   static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse(
       StringRef Name, bool IsPseudo, Optional<size_t> LineNumber,
       FileCheckPatternContext *Context, const SourceMgr &SM);
-  enum class AllowedOperand { LineVar, Literal, Any };
+  enum class AllowedOperand { LineVar, LegacyLiteral, Any };
   /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
   /// before input is parsed if \p LineNumber is None. Accepts both literal
   /// values and numeric variables, depending on the value of \p AO. Parameter
index 5d885d8..0f98a2a 100644 (file)
 62 BAD12: [[#@LINE-1]] NOT HERE
 63 ERR12: note: with "@LINE-1" equal to "61"
 64
-65 CHECK: [[#@LINE]] CHECK
-66 CHECK: [[# @LINE]] CHECK
-67 CHECK: [[# @LINE ]] CHECK
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
+; RUN:   | FileCheck --check-prefix ERR13 %s
 68
-69 CHECK: [[#@LINE-1]]
-70 CHECK: [[# @LINE-1]] CHECK
-71 CHECK: [[# @LINE -1]] CHECK
-72 CHECK: [[# @LINE - 1]] CHECK
-73 CHECK: [[# @LINE - 1 ]] CHECK
+69 BAD13: [[@LINE-0xA]]
+70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
+71
+72 CHECK: [[#@LINE]] CHECK
+73 CHECK: [[# @LINE]] CHECK
+74 CHECK: [[# @LINE ]] CHECK
+75
+76 CHECK: [[#@LINE-1]]
+77 CHECK: [[# @LINE-1]] CHECK
+78 CHECK: [[# @LINE -1]] CHECK
+79 CHECK: [[# @LINE - 1]] CHECK
+80 CHECK: [[# @LINE - 1 ]] CHECK
index ae4a501..da8c9cc 100644 (file)
@@ -3,11 +3,11 @@
 ; Invalid variable name: starts with a digit.
 RUN: %ProtectFileCheckOutput \
 RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
+RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLINAME
 
-NUMERRCLIFMT:Global defines:1:46: error: invalid variable name
-NUMERRCLIFMT-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
-    NUMERRCLIFMT-NEXT:                                             ^
+NUMERRCLINAME:Global defines:1:46: error: invalid variable name
+NUMERRCLINAME-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
+    NUMERRCLINAME-NEXT:                                             ^
 
 ; Invalid definition of pseudo variable.
 RUN: %ProtectFileCheckOutput \
@@ -26,3 +26,11 @@ RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUME
 NUMERRCLITRAIL:Global defines:1:51: error: unexpected characters after numeric variable name
 NUMERRCLITRAIL-NEXT:Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
      NUMERRCLITRAIL-NEXT:                                                  ^
+
+; Invalid format for variable.
+RUN: not FileCheck -D#,VALUE=10 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
+
+NUMERRCLIFMT:Global defines:1:45: error: invalid matching format specification in expression
+NUMERRCLIFMT-NEXT:Global define #1: #,VALUE=10 (parsed as: {{\[\[#,VALUE:10\]\]}})
+    NUMERRCLIFMT-NEXT:                                            ^
index 2cc9f55..1bd56b7 100644 (file)
@@ -1,31 +1,33 @@
 ; Test functionality of -D# option: numeric variables are defined to the right
 ; value and CHECK directives using them match as expected given the value set.
 
-RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s
+; Tests with default format specifier.
+RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' --check-prefixes CHECKNUM1,CHECKNUM2 --input-file %s %s
 
 RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix CHECKNUM1 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG1
 
 RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix CHECKNUM2 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG2
 
 RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG1
 
 RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2
-RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG2
+
+RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s
 
 Numeric value #1 = 8
 Numeric value #2 = 12
 CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
 CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
-NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]]
-NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]]
+NUMNOT1-NOT: Numeric value #1 = [[#NUMVAL1]]
+NUMNOT1-NOT: Numeric value #2 = [[#NUMVAL2]]
 
 NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
 NUMERRMSG1: defines.txt:1:1: note: scanning from here
@@ -37,10 +39,61 @@ NUMERRMSG2: defines.txt:1:1: note: scanning from here
 NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8"
 NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
 
-NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
 NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here
 NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8"
 
-NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
 NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here
 NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12"
+
+; Tests with explicit format specifiers.
+RUN: FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix CHECKNUM3 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG3
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix CHECKNUM4 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG4
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG3
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
+RUN:   | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG4
+
+RUN: FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s
+
+; Test with explicit and default format specifiers.
+RUN: FileCheck -D#NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+; Test with explicit and implicit format specifiers.
+RUN: FileCheck -D#%X,NUMVAL3=8 -D#NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+
+Numeric value #3 = 8
+Numeric value #4 = C
+CHECKNUM3: Numeric value #3 = [[#NUMVAL3]]
+CHECKNUM4: Numeric value #4 = [[#NUMVAL4]]
+NUMNOT2-NOT: Numeric value #3 = [[#NUMVAL3]]
+NUMNOT2-NOT: Numeric value #4 = [[#NUMVAL4]]
+
+NUMERRMSG3: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM3: expected string not found in input
+NUMERRMSG3: defines.txt:1:1: note: scanning from here
+NUMERRMSG3: defines.txt:1:1: note: with "NUMVAL3" equal to "7"
+NUMERRMSG3: defines.txt:[[#@LINE-10]]:1: note: possible intended match here
+
+NUMERRMSG4: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM4: expected string not found in input
+NUMERRMSG4: defines.txt:1:1: note: scanning from here
+NUMERRMSG4: defines.txt:1:1: note: with "NUMVAL4" equal to "8"
+NUMERRMSG4: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
+
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-18]]:1: note: found here
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL3" equal to "8"
+
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-21]]:1: note: found here
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL4" equal to "C"
index 01cd281..14e3782 100644 (file)
@@ -2,18 +2,19 @@ RUN: FileCheck --input-file %s %s
 
 ; We use CHECK-NEXT directives to force a match on all lines with digits.
 
-; Numeric variable definition without spaces.
-DEF NO SPC
+; Numeric variable definition with default matching format without spaces.
+DEF DEFAULT FMT NO SPC
 10
-CHECK-LABEL: DEF NO SPC
+CHECK-LABEL: DEF DEFAULT FMT NO SPC
 CHECK-NEXT: [[#VAR1:]]
 
-; Numeric variable definition with different spacing.
-DEF SPC
+; Numeric variable definition with default matching format with different
+; spacing.
+DEF DEFAULT FMT SPC
 11
 11
 11
-CHECK-LABEL: DEF SPC
+CHECK-LABEL: DEF DEFAULT FMT SPC
 CHECK-NEXT: [[# VAR1a:]]
 CHECK-NEXT: [[# VAR1b :]]
 CHECK-NEXT: [[# VAR1c : ]]
@@ -24,63 +25,187 @@ REDEF NO SPC
 CHECK-LABEL: REDEF
 CHECK-NEXT: [[#VAR1:]]
 
-; Numeric expressions using variables defined on other lines without spaces.
-USE NO SPC
+; Numeric variable definition with explicit matching format.
+DEF FMT
+c
+D
+CHECK-LABEL: DEF FMT
+CHECK-NEXT: [[#%x,VAR2:]]
+CHECK-NEXT: [[#%X,VAR3:]]
+
+; Numeric variable definition with explicit matching format with different
+; spacing.
+DEF FMT SPC
+c
+CHECK-LABEL: DEF FMT SPC
+CHECK-NEXT: [[# %x , VAR2a : ]]
+
+; Numeric variable definition with unsupported matching format.
+RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC1 --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefix INVALID-FMT-SPEC-MSG1 --strict-whitespace %s
+RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC2 --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefix INVALID-FMT-SPEC-MSG2 --strict-whitespace %s
+
+DEF INVALID FMT
+INVVAR1=a
+INVVAR2=11
+ERR-LABEL: DEF INVALID FMT
+INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]]
+INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]]
+INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression
+INVALID-FMT-SPEC-MSG1-NEXT: {{I}}NVALID-FMT-SPEC1-NEXT: INVVAR1={{\[\[#%c,INVVAR1:\]\]}}
+INVALID-FMT-SPEC-MSG1-NEXT:    {{^}}                                    ^{{$}}
+INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression
+INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}}
+INVALID-FMT-SPEC-MSG2-NEXT:    {{^}}                                    ^{{$}}
+
+; Numeric expressions in explicit matching format and default matching rule using
+; variables defined on other lines without spaces.
+USE DEF FMT IMPL MATCH
 11
 12
 10
+c
+d
+b
+1a
+D
+E
+C
+1B
 11
 11
 11
-CHECK-LABEL: USE
-CHECK-NEXT: [[#VAR1]]
-CHECK-NEXT: [[#VAR1+1]]
-CHECK-NEXT: [[#VAR1-1]]
-CHECK-NEXT: [[#VAR1a]]
-CHECK-NEXT: [[#VAR1b]]
-CHECK-NEXT: [[#VAR1c]]
+c
+c
+c
+c
+c
+CHECK-LABEL: USE DEF FMT IMPL MATCH
+CHECK-NEXT: [[#%u,VAR1]]
+CHECK-NEXT: [[#%u,VAR1+1]]
+CHECK-NEXT: [[#%u,VAR1-1]]
+CHECK-NEXT: [[#%x,VAR2]]
+CHECK-NEXT: [[#%x,VAR2+1]]
+CHECK-NEXT: [[#%x,VAR2-1]]
+CHECK-NEXT: [[#%x,VAR2+14]]
+CHECK-NEXT: [[#%X,VAR3]]
+CHECK-NEXT: [[#%X,VAR3+1]]
+CHECK-NEXT: [[#%X,VAR3-1]]
+CHECK-NEXT: [[#%X,VAR3+14]]
+CHECK-NEXT: [[#%u,VAR1a]]
+CHECK-NEXT: [[#%u,VAR1b]]
+CHECK-NEXT: [[#%u,VAR1c]]
+CHECK-NEXT: [[#%x,VAR2a]]
 
-; Numeric expressions using variables defined on other lines with different
-; spacing.
-USE SPC
+; Numeric expressions in explicit matching format and default matching rule using
+; variables defined on other lines with different spacing.
+USE EXPL FMT IMPL MATCH SPC
 11
 11
+11
+12
 12
 12
 12
 12
+12
+10
+10
 10
 10
 10
 10
-CHECK-LABEL: USE SPC
-CHECK-NEXT: [[# VAR1]]
-CHECK-NEXT: [[# VAR1 ]]
-CHECK-NEXT: [[# VAR1+1]]
-CHECK-NEXT: [[# VAR1 +1]]
-CHECK-NEXT: [[# VAR1 + 1]]
-CHECK-NEXT: [[# VAR1 + 1 ]]
-CHECK-NEXT: [[# VAR1-1]]
-CHECK-NEXT: [[# VAR1 -1]]
-CHECK-NEXT: [[# VAR1 - 1]]
-CHECK-NEXT: [[# VAR1 - 1 ]]
+CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
+CHECK-NEXT: [[#%u, VAR1]]
+CHECK-NEXT: [[# %u, VAR1]]
+CHECK-NEXT: [[# %u, VAR1 ]]
+CHECK-NEXT: [[#%u, VAR1+1]]
+CHECK-NEXT: [[# %u, VAR1+1]]
+CHECK-NEXT: [[# %u , VAR1+1]]
+CHECK-NEXT: [[# %u , VAR1 +1]]
+CHECK-NEXT: [[# %u , VAR1 + 1]]
+CHECK-NEXT: [[# %u , VAR1 + 1 ]]
+CHECK-NEXT: [[#%u, VAR1-1]]
+CHECK-NEXT: [[# %u, VAR1-1]]
+CHECK-NEXT: [[# %u , VAR1-1]]
+CHECK-NEXT: [[# %u , VAR1 -1]]
+CHECK-NEXT: [[# %u , VAR1 - 1]]
+CHECK-NEXT: [[# %u , VAR1 - 1 ]]
+
+; Numeric expressions in implicit matching format and default matching rule using
+; variables defined on other lines.
+USE IMPL FMT IMPL MATCH
+11
+12
+10
+c
+d
+b
+1a
+D
+E
+C
+1B
+CHECK-LABEL: USE IMPL FMT IMPL MATCH
+CHECK-NEXT: [[#VAR1]]
+CHECK-NEXT: [[#VAR1+1]]
+CHECK-NEXT: [[#VAR1-1]]
+CHECK-NEXT: [[#VAR2]]
+CHECK-NEXT: [[#VAR2+1]]
+CHECK-NEXT: [[#VAR2-1]]
+CHECK-NEXT: [[#VAR2+14]]
+CHECK-NEXT: [[#VAR3]]
+CHECK-NEXT: [[#VAR3+1]]
+CHECK-NEXT: [[#VAR3-1]]
+CHECK-NEXT: [[#VAR3+14]]
 
 ; Numeric expressions using variables defined on other lines and an immediate
 ; interpreted as an unsigned value.
 ; Note: 9223372036854775819 = 0x8000000000000000 + 11
-;       9223372036854775808 = 0x8000000000000000
-USE UNSIGNED IMM
+USE IMPL FMT IMPL MATCH UNSIGNED IMM
 9223372036854775819
-CHECK-LABEL: USE UNSIGNED IMM
-CHECK-NEXT: [[#VAR1+9223372036854775808]]
+CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
+CHECK-NEXT: [[#VAR1+0x8000000000000000]]
+
+; Numeric expressions with matching format overriding the implicit format of
+; variables defined on other lines.
+USE CONV FMT IMPL MATCH
+b
+B
+12
+13
+CHECK-LABEL: USE CONV FMT IMPL MATCH
+CHECK-NEXT: [[# %x, VAR1]]
+CHECK-NEXT: [[# %X, VAR1]]
+CHECK-NEXT: [[# %u, VAR2]]
+CHECK-NEXT: [[# %u, VAR3]]
+
+; Conflicting implicit format.
+RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s
+
+VAR USE IMPL FMT CONFLICT
+23
+FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT
+FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]]
+FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one
+FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
+FMT-CONFLICT-MSG-NEXT: {{^                      \^$}}
+
+; Explicitly specified format can override conflicting implicit formats.
+VAR USE IMPL OVERRIDE FMT CONFLICT
+23
+CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT
+CHECK-NEXT: [[# %u, VAR1 + VAR2]]
 
 ; Numeric expressions using more than one variable defined on other lines.
 USE MULTI VAR
 31
 42
 CHECK-LABEL: USE MULTI VAR
-CHECK-NEXT: [[#VAR2:]]
-CHECK-NEXT: [[#VAR1+VAR2]]
+CHECK-NEXT: [[#VAR4:]]
+CHECK-NEXT: [[#VAR1+VAR4]]
 
 ; Numeric expression using a variable defined from a numeric expression.
 DEF EXPR GOOD MATCH
@@ -93,8 +218,8 @@ DEF EXPR GOOD MATCH
 ; Empty numeric expression.
 EMPTY NUM EXPR
 foo 104 bar
-CHECK-LABEL: EMPTY NUM EXPR
-CHECK-NEXT: foo [[#]] bar
+CHECK-LABEL: EMPTY NUM EXPR
+CHECK-NEXT: foo [[#]] bar
 
 ; Numeric expression using undefined variables.
 RUN: %ProtectFileCheckOutput \
@@ -187,7 +312,7 @@ BIG VALUE
 NUMVAR: 10000000000000000000000
 BIGVAL-LABEL: BIG VALUE
 BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
-BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value
+BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value
  BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
 BIGVAL-MSG-NEXT: {{^}}        ^{{$}}
 
@@ -232,3 +357,17 @@ SAME-LINE-USE2-NEXT: [[#VAR2:VAR1+1]] [[#VAR2+1]]
 SAME-LINE-USE-MSG2: numeric-expression.txt:[[#@LINE-1]]:42: error: numeric variable 'VAR2' defined earlier in the same CHECK directive
 SAME-LINE-USE-MSG2-NEXT: {{S}}AME-LINE-USE2-NEXT: {{\[\[#VAR2:VAR1\+1\]\] \[\[#VAR2\+1\]\]}}
 SAME-LINE-USE-MSG2-NEXT:        {{^}}                                         ^{{$}}
+
+; Invalid change of format in variable redefinition.
+RUN: not FileCheck --check-prefix REDEF-NEW-FMT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --strict-whitespace --check-prefix REDEF-NEW-FMT-MSG %s
+
+VAR REDEF FMT CHANGE
+22
+DC
+REDEF-NEW-FMT-LABEL: VAR REDEF FMT CHANGE
+REDEF-NEW-FMT-NEXT: [[#VAR1:]]
+REDEF-NEW-FMT-NEXT: [[#%X,VAR1:]]
+REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition
+REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,VAR1:\]\]}}
+REDEF-NEW-FMT-MSG-NEXT: {{^                              \^$}}
index b975c38..2c88b66 100644 (file)
@@ -27,11 +27,11 @@ ERRCLIVAR2: Missing variable name in command-line definition '-D='
 ; Invalid variable name: starts with a digit.
 RUN: %ProtectFileCheckOutput \
 RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
-RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLIFMT
+RUN:   | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLINAME
 
-ERRCLIFMT:Global defines:1:19: error: invalid variable name
-ERRCLIFMT-NEXT:Global define #1: 10VALUE=10
-ERRCLIFMT-NEXT:                  ^
+ERRCLINAME:Global defines:1:19: error: invalid variable name
+ERRCLINAME-NEXT:Global define #1: 10VALUE=10
+ERRCLINAME-NEXT:                  ^
 
 ; Invalid definition of pseudo variable.
 RUN: %ProtectFileCheckOutput \
index ebe2362..a329d8c 100644 (file)
@@ -8,8 +8,10 @@
 
 #include "llvm/Support/FileCheck.h"
 #include "../lib/Support/FileCheckImpl.h"
+#include "llvm/Support/Regex.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
+#include <tuple>
 #include <unordered_set>
 
 using namespace llvm;
@@ -18,12 +20,216 @@ namespace {
 
 class FileCheckTest : public ::testing::Test {};
 
+static StringRef bufferize(SourceMgr &SM, StringRef Str) {
+  std::unique_ptr<MemoryBuffer> Buffer =
+      MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
+  StringRef StrBufferRef = Buffer->getBuffer();
+  SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+  return StrBufferRef;
+}
+
+template <typename ErrorT>
+static void expectError(StringRef ExpectedMsg, Error Err) {
+  bool ErrorHandled = false;
+  EXPECT_THAT_ERROR(handleErrors(std::move(Err),
+                                 [&](const ErrorT &E) {
+                                   EXPECT_NE(
+                                       E.message().find(ExpectedMsg.str()),
+                                       std::string::npos);
+                                   ErrorHandled = true;
+                                 }),
+                    Succeeded());
+  EXPECT_TRUE(ErrorHandled);
+}
+
+static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
+  expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
+}
+
+struct ExpressionFormatParameterisedFixture
+    : public ::testing::TestWithParam<
+          std::tuple<ExpressionFormat::Kind, bool, bool>> {
+  void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); }
+
+  ExpressionFormat::Kind Kind;
+  bool AllowHex;
+  bool AllowUpperHex;
+};
+
+TEST_P(ExpressionFormatParameterisedFixture, Format) {
+  SourceMgr SM;
+  ExpressionFormat Format(Kind);
+
+  Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
+  ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
+  Regex WildcardRegex(*WildcardPattern);
+  ASSERT_TRUE(WildcardRegex.isValid());
+  // Does not match empty string.
+  EXPECT_FALSE(WildcardRegex.match(""));
+  // Matches all decimal digits and matches several of them.
+  SmallVector<StringRef, 4> Matches;
+  StringRef DecimalDigits = "0123456789";
+  ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
+  EXPECT_EQ(Matches[0], DecimalDigits);
+  // Check non digits or digits with wrong casing are not matched.
+  if (AllowHex) {
+    StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
+    StringRef AcceptedHexOnlyDigits =
+        AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0];
+    StringRef RefusedHexOnlyDigits =
+        AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1];
+    ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches));
+    EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits);
+    EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits));
+
+    EXPECT_FALSE(WildcardRegex.match("g"));
+    EXPECT_FALSE(WildcardRegex.match("G"));
+  } else {
+    EXPECT_FALSE(WildcardRegex.match("a"));
+    EXPECT_FALSE(WildcardRegex.match("A"));
+  }
+
+  Expected<std::string> MatchingString = Format.getMatchingString(0U);
+  ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
+  EXPECT_EQ(*MatchingString, "0");
+  MatchingString = Format.getMatchingString(9U);
+  ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
+  EXPECT_EQ(*MatchingString, "9");
+  Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
+  ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
+  Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
+  ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
+  StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
+  if (AllowHex) {
+    if (AllowUpperHex) {
+      ExpectedTenMatchingString = "A";
+      ExpectedFifteenMatchingString = "F";
+    } else {
+      ExpectedTenMatchingString = "a";
+      ExpectedFifteenMatchingString = "f";
+    }
+  } else {
+    ExpectedTenMatchingString = "10";
+    ExpectedFifteenMatchingString = "15";
+  }
+  EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
+  EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
+
+  StringRef BufferizedValidValueStr = bufferize(SM, "0");
+  Expected<uint64_t> Val =
+      Format.valueFromStringRepr(BufferizedValidValueStr, SM);
+  ASSERT_THAT_EXPECTED(Val, Succeeded());
+  EXPECT_EQ(*Val, 0U);
+  BufferizedValidValueStr = bufferize(SM, "9");
+  Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
+  ASSERT_THAT_EXPECTED(Val, Succeeded());
+  EXPECT_EQ(*Val, 9U);
+  StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
+  StringRef TenStr, FifteenStr, InvalidTenStr;
+  if (AllowHex) {
+    if (AllowUpperHex) {
+      TenStr = "A";
+      FifteenStr = "F";
+      InvalidTenStr = "a";
+    } else {
+      TenStr = "a";
+      FifteenStr = "f";
+      InvalidTenStr = "A";
+    }
+  } else {
+    TenStr = "10";
+    FifteenStr = "15";
+    InvalidTenStr = "A";
+  }
+  BufferizedTenStr = bufferize(SM, TenStr);
+  Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
+  ASSERT_THAT_EXPECTED(Val, Succeeded());
+  EXPECT_EQ(*Val, 10U);
+  BufferizedFifteenStr = bufferize(SM, FifteenStr);
+  Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
+  ASSERT_THAT_EXPECTED(Val, Succeeded());
+  EXPECT_EQ(*Val, 15U);
+  // Wrong casing is not tested because valueFromStringRepr() relies on
+  // StringRef's getAsInteger() which does not allow to restrict casing.
+  BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
+  expectDiagnosticError(
+      "unable to represent numeric value",
+      Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
+
+  // Check boolean operator.
+  EXPECT_TRUE(bool(Format));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
+    ::testing::Values(
+        std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
+                        /*AllowUpperHex=*/false),
+        std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
+                        /*AllowUpperHex=*/false),
+        std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
+                        /*AllowUpperHex=*/true)), );
+
+TEST_F(FileCheckTest, NoFormatProperties) {
+  ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+  expectError<StringError>("trying to match value with invalid format",
+                           NoFormat.getWildcardRegex().takeError());
+  expectError<StringError>("trying to match value with invalid format",
+                           NoFormat.getMatchingString(18).takeError());
+  EXPECT_FALSE(bool(NoFormat));
+}
+
+TEST_F(FileCheckTest, ConflictFormatProperties) {
+  ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+  expectError<StringError>("trying to match value with invalid format",
+                           ConflictFormat.getWildcardRegex().takeError());
+  expectError<StringError>("trying to match value with invalid format",
+                           ConflictFormat.getMatchingString(18).takeError());
+  EXPECT_FALSE(bool(ConflictFormat));
+}
+
+TEST_F(FileCheckTest, FormatEqualityOperators) {
+  ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+  ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
+  EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);
+  EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);
+
+  ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);
+  EXPECT_FALSE(UnsignedFormat == HexLowerFormat);
+  EXPECT_TRUE(UnsignedFormat != HexLowerFormat);
+
+  ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+  ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
+  EXPECT_FALSE(NoFormat == NoFormat2);
+  EXPECT_TRUE(NoFormat != NoFormat2);
+
+  ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+  ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict);
+  EXPECT_TRUE(ConflictFormat == ConflictFormat2);
+  EXPECT_FALSE(ConflictFormat != ConflictFormat2);
+}
+
+TEST_F(FileCheckTest, FormatKindEqualityOperators) {
+  ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+  EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);
+  EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
+  EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
+  EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower);
+  ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+  EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict);
+  EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict);
+  ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+  EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
+  EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
+}
+
 TEST_F(FileCheckTest, Literal) {
   // Eval returns the literal's value.
   ExpressionLiteral Ten(10);
   Expected<uint64_t> Value = Ten.eval();
   ASSERT_THAT_EXPECTED(Value, Succeeded());
   EXPECT_EQ(10U, *Value);
+  EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
 
   // Max value can be correctly represented.
   ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
@@ -43,6 +249,16 @@ static std::string toString(const std::unordered_set<std::string> &Set) {
   return Str;
 }
 
+TEST_F(FileCheckTest, Expression) {
+  std::unique_ptr<ExpressionLiteral> Ten =
+      std::make_unique<ExpressionLiteral>(10);
+  ExpressionLiteral *TenPtr = Ten.get();
+  Expression Expr(std::move(Ten),
+                  ExpressionFormat(ExpressionFormat::Kind::HexLower));
+  EXPECT_EQ(Expr.getAST(), TenPtr);
+  EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);
+}
+
 static void
 expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
                   Error Err) {
@@ -60,9 +276,12 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
 TEST_F(FileCheckTest, NumericVariable) {
   // Undefined variable: getValue and eval fail, error returned by eval holds
   // the name of the undefined variable.
-  NumericVariable FooVar("FOO", 1);
+  NumericVariable FooVar("FOO",
+                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
   EXPECT_EQ("FOO", FooVar.getName());
+  EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
   NumericVariableUse FooVarUse("FOO", &FooVar);
+  EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
   EXPECT_FALSE(FooVar.getValue());
   Expected<uint64_t> EvalResult = FooVarUse.eval();
   expectUndefErrors({"FOO"}, EvalResult.takeError());
@@ -86,20 +305,24 @@ TEST_F(FileCheckTest, NumericVariable) {
 }
 
 TEST_F(FileCheckTest, Binop) {
-  NumericVariable FooVar("FOO", 1);
+  NumericVariable FooVar("FOO",
+                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
   FooVar.setValue(42);
   std::unique_ptr<NumericVariableUse> FooVarUse =
       std::make_unique<NumericVariableUse>("FOO", &FooVar);
-  NumericVariable BarVar("BAR", 2);
+  NumericVariable BarVar("BAR",
+                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
   BarVar.setValue(18);
   std::unique_ptr<NumericVariableUse> BarVarUse =
       std::make_unique<NumericVariableUse>("BAR", &BarVar);
   BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
 
-  // Defined variable: eval returns right value.
+  // Defined variables: eval returns right value; implicit format is as
+  // expected.
   Expected<uint64_t> Value = Binop.eval();
   ASSERT_THAT_EXPECTED(Value, Succeeded());
   EXPECT_EQ(60U, *Value);
+  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 
   // 1 undefined variable: eval fails, error contains name of undefined
   // variable.
@@ -112,6 +335,26 @@ TEST_F(FileCheckTest, Binop) {
   BarVar.clearValue();
   Value = Binop.eval();
   expectUndefErrors({"FOO", "BAR"}, Value.takeError());
+
+  // Literal + Variable has format of variable.
+  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
+  std::unique_ptr<ExpressionLiteral> Eighteen =
+      std::make_unique<ExpressionLiteral>(18);
+  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
+  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
+  Eighteen = std::make_unique<ExpressionLiteral>(18);
+  Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
+  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+
+  // Variables with different implicit format conflict.
+  NumericVariable BazVar("BAZ",
+                         ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
+  FooVarUse = std::make_unique<NumericVariableUse>("BAZ", &FooVar);
+  std::unique_ptr<NumericVariableUse> BazVarUse =
+      std::make_unique<NumericVariableUse>("BAZ", &BazVar);
+  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
+  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
 }
 
 TEST_F(FileCheckTest, ValidVarNameStart) {
@@ -126,32 +369,6 @@ TEST_F(FileCheckTest, ValidVarNameStart) {
   EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
 }
 
-static StringRef bufferize(SourceMgr &SM, StringRef Str) {
-  std::unique_ptr<MemoryBuffer> Buffer =
-      MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
-  StringRef StrBufferRef = Buffer->getBuffer();
-  SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
-  return StrBufferRef;
-}
-
-template <typename ErrorT>
-static void expectError(StringRef ExpectedMsg, Error Err) {
-  bool ErrorHandled = false;
-  EXPECT_THAT_ERROR(handleErrors(std::move(Err),
-                                 [&](const ErrorT &E) {
-                                   EXPECT_NE(
-                                       E.message().find(ExpectedMsg.str()),
-                                       std::string::npos);
-                                   ErrorHandled = true;
-                                 }),
-                    Succeeded());
-  EXPECT_TRUE(ErrorHandled);
-}
-
-static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
-  expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
-}
-
 TEST_F(FileCheckTest, ParseVar) {
   SourceMgr SM;
   StringRef OrigVarName = bufferize(SM, "GoodVar42");
@@ -257,7 +474,7 @@ public:
 
   size_t getLineNumber() const { return LineNumber; }
 
-  Expected<std::unique_ptr<ExpressionAST>>
+  Expected<std::unique_ptr<Expression>>
   parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
     StringRef ExprBufferRef = bufferize(SM, Expr);
     Optional<NumericVariable *> DefinedNumericVariable;
@@ -295,13 +512,36 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   expectDiagnosticError("unexpected characters after numeric variable name",
                         Tester.parseSubst("VAR GARBAGE:").takeError());
 
+  // Change of format.
+  expectDiagnosticError("format different from previous variable definition",
+                        Tester.parseSubst("%X,FOO:").takeError());
+
+  // Invalid format.
+  expectDiagnosticError("invalid matching format specification in expression",
+                        Tester.parseSubst("X,VAR1:").takeError());
+  expectDiagnosticError("invalid format specifier in expression",
+                        Tester.parseSubst("%F,VAR1:").takeError());
+  expectDiagnosticError("invalid matching format specification in expression",
+                        Tester.parseSubst("%X a,VAR1:").takeError());
+
+  // Acceptable variable definition.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());
   EXPECT_THAT_EXPECTED(Tester.parseSubst("  VAR2:"), Succeeded());
   EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3  :"), Succeeded());
   EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3:  "), Succeeded());
+
+  // Acceptable variable definition with format specifier. Use parsePattern for
+  // variables whose definition needs to be visible for later checks.
+  EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));
+  EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
+
+  // Acceptable variable definition from a numeric expression.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
 
-  // Numeric expression.
+  // Numeric expression. Switch to next line to make above valid definition
+  // available in expressions.
+  Tester.initNextPattern();
 
   // Invalid variable name.
   expectDiagnosticError("invalid operand format '%VAR'",
@@ -342,8 +582,17 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   // Valid single operand expression.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
 
+  // Invalid format.
+  expectDiagnosticError("invalid matching format specification in expression",
+                        Tester.parseSubst("X,FOO:").takeError());
+  expectDiagnosticError("invalid format specifier in expression",
+                        Tester.parseSubst("%F,FOO").takeError());
+  expectDiagnosticError("invalid matching format specification in expression",
+                        Tester.parseSubst("%X a,FOO").takeError());
+
   // Valid expression with 2 or more operands.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());
   EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
 
   expectDiagnosticError("unsupported operation '/'",
@@ -367,6 +616,16 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
       "invalid variable name",
       Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());
 
+  // Invalid hex literal in legacy @LINE expression.
+  expectDiagnosticError(
+      "unexpected characters at end of expression 'xC'",
+      Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());
+
+  // Valid expression with format specifier.
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
+
   // Valid legacy @LINE expression.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
                        Succeeded());
@@ -378,6 +637,18 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   expectDiagnosticError(
       "unexpected characters at end of expression '+2'",
       Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());
+
+  // Valid expression with several variables when their implicit formats do not
+  // conflict.
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());
+
+  // Valid implicit format conflict in presence of explicit formats.
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());
+
+  // Implicit format conflict.
+  expectDiagnosticError(
+      "variables with conflicting format specifier: need an explicit one",
+      Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
 }
 
 TEST_F(FileCheckTest, ParsePattern) {
@@ -407,6 +678,12 @@ TEST_F(FileCheckTest, ParsePattern) {
 
   // Valid numeric substitution.
   EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
+
+  // Valid legacy @LINE expression.
+  EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
+
+  // Invalid legacy @LINE expression with non decimal literal.
+  EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
 }
 
 TEST_F(FileCheckTest, Match) {
@@ -417,12 +694,44 @@ TEST_F(FileCheckTest, Match) {
   expectNotFoundError(Tester.match("FAIL").takeError());
   EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
 
-  // Check matching a definition only matches a number.
+  // Check matching a definition only matches a number with the right format.
   Tester.initNextPattern();
   ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
   expectNotFoundError(Tester.match("FAIL").takeError());
   expectNotFoundError(Tester.match("").takeError());
   EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
+  expectNotFoundError(Tester.match("C").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
+  expectNotFoundError(Tester.match("g").takeError());
+  expectNotFoundError(Tester.match("C").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
+  expectNotFoundError(Tester.match("H").takeError());
+  expectNotFoundError(Tester.match("b").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
+
+  // Check matching expressions with no explicit format matches the values in
+  // the right format.
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
+  expectNotFoundError(Tester.match("f").takeError());
+  expectNotFoundError(Tester.match("F").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
+  expectNotFoundError(Tester.match("13").takeError());
+  expectNotFoundError(Tester.match("D").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
+  Tester.initNextPattern();
+  Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
+  expectNotFoundError(Tester.match("12").takeError());
+  expectNotFoundError(Tester.match("c").takeError());
+  EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
 
   // Check matching an undefined variable returns a NotFound error.
   Tester.initNextPattern();
@@ -441,7 +750,7 @@ TEST_F(FileCheckTest, Match) {
   expectNotFoundError(Tester.match("18 21").takeError());
   EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
 
-  // Check matching a numeric expression using @LINE after match failure uses
+  // Check matching a numeric expression using @LINE after match failure uses
   // the correct value for @LINE.
   Tester.initNextPattern();
   ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
@@ -476,14 +785,17 @@ TEST_F(FileCheckTest, Substitution) {
 
   // Numeric substitution blocks constituted of defined numeric variables are
   // substituted for the variable's value.
-  NumericVariable NVar("N", 1);
+  NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
+                       1);
   NVar.setValue(10);
   auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
-  NumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse),
+  auto ExpressionN = std::make_unique<Expression>(
+      std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
+  NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
                                     /*InsertIdx=*/30);
   SubstValue = SubstitutionN.getResult();
   ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
-  EXPECT_EQ("10", *SubstValue);
+  EXPECT_EQ("A", *SubstValue);
 
   // Substitution of an undefined numeric variable fails, error holds name of
   // undefined variable.
@@ -565,7 +877,8 @@ TEST_F(FileCheckTest, FileCheckContext) {
   GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
   GlobalDefines.emplace_back(std::string("EmptyVar="));
   GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
-  GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
+  GlobalDefines.emplace_back(std::string("#%x,LocalNumVar2=LocalNumVar1+2"));
+  GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc"));
   ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
 
   // Create @LINE pseudo numeric variable and check it is present by matching
@@ -588,12 +901,13 @@ TEST_F(FileCheckTest, FileCheckContext) {
   StringRef LocalVarStr = "LocalVar";
   StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
   StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
+  StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
   StringRef EmptyVarStr = "EmptyVar";
   StringRef UnknownVarStr = "UnknownVar";
   Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
   P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
   Optional<NumericVariable *> DefinedNumericVariable;
-  Expected<std::unique_ptr<ExpressionAST>> ExpressionASTPointer =
+  Expected<std::unique_ptr<Expression>> ExpressionPointer =
       P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
                                       /*IsLegacyLineExpr=*/false, LineNumber,
                                       &Cxt, SM);
@@ -601,17 +915,25 @@ TEST_F(FileCheckTest, FileCheckContext) {
   EXPECT_EQ(*LocalVar, "FOO");
   Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  Expected<uint64_t> ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
   EXPECT_EQ(*ExpressionVal, 18U);
-  ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+  ExpressionPointer = P.parseNumericSubstitutionBlock(
       LocalNumVar2Ref, DefinedNumericVariable,
       /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
   EXPECT_EQ(*ExpressionVal, 20U);
+  ExpressionPointer =
+      P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,
+                                      /*IsLegacyLineExpr=*/false,
+                                      LineNumber, &Cxt, SM);
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+  EXPECT_EQ(*ExpressionVal, 12U);
   ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
   EXPECT_EQ(*EmptyVar, "");
   expectUndefErrors({UnknownVarStr}, UnknownVar.takeError());
@@ -624,20 +946,20 @@ TEST_F(FileCheckTest, FileCheckContext) {
   // local variables, if it was created before. This is important because local
   // variable clearing due to --enable-var-scope happens after numeric
   // expressions are linked to the numeric variables they use.
-  expectUndefErrors({"LocalNumVar2"},
-                    (*ExpressionASTPointer)->eval().takeError());
+  expectUndefErrors({"LocalNumVar3"},
+                    (*ExpressionPointer)->getAST()->eval().takeError());
   P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
-  ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+  ExpressionPointer = P.parseNumericSubstitutionBlock(
       LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
       LineNumber, &Cxt, SM);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
-  ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+  ExpressionPointer = P.parseNumericSubstitutionBlock(
       LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
       LineNumber, &Cxt, SM);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
   EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());
@@ -655,11 +977,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
   ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
   EXPECT_EQ(*GlobalVar, "BAR");
   P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
-  ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+  ExpressionPointer = P.parseNumericSubstitutionBlock(
       GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
       LineNumber, &Cxt, SM);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
   EXPECT_EQ(*ExpressionVal, 36U);
 
@@ -667,11 +989,11 @@ TEST_F(FileCheckTest, FileCheckContext) {
   Cxt.clearLocalVars();
   EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
   P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
-  ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+  ExpressionPointer = P.parseNumericSubstitutionBlock(
       GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
       LineNumber, &Cxt, SM);
-  ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
-  ExpressionVal = (*ExpressionASTPointer)->eval();
+  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
   ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
   EXPECT_EQ(*ExpressionVal, 36U);
 }