[llvm] r366001 - FileCheck [7/12]: Arbitrary long numeric expressions

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 13 06:24:31 PDT 2019


Author: thopre
Date: Sat Jul 13 06:24:30 2019
New Revision: 366001

URL: http://llvm.org/viewvc/llvm-project?rev=366001&view=rev
Log:
FileCheck [7/12]: Arbitrary long numeric expressions

Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch extend numeric expression to
support an arbitrary number of operands, either variable or literals.

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, rnk

Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

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

Modified:
    llvm/trunk/docs/CommandGuide/FileCheck.rst
    llvm/trunk/include/llvm/Support/FileCheck.h
    llvm/trunk/lib/Support/FileCheck.cpp
    llvm/trunk/test/FileCheck/line-count.txt
    llvm/trunk/test/FileCheck/numeric-expression.txt
    llvm/trunk/test/FileCheck/var-scope.txt
    llvm/trunk/unittests/Support/FileCheckTest.cpp

Modified: llvm/trunk/docs/CommandGuide/FileCheck.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Sat Jul 13 06:24:30 2019
@@ -107,10 +107,12 @@ and from the command line.
   Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
   used in ``CHECK:`` lines.
 
-.. option:: -D#<NUMVAR>=<VALUE>
+.. option:: -D#<NUMVAR>=<VALUE EXPRESSION>
 
-  Sets a filecheck numeric variable ``NUMVAR`` to ``<VALUE>`` that can be used
-  in ``CHECK:`` lines.
+  Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
+  ``<VALUE EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
+  ``FileCheck Numeric Variables and Expressions`` for details on the format
+  and meaning of ``<VALUE EXPRESSION>``.
 
 .. option:: -version
 
@@ -590,18 +592,15 @@ For example:
 
 would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
 
-The syntax of a numeric substitution is ``[[#<NUMVAR><op><offset>]]`` where:
+The syntax of a numeric substitution is ``[[#<expr>]]`` where ``<expr>`` is an
+expression. An expression is recursively defined as:
 
-* ``<NUMVAR>`` is the name of a defined numeric variable.
+* a numeric operand, or
+* an expression followed by an operator and a numeric operand.
 
-* ``<op>`` is an optional operation to perform on the value of ``<NUMVAR>``.
-  Currently supported operations are ``+`` and ``-``.
-
-* ``<offset>`` is the immediate value that constitutes the second operand of
-  the operation ``<op>``. It must be present if ``<op>`` is present, absent
-  otherwise.
-
-Spaces are accepted before, after and between any of these elements.
+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:
 

Modified: llvm/trunk/include/llvm/Support/FileCheck.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)
+++ llvm/trunk/include/llvm/Support/FileCheck.h Sat Jul 13 06:24:30 2019
@@ -40,6 +40,54 @@ struct FileCheckRequest {
 // Numeric substitution handling code.
 //===----------------------------------------------------------------------===//
 
+/// Base class representing the AST of a given expression.
+class FileCheckExpressionAST {
+public:
+  virtual ~FileCheckExpressionAST() = default;
+
+  /// 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;
+};
+
+/// Class representing an unsigned literal in the AST of an expression.
+class FileCheckExpressionLiteral : public FileCheckExpressionAST {
+private:
+  /// Actual value of the literal.
+  uint64_t Value;
+
+public:
+  /// Constructs a literal with the specified value.
+  FileCheckExpressionLiteral(uint64_t Val) : Value(Val) {}
+
+  /// \returns the literal's value.
+  Expected<uint64_t> eval() const { return Value; }
+};
+
+/// Class to represent an undefined variable error, which quotes that
+/// variable's name when printed.
+class FileCheckUndefVarError : public ErrorInfo<FileCheckUndefVarError> {
+private:
+  StringRef VarName;
+
+public:
+  static char ID;
+
+  FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {}
+
+  StringRef getVarName() const { return VarName; }
+
+  std::error_code convertToErrorCode() const override {
+    return inconvertibleErrorCode();
+  }
+
+  /// Print name of variable associated with this error.
+  void log(raw_ostream &OS) const override {
+    OS << "\"";
+    OS.write_escaped(VarName) << "\"";
+  }
+};
+
 /// Class representing a numeric variable and its associated current value.
 class FileCheckNumericVariable {
 private:
@@ -81,56 +129,53 @@ public:
   size_t getDefLineNumber() { return DefLineNumber; }
 };
 
-/// Type of functions evaluating a given binary operation.
-using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
-
-/// Class to represent an undefined variable error which prints that variable's
-/// name between quotes when printed.
-class FileCheckUndefVarError : public ErrorInfo<FileCheckUndefVarError> {
+/// Class representing the use of a numeric variable in the AST of an
+/// expression.
+class FileCheckNumericVariableUse : public FileCheckExpressionAST {
 private:
-  StringRef VarName;
-
-public:
-  static char ID;
-
-  FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {}
+  /// Name of the numeric variable.
+  StringRef Name;
 
-  StringRef getVarName() const { return VarName; }
+  /// Pointer to the class instance for the variable this use is about.
+  FileCheckNumericVariable *NumericVariable;
 
-  std::error_code convertToErrorCode() const override {
-    return inconvertibleErrorCode();
-  }
+public:
+  FileCheckNumericVariableUse(StringRef Name,
+                              FileCheckNumericVariable *NumericVariable)
+      : Name(Name), NumericVariable(NumericVariable) {}
 
-  /// Print name of variable associated with this error.
-  void log(raw_ostream &OS) const override {
-    OS << "\"";
-    OS.write_escaped(VarName) << "\"";
-  }
+  /// \returns the value of the variable referenced by this instance.
+  Expected<uint64_t> eval() const;
 };
 
-/// Class representing an expression consisting of either a single numeric
-/// variable or a binary operation between a numeric variable and an
-/// immediate.
-class FileCheckExpression {
+/// Type of functions evaluating a given binary operation.
+using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
+
+/// Class representing a single binary operation in the AST of an expression.
+class FileCheckASTBinop : public FileCheckExpressionAST {
 private:
   /// Left operand.
-  FileCheckNumericVariable *LeftOp;
+  std::unique_ptr<FileCheckExpressionAST> LeftOperand;
 
   /// Right operand.
-  uint64_t RightOp;
+  std::unique_ptr<FileCheckExpressionAST> RightOperand;
 
   /// Pointer to function that can evaluate this binary operation.
   binop_eval_t EvalBinop;
 
 public:
-  FileCheckExpression(binop_eval_t EvalBinop,
-                      FileCheckNumericVariable *OperandLeft,
-                      uint64_t OperandRight)
-      : LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {}
-
-  /// Evaluates the value of this expression, using EvalBinop to perform the
-  /// binary operation it consists of. \returns an error if the numeric
-  /// variable used is undefined, or the expression value otherwise.
+  FileCheckASTBinop(binop_eval_t EvalBinop,
+                    std::unique_ptr<FileCheckExpressionAST> LeftOp,
+                    std::unique_ptr<FileCheckExpressionAST> RightOp)
+      : EvalBinop(EvalBinop) {
+    LeftOperand = std::move(LeftOp);
+    RightOperand = std::move(RightOp);
+  }
+
+  /// Evaluates the value of the binary operation represented by this AST,
+  /// using EvalBinop on the result of recursively evaluating the operands.
+  /// \returns the expression value or an error if an undefined numeric
+  /// variable is used in one of the operands.
   Expected<uint64_t> eval() const;
 };
 
@@ -187,15 +232,15 @@ class FileCheckNumericSubstitution : pub
 private:
   /// Pointer to the class representing the expression whose value is to be
   /// substituted.
-  FileCheckExpression *Expression;
+  std::unique_ptr<FileCheckExpressionAST> ExpressionAST;
 
 public:
-  FileCheckNumericSubstitution(FileCheckPatternContext *Context,
-                               StringRef ExpressionStr,
-                               FileCheckExpression *Expression,
+  FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
+                               std::unique_ptr<FileCheckExpressionAST> ExprAST,
                                size_t InsertIdx)
-      : FileCheckSubstitution(Context, ExpressionStr, InsertIdx),
-        Expression(Expression) {}
+      : FileCheckSubstitution(Context, Expr, InsertIdx) {
+    ExpressionAST = std::move(ExprAST);
+  }
 
   /// \returns a string containing the result of evaluating the expression in
   /// this substitution, or an error if evaluation failed.
@@ -278,10 +323,6 @@ private:
   /// easily updating its value.
   FileCheckNumericVariable *LineVariable = nullptr;
 
-  /// 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<FileCheckExpression>> Expressions;
-
   /// Vector holding pointers to all parsed numeric variables. Used to
   /// automatically free them once they are guaranteed to no longer be used.
   std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
@@ -313,12 +354,6 @@ public:
   void clearLocalVars();
 
 private:
-  /// Makes a new expression instance and registers it for destruction when
-  /// the context is destroyed.
-  FileCheckExpression *makeExpression(binop_eval_t EvalBinop,
-                                      FileCheckNumericVariable *OperandLeft,
-                                      uint64_t OperandRight);
-
   /// Makes a new numeric variable and registers it for destruction when the
   /// context is destroyed.
   template <class... Types>
@@ -333,7 +368,8 @@ private:
   /// the context is destroyed.
   FileCheckSubstitution *
   makeNumericSubstitution(StringRef ExpressionStr,
-                          FileCheckExpression *Expression, size_t InsertIdx);
+                          std::unique_ptr<FileCheckExpressionAST> ExpressionAST,
+                          size_t InsertIdx);
 };
 
 /// Class to represent an error holding a diagnostic with location information
@@ -458,13 +494,20 @@ public:
 
   /// \returns whether \p C is a valid first character for a variable name.
   static bool isValidVarNameStart(char C);
+
+  /// Parsing information about a variable.
+  struct VariableProperties {
+    StringRef Name;
+    bool IsPseudo;
+  };
+
   /// Parses the string at the start of \p Str for a variable name. \returns
-  /// an error holding a diagnostic against \p SM if parsing fail, or the
-  /// name of the variable otherwise. In the latter case, sets \p IsPseudo to
-  /// indicate if it is a pseudo variable and strips \p Str from the variable
-  /// name.
-  static Expected<StringRef> parseVariable(StringRef &Str, bool &IsPseudo,
-                                           const SourceMgr &SM);
+  /// a VariableProperties structure holding the variable name and whether it
+  /// is the name of a pseudo variable, or an error holding a diagnostic
+  /// against \p SM if parsing fail. If parsing was successful, also strips
+  /// \p Str from the variable name.
+  static Expected<VariableProperties> parseVariable(StringRef &Str,
+                                                    const SourceMgr &SM);
   /// Parses \p Expr for the name of a numeric variable to be defined at line
   /// \p LineNumber. \returns a pointer to the class instance representing that
   /// variable, creating it if needed, or an error holding a diagnostic against
@@ -473,16 +516,19 @@ public:
   parseNumericVariableDefinition(StringRef &Expr,
                                  FileCheckPatternContext *Context,
                                  size_t LineNumber, const SourceMgr &SM);
-  /// Parses \p Expr for a numeric substitution block. \returns the class
-  /// representing the AST of the expression whose value must be substituted,
-  /// 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
+  /// Parses \p Expr for a numeric substitution block. Parameter
+  /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
+  /// expression. \returns a pointer to the class instance representing the AST
+  /// of the expression whose value must be substituted, 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 being defined in this numeric
   /// substitution block, or None if this block does not define any variable.
-  Expected<FileCheckExpression *> parseNumericSubstitutionBlock(
+  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  parseNumericSubstitutionBlock(
       StringRef Expr,
       Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
-      const SourceMgr &SM) const;
+      bool IsLegacyLineExpr, const SourceMgr &SM) const;
   /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
   /// instance accordingly.
   ///
@@ -507,7 +553,7 @@ public:
   Expected<size_t> match(StringRef Buffer, size_t &MatchLen,
                          const SourceMgr &SM) const;
   /// Prints the value of successful substitutions or the name of the undefined
-  /// string or numeric variable preventing a successful substitution.
+  /// string or numeric variables preventing a successful substitution.
   void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
                           SMRange MatchRange = None) const;
   void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
@@ -536,16 +582,28 @@ private:
   /// was not found.
   size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
 
-  /// Parses \p Expr for the use of a numeric variable. \returns the pointer to
-  /// the class instance representing that variable if successful, or an error
+  /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use.
+  /// \returns the pointer to the class instance representing that variable if
+  /// successful, or an error holding a diagnostic against \p SM otherwise.
+  Expected<std::unique_ptr<FileCheckNumericVariableUse>>
+  parseNumericVariableUse(StringRef Name, bool IsPseudo,
+                          const SourceMgr &SM) const;
+  enum class AllowedOperand { LineVar, Literal, Any };
+  /// Parses \p Expr for use of a numeric operand. Accepts both literal values
+  /// and numeric variables, depending on the value of \p AO. \returns the
+  /// class representing that operand in the AST of the expression or an error
   /// holding a diagnostic against \p SM otherwise.
-  Expected<FileCheckNumericVariable *>
-  parseNumericVariableUse(StringRef &Expr, const SourceMgr &SM) const;
-  /// Parses \p Expr for a binary operation.
-  /// \returns the class representing the binary operation of the expression,
-  /// or an error holding a diagnostic against \p SM otherwise.
-  Expected<FileCheckExpression *> parseBinop(StringRef &Expr,
-                                             const SourceMgr &SM) const;
+  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  parseNumericOperand(StringRef &Expr, AllowedOperand AO,
+                      const SourceMgr &SM) const;
+  /// Parses \p Expr for a binary operation. The left operand of this binary
+  /// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
+  /// we are parsing a legacy @LINE expression. \returns the class representing
+  /// the binary operation in the AST of the expression, or an error holding a
+  /// diagnostic against \p SM otherwise.
+  Expected<std::unique_ptr<FileCheckExpressionAST>>
+  parseBinop(StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp,
+             bool IsLegacyLineExpr, const SourceMgr &SM) const;
 };
 
 //===----------------------------------------------------------------------===//

Modified: llvm/trunk/lib/Support/FileCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/lib/Support/FileCheck.cpp (original)
+++ llvm/trunk/lib/Support/FileCheck.cpp Sat Jul 13 06:24:30 2019
@@ -35,17 +35,33 @@ void FileCheckNumericVariable::clearValu
   Value = None;
 }
 
-Expected<uint64_t> FileCheckExpression::eval() const {
-  assert(LeftOp && "Evaluating an empty expression");
-  Optional<uint64_t> LeftOpValue = LeftOp->getValue();
-  // Variable is undefined.
-  if (!LeftOpValue)
-    return make_error<FileCheckUndefVarError>(LeftOp->getName());
-  return EvalBinop(*LeftOpValue, RightOp);
+Expected<uint64_t> FileCheckNumericVariableUse::eval() const {
+  Optional<uint64_t> Value = NumericVariable->getValue();
+  if (Value)
+    return *Value;
+  return make_error<FileCheckUndefVarError>(Name);
+}
+
+Expected<uint64_t> FileCheckASTBinop::eval() const {
+  Expected<uint64_t> LeftOp = LeftOperand->eval();
+  Expected<uint64_t> RightOp = RightOperand->eval();
+
+  // Bubble up any error (e.g. undefined variables) in the recursive
+  // evaluation.
+  if (!LeftOp || !RightOp) {
+    Error Err = Error::success();
+    if (!LeftOp)
+      Err = joinErrors(std::move(Err), LeftOp.takeError());
+    if (!RightOp)
+      Err = joinErrors(std::move(Err), RightOp.takeError());
+    return std::move(Err);
+  }
+
+  return EvalBinop(*LeftOp, *RightOp);
 }
 
 Expected<std::string> FileCheckNumericSubstitution::getResult() const {
-  Expected<uint64_t> EvaluatedValue = Expression->eval();
+  Expected<uint64_t> EvaluatedValue = ExpressionAST->eval();
   if (!EvaluatedValue)
     return EvaluatedValue.takeError();
   return utostr(*EvaluatedValue);
@@ -63,15 +79,14 @@ bool FileCheckPattern::isValidVarNameSta
   return C == '_' || isalpha(C);
 }
 
-Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str,
-                                                    bool &IsPseudo,
-                                                    const SourceMgr &SM) {
+Expected<FileCheckPattern::VariableProperties>
+FileCheckPattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
   if (Str.empty())
     return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name");
 
   bool ParsedOneChar = false;
   unsigned I = 0;
-  IsPseudo = Str[0] == '@';
+  bool IsPseudo = Str[0] == '@';
 
   // Global vars start with '$'.
   if (Str[0] == '$' || IsPseudo)
@@ -89,7 +104,7 @@ Expected<StringRef> FileCheckPattern::pa
 
   StringRef Name = Str.take_front(I);
   Str = Str.substr(I);
-  return Name;
+  return VariableProperties {Name, IsPseudo};
 }
 
 // StringRef holding all characters considered as horizontal whitespaces by
@@ -111,13 +126,12 @@ Expected<FileCheckNumericVariable *>
 FileCheckPattern::parseNumericVariableDefinition(
     StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber,
     const SourceMgr &SM) {
-  bool IsPseudo;
-  Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
+  Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
   if (!ParseVarResult)
     return ParseVarResult.takeError();
-  StringRef Name = *ParseVarResult;
+  StringRef Name = ParseVarResult->Name;
 
-  if (IsPseudo)
+  if (ParseVarResult->IsPseudo)
     return FileCheckErrorDiagnostic::get(
         SM, Name, "definition of pseudo numeric variable unsupported");
 
@@ -143,15 +157,9 @@ FileCheckPattern::parseNumericVariableDe
   return DefinedNumericVariable;
 }
 
-Expected<FileCheckNumericVariable *>
-FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
+Expected<std::unique_ptr<FileCheckNumericVariableUse>>
+FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo,
                                           const SourceMgr &SM) const {
-  bool IsPseudo;
-  Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
-  if (!ParseVarResult)
-    return ParseVarResult.takeError();
-  StringRef Name = *ParseVarResult;
-
   if (IsPseudo && !Name.equals("@LINE"))
     return FileCheckErrorDiagnostic::get(
         SM, Name, "invalid pseudo numeric variable '" + Name + "'");
@@ -178,7 +186,32 @@ FileCheckPattern::parseNumericVariableUs
         SM, Name,
         "numeric variable '" + Name + "' defined on the same line as used");
 
-  return NumericVariable;
+  return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable);
+}
+
+Expected<std::unique_ptr<FileCheckExpressionAST>>
+FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO,
+                                      const SourceMgr &SM) const {
+  if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
+    // Try to parse as a numeric variable use.
+    Expected<FileCheckPattern::VariableProperties> ParseVarResult =
+        parseVariable(Expr, SM);
+    if (ParseVarResult)
+      return parseNumericVariableUse(ParseVarResult->Name,
+                                     ParseVarResult->IsPseudo, SM);
+    if (AO == AllowedOperand::LineVar)
+      return ParseVarResult.takeError();
+    // Ignore the error and retry parsing as a literal.
+    consumeError(ParseVarResult.takeError());
+  }
+
+  // Otherwise, parse it as a literal.
+  uint64_t LiteralValue;
+  if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
+    return llvm::make_unique<FileCheckExpressionLiteral>(LiteralValue);
+
+  return FileCheckErrorDiagnostic::get(SM, Expr,
+                                       "invalid operand format '" + Expr + "'");
 }
 
 static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
@@ -189,20 +222,16 @@ static uint64_t sub(uint64_t LeftOp, uin
   return LeftOp - RightOp;
 }
 
-Expected<FileCheckExpression *>
-FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const {
-  Expected<FileCheckNumericVariable *> LeftParseResult =
-      parseNumericVariableUse(Expr, SM);
-  if (!LeftParseResult) {
-    return LeftParseResult.takeError();
-  }
-  FileCheckNumericVariable *LeftOp = *LeftParseResult;
+Expected<std::unique_ptr<FileCheckExpressionAST>>
+FileCheckPattern::parseBinop(StringRef &Expr,
+                             std::unique_ptr<FileCheckExpressionAST> LeftOp,
+                             bool IsLegacyLineExpr, const SourceMgr &SM) const {
+  Expr = Expr.ltrim(SpaceChars);
+  if (Expr.empty())
+    return std::move(LeftOp);
 
   // Check if this is a supported operation and select a function to perform
   // it.
-  Expr = Expr.ltrim(SpaceChars);
-  if (Expr.empty())
-    return Context->makeExpression(add, LeftOp, 0);
   SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
   char Operator = popFront(Expr);
   binop_eval_t EvalBinop;
@@ -223,22 +252,24 @@ FileCheckPattern::parseBinop(StringRef &
   if (Expr.empty())
     return FileCheckErrorDiagnostic::get(SM, Expr,
                                          "missing operand in expression");
-  uint64_t RightOp;
-  if (Expr.consumeInteger(10, RightOp))
-    return FileCheckErrorDiagnostic::get(
-        SM, Expr, "invalid offset in expression '" + Expr + "'");
-  Expr = Expr.ltrim(SpaceChars);
-  if (!Expr.empty())
-    return FileCheckErrorDiagnostic::get(
-        SM, Expr, "unexpected characters at end of expression '" + Expr + "'");
+  // The second operand in a legacy @LINE expression is always a literal.
+  AllowedOperand AO =
+      IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
+  Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult =
+      parseNumericOperand(Expr, AO, SM);
+  if (!RightOpResult)
+    return RightOpResult;
 
-  return Context->makeExpression(EvalBinop, LeftOp, RightOp);
+  Expr = Expr.ltrim(SpaceChars);
+  return llvm::make_unique<FileCheckASTBinop>(EvalBinop, std::move(LeftOp),
+                                              std::move(*RightOpResult));
 }
 
-Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock(
+Expected<std::unique_ptr<FileCheckExpressionAST>>
+FileCheckPattern::parseNumericSubstitutionBlock(
     StringRef Expr,
     Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
-    const SourceMgr &SM) const {
+    bool IsLegacyLineExpr, const SourceMgr &SM) const {
   // Parse the numeric variable definition.
   DefinedNumericVariable = None;
   size_t DefEnd = Expr.find(':');
@@ -259,12 +290,29 @@ Expected<FileCheckExpression *> FileChec
       return ParseResult.takeError();
     DefinedNumericVariable = *ParseResult;
 
-    return Context->makeExpression(add, nullptr, 0);
+    return nullptr;
   }
 
   // Parse the expression itself.
   Expr = Expr.ltrim(SpaceChars);
-  return parseBinop(Expr, SM);
+  // The first operand in a legacy @LINE expression is always the @LINE pseudo
+  // variable.
+  AllowedOperand AO =
+      IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
+  Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
+      parseNumericOperand(Expr, AO, SM);
+  while (ParseResult && !Expr.empty()) {
+    ParseResult =
+        parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM);
+    // Legacy @LINE expressions only allow 2 operands.
+    if (ParseResult && IsLegacyLineExpr && !Expr.empty())
+      return FileCheckErrorDiagnostic::get(
+          SM, Expr,
+          "unexpected characters at end of expression '" + Expr + "'");
+  }
+  if (!ParseResult)
+    return ParseResult;
+  return std::move(*ParseResult);
 }
 
 bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@@ -375,12 +423,15 @@ bool FileCheckPattern::parsePattern(Stri
       PatternStr = UnparsedPatternStr.substr(End + 2);
 
       bool IsDefinition = false;
+      // Whether the substitution block is a legacy use of @LINE with string
+      // substitution block syntax.
+      bool IsLegacyLineExpr = false;
       StringRef DefName;
       StringRef SubstStr;
       StringRef MatchRegexp;
       size_t SubstInsertIdx = RegExStr.size();
 
-      // Parse string variable or legacy expression.
+      // Parse string variable or legacy @LINE expression.
       if (!IsNumBlock) {
         size_t VarEndIdx = MatchStr.find(":");
         size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
@@ -391,15 +442,15 @@ bool FileCheckPattern::parsePattern(Stri
         }
 
         // Get the name (e.g. "foo") and verify it is well formed.
-        bool IsPseudo;
         StringRef OrigMatchStr = MatchStr;
-        Expected<StringRef> ParseVarResult =
-            parseVariable(MatchStr, IsPseudo, SM);
+        Expected<FileCheckPattern::VariableProperties> ParseVarResult =
+            parseVariable(MatchStr, SM);
         if (!ParseVarResult) {
           logAllUnhandledErrors(ParseVarResult.takeError(), errs());
           return true;
         }
-        StringRef Name = *ParseVarResult;
+        StringRef Name = ParseVarResult->Name;
+        bool IsPseudo = ParseVarResult->IsPseudo;
 
         IsDefinition = (VarEndIdx != StringRef::npos);
         if (IsDefinition) {
@@ -424,23 +475,24 @@ bool FileCheckPattern::parsePattern(Stri
         } else {
           if (IsPseudo) {
             MatchStr = OrigMatchStr;
-            IsNumBlock = true;
+            IsLegacyLineExpr = IsNumBlock = true;
           } else
             SubstStr = Name;
         }
       }
 
       // Parse numeric substitution block.
-      FileCheckExpression *Expression;
+      std::unique_ptr<FileCheckExpressionAST> ExpressionAST;
       Optional<FileCheckNumericVariable *> DefinedNumericVariable;
       if (IsNumBlock) {
-        Expected<FileCheckExpression *> ParseResult =
-            parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
+        Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult =
+            parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
+                                          IsLegacyLineExpr, SM);
         if (!ParseResult) {
           logAllUnhandledErrors(ParseResult.takeError(), errs());
           return true;
         }
-        Expression = *ParseResult;
+        ExpressionAST = std::move(*ParseResult);
         if (DefinedNumericVariable) {
           IsDefinition = true;
           DefName = (*DefinedNumericVariable)->getName();
@@ -468,8 +520,8 @@ bool FileCheckPattern::parsePattern(Stri
           // previous CHECK patterns, and substitution of expressions.
           FileCheckSubstitution *Substitution =
               IsNumBlock
-                  ? Context->makeNumericSubstitution(SubstStr, Expression,
-                                                     SubstInsertIdx)
+                  ? Context->makeNumericSubstitution(
+                        SubstStr, std::move(ExpressionAST), SubstInsertIdx)
                   : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
           Substitutions.push_back(Substitution);
         }
@@ -660,7 +712,7 @@ void FileCheckPattern::printSubstitution
       Expected<std::string> MatchedValue = Substitution->getResult();
 
       // Substitution failed or is not known at match time, print the undefined
-      // variable it uses.
+      // variables it uses.
       if (!MatchedValue) {
         bool UndefSeen = false;
         handleAllErrors(MatchedValue.takeError(),
@@ -669,13 +721,11 @@ void FileCheckPattern::printSubstitution
                         [](const FileCheckErrorDiagnostic &E) {},
                         [&](const FileCheckUndefVarError &E) {
                           if (!UndefSeen) {
-                            OS << "uses undefined variable ";
+                            OS << "uses undefined variable(s):";
                             UndefSeen = true;
                           }
+                          OS << " ";
                           E.log(OS);
-                        },
-                        [](const ErrorInfoBase &E) {
-                          llvm_unreachable("Unexpected error");
                         });
       } else {
         // Substitution succeeded. Print substituted value.
@@ -768,15 +818,6 @@ FileCheckPatternContext::getPatternVarVa
   return VarIter->second;
 }
 
-FileCheckExpression *
-FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop,
-                                        FileCheckNumericVariable *OperandLeft,
-                                        uint64_t OperandRight) {
-  Expressions.push_back(llvm::make_unique<FileCheckExpression>(
-      EvalBinop, OperandLeft, OperandRight));
-  return Expressions.back().get();
-}
-
 template <class... Types>
 FileCheckNumericVariable *
 FileCheckPatternContext::makeNumericVariable(Types... args) {
@@ -794,10 +835,10 @@ FileCheckPatternContext::makeStringSubst
 }
 
 FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution(
-    StringRef ExpressionStr, FileCheckExpression *Expression,
-    size_t InsertIdx) {
+    StringRef ExpressionStr,
+    std::unique_ptr<FileCheckExpressionAST> ExpressionAST, size_t InsertIdx) {
   Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>(
-      this, ExpressionStr, Expression, InsertIdx));
+      this, ExpressionStr, std::move(ExpressionAST), InsertIdx));
   return Substitutions.back().get();
 }
 
@@ -1777,9 +1818,8 @@ Error FileCheckPatternContext::defineCmd
       std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
       StringRef CmdlineName = CmdlineNameVal.first;
       StringRef OrigCmdlineName = CmdlineName;
-      bool IsPseudo;
-      Expected<StringRef> ParseVarResult =
-          FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM);
+      Expected<FileCheckPattern::VariableProperties> ParseVarResult =
+          FileCheckPattern::parseVariable(CmdlineName, SM);
       if (!ParseVarResult) {
         Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
         continue;
@@ -1787,7 +1827,7 @@ Error FileCheckPatternContext::defineCmd
       // Check that CmdlineName does not denote a pseudo variable is only
       // composed of the parsed numeric variable. This catches cases like
       // "FOO+2" in a "FOO+2=10" definition.
-      if (IsPseudo || !CmdlineName.empty()) {
+      if (ParseVarResult->IsPseudo || !CmdlineName.empty()) {
         Errs = joinErrors(std::move(Errs),
                           FileCheckErrorDiagnostic::get(
                               SM, OrigCmdlineName,
@@ -1795,7 +1835,7 @@ Error FileCheckPatternContext::defineCmd
                                   OrigCmdlineName + "'"));
         continue;
       }
-      StringRef Name = *ParseVarResult;
+      StringRef Name = ParseVarResult->Name;
 
       // Detect collisions between string and numeric variables when the former
       // is created later than the latter.

Modified: llvm/trunk/test/FileCheck/line-count.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/line-count.txt?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/line-count.txt (original)
+++ llvm/trunk/test/FileCheck/line-count.txt Sat Jul 13 06:24:30 2019
@@ -50,7 +50,7 @@
 50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported operation '*'
 51
 52 BAD10: [[@LINE-x]]
-53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in expression 'x'
+53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format 'x'
 54
 55 BAD11: [[@LINE-1x]]
 56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'

Modified: llvm/trunk/test/FileCheck/numeric-expression.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-expression.txt?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-expression.txt (original)
+++ llvm/trunk/test/FileCheck/numeric-expression.txt Sat Jul 13 06:24:30 2019
@@ -59,8 +59,8 @@ CHECK-NEXT: [[# VAR1 -1]]
 CHECK-NEXT: [[# VAR1 - 1]]
 CHECK-NEXT: [[# VAR1 - 1 ]]
 
-; Numeric expressions using variables defined on the command-line and an
-; immediate interpreted as an unsigned value.
+; 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
@@ -68,21 +68,29 @@ USE UNSIGNED IMM
 CHECK-LABEL: USE UNSIGNED IMM
 CHECK-NEXT: [[#VAR1+9223372036854775808]]
 
-; Numeric expression using undefined variable.
+; 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]]
+
+; Numeric expression using undefined variables.
 RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
 RUN:   | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
 
 UNDEF VAR USE
 UNDEFVAR: 11
 UNDEF-USE-LABEL: UNDEF VAR USE
-UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]]
+UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]]
 UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:17: error: {{U}}NDEF-USE-NEXT: expected string not found in input
-UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}}
+UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}}
 UNDEF-USE-MSG-NEXT: {{^                \^$}}
 UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-6]]:1: note: scanning from here
 UNDEF-USE-MSG-NEXT: UNDEFVAR: 11
 UNDEF-USE-MSG-NEXT: {{^\^$}}
-UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable "UNDEFVAR"
+UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-9]]:1: note: uses undefined variable(s): "UNDEFVAR1" "UNDEFVAR2"
 UNDEF-USE-MSG-NEXT: UNDEFVAR: 11
 UNDEF-USE-MSG-NEXT: {{^\^$}}
 

Modified: llvm/trunk/test/FileCheck/var-scope.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/var-scope.txt?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/var-scope.txt (original)
+++ llvm/trunk/test/FileCheck/var-scope.txt Sat Jul 13 06:24:30 2019
@@ -34,5 +34,5 @@ LOCAL3: [[LOCAL]][[#LOCNUM+2]]
 GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]]
 
 ERRUNDEF: expected string not found in input
-ERRUNDEFLOCAL: uses undefined variable "LOCAL"
-ERRUNDEFLOCNUM: uses undefined variable "LOCNUM"
+ERRUNDEFLOCAL: uses undefined variable(s): "LOCAL"
+ERRUNDEFLOCNUM: uses undefined variable(s): "LOCNUM"

Modified: llvm/trunk/unittests/Support/FileCheckTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/FileCheckTest.cpp?rev=366001&r1=366000&r2=366001&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FileCheckTest.cpp (original)
+++ llvm/trunk/unittests/Support/FileCheckTest.cpp Sat Jul 13 06:24:30 2019
@@ -8,56 +8,112 @@
 
 #include "llvm/Support/FileCheck.h"
 #include "gtest/gtest.h"
+#include <unordered_set>
 
 using namespace llvm;
 namespace {
 
 class FileCheckTest : public ::testing::Test {};
 
+TEST_F(FileCheckTest, Literal) {
+  // Eval returns the literal's value.
+  FileCheckExpressionLiteral Ten(10);
+  Expected<uint64_t> Value = Ten.eval();
+  EXPECT_TRUE(bool(Value));
+  EXPECT_EQ(10U, *Value);
+
+  // Max value can be correctly represented.
+  FileCheckExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
+  Value = Max.eval();
+  EXPECT_TRUE(bool(Value));
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
+}
+
+static std::string toString(const std::unordered_set<std::string> &Set) {
+  bool First = true;
+  std::string Str;
+  for (StringRef S : Set) {
+    Str += Twine(First ? "{" + S : ", " + S).str();
+    First = false;
+  }
+  Str += '}';
+  return Str;
+}
+
+static void
+expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
+                  Error Err) {
+  handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
+    ExpectedUndefVarNames.erase(E.getVarName());
+  });
+  EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
+}
+
+static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
+  expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
+}
+
 TEST_F(FileCheckTest, NumericVariable) {
-  // Undefined variable: getValue fails, setValue does not trigger assert.
+  // Undefined variable: getValue and eval fail, error returned by eval holds
+  // the name of the undefined variable and setValue does not trigger assert.
   FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO");
   EXPECT_EQ("FOO", FooVar.getName());
-  llvm::Optional<uint64_t> Value = FooVar.getValue();
-  EXPECT_FALSE(Value);
-  FooVar.clearValue();
+  FileCheckNumericVariableUse FooVarUse =
+      FileCheckNumericVariableUse("FOO", &FooVar);
+  EXPECT_FALSE(FooVar.getValue());
+  Expected<uint64_t> EvalResult = FooVarUse.eval();
+  EXPECT_FALSE(EvalResult);
+  expectUndefError("FOO", EvalResult.takeError());
   FooVar.setValue(42);
 
-  // Defined variable: getValue returns value set.
-  Value = FooVar.getValue();
-  EXPECT_TRUE(Value);
+  // Defined variable: getValue and eval return value set.
+  Optional<uint64_t> Value = FooVar.getValue();
+  EXPECT_TRUE(bool(Value));
   EXPECT_EQ(42U, *Value);
+  EvalResult = FooVarUse.eval();
+  EXPECT_TRUE(bool(EvalResult));
+  EXPECT_EQ(42U, *EvalResult);
 
-  // Clearing variable: getValue fails.
+  // Clearing variable: getValue and eval fail. Error returned by eval holds
+  // the name of the cleared variable.
   FooVar.clearValue();
   Value = FooVar.getValue();
   EXPECT_FALSE(Value);
+  EvalResult = FooVarUse.eval();
+  EXPECT_FALSE(EvalResult);
+  expectUndefError("FOO", EvalResult.takeError());
 }
 
 uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
 
-static void expectUndefError(const Twine &ExpectedStr, Error Err) {
-  handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
-    EXPECT_EQ(ExpectedStr.str(), E.getVarName());
-  });
-}
-
-TEST_F(FileCheckTest, Expression) {
+TEST_F(FileCheckTest, Binop) {
   FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
-  FileCheckExpression Expression = FileCheckExpression(doAdd, &FooVar, 18);
+  std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
+      llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
+  FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 18);
+  std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
+      llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
+  FileCheckASTBinop Binop =
+      FileCheckASTBinop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
 
   // Defined variable: eval returns right value.
-  Expected<uint64_t> Value = Expression.eval();
+  Expected<uint64_t> Value = Binop.eval();
   EXPECT_TRUE(bool(Value));
   EXPECT_EQ(60U, *Value);
 
-  // Undefined variable: eval fails, undefined variable returned. We call
-  // getUndefVarName first to check that it can be called without calling
-  // eval() first.
+  // 1 undefined variable: eval fails, error contains name of undefined
+  // variable.
   FooVar.clearValue();
-  Error EvalError = Expression.eval().takeError();
-  EXPECT_TRUE(errorToBool(std::move(EvalError)));
-  expectUndefError("FOO", std::move(EvalError));
+  Value = Binop.eval();
+  EXPECT_FALSE(Value);
+  expectUndefError("FOO", Value.takeError());
+
+  // 2 undefined variables: eval fails, error contains names of all undefined
+  // variables.
+  BarVar.clearValue();
+  Value = Binop.eval();
+  EXPECT_FALSE(Value);
+  expectUndefErrors({"FOO", "BAR"}, Value.takeError());
 }
 
 TEST_F(FileCheckTest, ValidVarNameStart) {
@@ -84,77 +140,69 @@ TEST_F(FileCheckTest, ParseVar) {
   SourceMgr SM;
   StringRef OrigVarName = bufferize(SM, "GoodVar42");
   StringRef VarName = OrigVarName;
-  bool IsPseudo = true;
-  Expected<StringRef> ParsedName =
-      FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
-  EXPECT_EQ(*ParsedName, OrigVarName);
+  Expected<FileCheckPattern::VariableProperties> ParsedVarResult =
+      FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
+  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
   EXPECT_TRUE(VarName.empty());
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
-  EXPECT_EQ(*ParsedName, OrigVarName);
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
+  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
   EXPECT_TRUE(VarName.empty());
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
-  EXPECT_EQ(*ParsedName, OrigVarName);
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
+  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
   EXPECT_TRUE(VarName.empty());
-  EXPECT_TRUE(IsPseudo);
+  EXPECT_TRUE(ParsedVarResult->IsPseudo);
 
   VarName = bufferize(SM, "42BadVar");
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(errorToBool(ParsedName.takeError()));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
 
   VarName = bufferize(SM, "$@");
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(errorToBool(ParsedName.takeError()));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
 
   VarName = OrigVarName = bufferize(SM, "B at dVar");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
   EXPECT_EQ(VarName, OrigVarName.substr(1));
-  EXPECT_EQ(*ParsedName, "B");
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_EQ(ParsedVarResult->Name, "B");
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = OrigVarName = bufferize(SM, "B$dVar");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
   EXPECT_EQ(VarName, OrigVarName.substr(1));
-  EXPECT_EQ(*ParsedName, "B");
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_EQ(ParsedVarResult->Name, "B");
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = bufferize(SM, "BadVar+");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
   EXPECT_EQ(VarName, "+");
-  EXPECT_EQ(*ParsedName, "BadVar");
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = bufferize(SM, "BadVar-");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
   EXPECT_EQ(VarName, "-");
-  EXPECT_EQ(*ParsedName, "BadVar");
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 
   VarName = bufferize(SM, "BadVar:");
-  IsPseudo = true;
-  ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
-  EXPECT_TRUE(bool(ParsedName));
+  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
+  EXPECT_TRUE(bool(ParsedVarResult));
   EXPECT_EQ(VarName, ":");
-  EXPECT_EQ(*ParsedName, "BadVar");
-  EXPECT_FALSE(IsPseudo);
+  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 }
 
 class PatternTester {
@@ -197,7 +245,7 @@ public:
     StringRef ExprBufferRef = bufferize(SM, Expr);
     Optional<FileCheckNumericVariable *> DefinedNumericVariable;
     return errorToBool(P.parseNumericSubstitutionBlock(
-                            ExprBufferRef, DefinedNumericVariable, SM)
+                            ExprBufferRef, DefinedNumericVariable, false, SM)
                            .takeError());
   }
 
@@ -269,15 +317,12 @@ TEST_F(FileCheckTest, ParseExpr) {
   // Missing offset operand.
   EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));
 
-  // Cannot parse offset operand.
-  EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x"));
-
-  // Unexpected string at end of numeric expression.
-  EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x"));
-
   // Valid expression.
   EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
   EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
+  Tester.initNextPattern();
+  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
+  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
 }
 
 TEST_F(FileCheckTest, ParsePattern) {
@@ -306,7 +351,6 @@ TEST_F(FileCheckTest, ParsePattern) {
   EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
   EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
   EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
-  EXPECT_TRUE(Tester.parsePatternExpect("[[#2+ at LINE]]"));
   EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]"));
 
   // Valid numeric expressions and numeric variable definition.
@@ -365,35 +409,37 @@ TEST_F(FileCheckTest, Substitution) {
   // the right value.
   FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
   FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10);
-  FileCheckExpression LineExpression = FileCheckExpression(doAdd, &LineVar, 0);
-  FileCheckExpression NExpression = FileCheckExpression(doAdd, &NVar, 3);
-  FileCheckNumericSubstitution SubstitutionLine =
-      FileCheckNumericSubstitution(&Context, "@LINE", &LineExpression, 12);
+  auto LineVarUse =
+      llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
+  auto NVarUse = llvm::make_unique<FileCheckNumericVariableUse>("N", &NVar);
+  FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(
+      &Context, "@LINE", std::move(LineVarUse), 12);
   FileCheckNumericSubstitution SubstitutionN =
-      FileCheckNumericSubstitution(&Context, "N", &NExpression, 30);
-  Expected<std::string> Value = SubstitutionLine.getResult();
-  EXPECT_TRUE(bool(Value));
-  EXPECT_EQ("42", *Value);
-  Value = SubstitutionN.getResult();
-  EXPECT_TRUE(bool(Value));
-  EXPECT_EQ("13", *Value);
+      FileCheckNumericSubstitution(&Context, "N", std::move(NVarUse), 30);
+  SubstValue = SubstitutionLine.getResult();
+  EXPECT_TRUE(bool(SubstValue));
+  EXPECT_EQ("42", *SubstValue);
+  SubstValue = SubstitutionN.getResult();
+  EXPECT_TRUE(bool(SubstValue));
+  EXPECT_EQ("10", *SubstValue);
 
-  // Substitution of an undefined numeric variable fails.
+  // Substitution of an undefined numeric variable fails, error holds name of
+  // undefined variable.
   LineVar.clearValue();
-  SubstValue = SubstitutionLine.getResult().takeError();
+  SubstValue = SubstitutionLine.getResult();
   EXPECT_FALSE(bool(SubstValue));
   expectUndefError("@LINE", SubstValue.takeError());
   NVar.clearValue();
-  SubstValue = SubstitutionN.getResult().takeError();
+  SubstValue = SubstitutionN.getResult();
   EXPECT_FALSE(bool(SubstValue));
   expectUndefError("N", SubstValue.takeError());
 
   // Substitution of a defined string variable returns the right value.
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
   StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
-  Value = StringSubstitution.getResult();
-  EXPECT_TRUE(bool(Value));
-  EXPECT_EQ("BAR", *Value);
+  SubstValue = StringSubstitution.getResult();
+  EXPECT_TRUE(bool(SubstValue));
+  EXPECT_EQ("BAR", *SubstValue);
 }
 
 TEST_F(FileCheckTest, FileCheckContext) {
@@ -456,14 +502,15 @@ TEST_F(FileCheckTest, FileCheckContext)
   Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
   Optional<FileCheckNumericVariable *> DefinedNumericVariable;
-  Expected<FileCheckExpression *> Expression = P.parseNumericSubstitutionBlock(
-      LocalNumVarRef, DefinedNumericVariable, SM);
-  Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
-  Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
+  Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
+      P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable,
+                                      /*IsLegacyLineExpr=*/false, SM);
   EXPECT_TRUE(bool(LocalVar));
   EXPECT_EQ(*LocalVar, "FOO");
-  EXPECT_TRUE(bool(Expression));
-  Expected<uint64_t> ExpressionVal = (*Expression)->eval();
+  Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
+  Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
+  EXPECT_TRUE(bool(ExpressionAST));
+  Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));
   EXPECT_EQ(*ExpressionVal, 18U);
   EXPECT_TRUE(bool(EmptyVar));
@@ -478,12 +525,12 @@ 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.
-  EXPECT_TRUE(errorToBool((*Expression)->eval().takeError()));
+  EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
-  Expression = P.parseNumericSubstitutionBlock(LocalNumVarRef,
-                                               DefinedNumericVariable, SM);
-  EXPECT_TRUE(bool(Expression));
-  ExpressionVal = (*Expression)->eval();
+  ExpressionAST = P.parseNumericSubstitutionBlock(
+      LocalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+  EXPECT_TRUE(bool(ExpressionAST));
+  ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
   EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   EXPECT_TRUE(errorToBool(EmptyVar.takeError()));
@@ -501,10 +548,10 @@ TEST_F(FileCheckTest, FileCheckContext)
   EXPECT_TRUE(bool(GlobalVar));
   EXPECT_EQ(*GlobalVar, "BAR");
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
-  Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
-                                               DefinedNumericVariable, SM);
-  EXPECT_TRUE(bool(Expression));
-  ExpressionVal = (*Expression)->eval();
+  ExpressionAST = P.parseNumericSubstitutionBlock(
+      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+  EXPECT_TRUE(bool(ExpressionAST));
+  ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));
   EXPECT_EQ(*ExpressionVal, 36U);
 
@@ -512,10 +559,10 @@ TEST_F(FileCheckTest, FileCheckContext)
   Cxt.clearLocalVars();
   EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
   P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
-  Expression = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
-                                               DefinedNumericVariable, SM);
-  EXPECT_TRUE(bool(Expression));
-  ExpressionVal = (*Expression)->eval();
+  ExpressionAST = P.parseNumericSubstitutionBlock(
+      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, SM);
+  EXPECT_TRUE(bool(ExpressionAST));
+  ExpressionVal = (*ExpressionAST)->eval();
   EXPECT_TRUE(bool(ExpressionVal));
   EXPECT_EQ(*ExpressionVal, 36U);
 }




More information about the llvm-commits mailing list