FileCheck [11/12]: Add matching constraint specification
authorThomas Preud'homme <thomasp@graphcore.ai>
Wed, 17 Jul 2019 15:15:52 +0000 (16:15 +0100)
committerThomas Preud'homme <thomasp@graphcore.ai>
Wed, 10 Jun 2020 14:56:10 +0000 (15:56 +0100)
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch adds support for specifying the
matching constraint for a numeric expression, ie. how the value being
matched should relate to the numeric expression.

This commit only adds the equality constraint where the numeric value
matched must be equal to the numeric expression. It is the default
matching constraint used when not specified. It is added to provision
other matching constraint (e.g. inequality relations).

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)

Reviewed By: jhenderson

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

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-expression.txt
llvm/unittests/Support/FileCheckTest.cpp

index 8199258..0f72b41 100644 (file)
@@ -670,7 +670,8 @@ For example:
 would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM``
 to the value ``0xF0F0``.
 
-The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
+The syntax of a numeric substitution is
+``[[#%<fmtspec>: <constraint> <expr>]]`` where:
 
 * ``%<fmtspec>`` is the same matching format specifier as for defining numeric
   variables but acting as a printf-style format to indicate how a numeric
@@ -680,6 +681,12 @@ The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
   is used.  In case of conflict between matching formats of several numeric
   variables the format specifier is mandatory.
 
+* ``<constraint>`` is the constraint describing how the value to match must
+  relate to the value of the numeric expression. The only currently accepted
+  constraint is ``==`` for an exact match and is the default if
+  ``<constraint>`` is not provided. No matching constraint must be specified
+  when the ``<expr>`` is empty.
+
 * ``<expr>`` is an expression. An expression is in turn recursively defined
   as:
 
@@ -747,11 +754,12 @@ does not matter:
 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 ``[[#%<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:
+in which case the numeric expression constraint is checked and if verified the
+variable is assigned to the value. The unified syntax for both defining numeric
+variables and checking a numeric expression is thus
+``[[#%<fmtspec>,<NUMVAR>: <constraint> <expr>]]`` with each element as
+described previously. One can use this syntax to make a testcase more
+self-describing by using variables instead of values:
 
 .. code-block:: gas
 
index 4a797ea..fe4d641 100644 (file)
@@ -448,8 +448,9 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
 }
 
 Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
-    StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber,
-    FileCheckPatternContext *Context, const SourceMgr &SM) {
+    StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,
+    Optional<size_t> LineNumber, FileCheckPatternContext *Context,
+    const SourceMgr &SM) {
   if (Expr.startswith("(")) {
     if (AO != AllowedOperand::Any)
       return ErrorDiagnostic::get(
@@ -497,8 +498,11 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
     return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
                                                SignedLiteralValue);
 
-  return ErrorDiagnostic::get(SM, Expr,
-                              "invalid operand format '" + Expr + "'");
+  return ErrorDiagnostic::get(
+      SM, Expr,
+      Twine("invalid ") +
+          (MaybeInvalidConstraint ? "matching constraint or " : "") +
+          "operand format");
 }
 
 Expected<std::unique_ptr<ExpressionAST>>
@@ -514,8 +518,9 @@ Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
     return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
 
   // Note: parseNumericOperand handles nested opening parentheses.
-  Expected<std::unique_ptr<ExpressionAST>> SubExprResult =
-      parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
+  Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(
+      Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
+      Context, SM);
   Expr = Expr.ltrim(SpaceChars);
   while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) {
     StringRef OrigExpr = Expr;
@@ -568,7 +573,8 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
   AllowedOperand AO =
       IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
   Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
-      parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM);
+      parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,
+                          LineNumber, Context, SM);
   if (!RightOpResult)
     return RightOpResult;
 
@@ -606,8 +612,9 @@ Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
 
     // Parse the argument, which is an arbitary expression.
     StringRef OuterBinOpExpr = Expr;
-    Expected<std::unique_ptr<ExpressionAST>> Arg =
-        parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
+    Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(
+        Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
+        Context, SM);
     while (Arg && !Expr.empty()) {
       Expr = Expr.ltrim(SpaceChars);
       // Have we reached an argument terminator?
@@ -702,17 +709,27 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
     Expr = Expr.substr(DefEnd + 1);
   }
 
+  // Parse matching constraint.
+  Expr = Expr.ltrim(SpaceChars);
+  bool HasParsedValidConstraint = false;
+  if (Expr.consume_front("=="))
+    HasParsedValidConstraint = true;
+
   // Parse the expression itself.
   Expr = Expr.ltrim(SpaceChars);
-  if (!Expr.empty()) {
+  if (Expr.empty()) {
+    if (HasParsedValidConstraint)
+      return ErrorDiagnostic::get(
+          SM, Expr, "empty numeric expression should not have a constraint");
+  } else {
     Expr = Expr.rtrim(SpaceChars);
     StringRef OuterBinOpExpr = Expr;
     // 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<ExpressionAST>> ParseResult =
-        parseNumericOperand(Expr, AO, LineNumber, Context, SM);
+    Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(
+        Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);
     while (ParseResult && !Expr.empty()) {
       ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
                                IsLegacyLineExpr, LineNumber, Context, SM);
index d3bc32b..c909316 100644 (file)
@@ -728,13 +728,14 @@ private:
   /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
   /// before input is parsed if \p LineNumber is None. Accepts literal values,
   /// numeric variables and function calls, depending on the value of \p AO.
-  /// Parameter \p Context points to the class instance holding the live string
-  /// and numeric variables. \returns the class representing that operand in the
-  /// AST of the expression or an error holding a diagnostic against \p SM
-  /// otherwise. If \p Expr starts with a "(" this function will attempt to
-  /// parse a parenthesized expression.
+  /// \p MaybeInvalidConstraint indicates whether the text being parsed could
+  /// be an invalid constraint. \p Context points to the class instance holding
+  /// the live string and numeric variables. \returns the class representing
+  /// that operand in the AST of the expression or an error holding a
+  /// diagnostic against \p SM otherwise. If \p Expr starts with a "(" this
+  /// function will attempt to parse a parenthesized expression.
   static Expected<std::unique_ptr<ExpressionAST>>
-  parseNumericOperand(StringRef &Expr, AllowedOperand AO,
+  parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed,
                       Optional<size_t> LineNumber,
                       FileCheckPatternContext *Context, const SourceMgr &SM);
   /// Parses and updates \p RemainingExpr for a binary operation at line
index 0f98a2a..01959aa 100644 (file)
 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 operand format 'x'
-54
-55 BAD11: [[@LINE-1x]]
-56 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'
-57
+53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid operand format
+54 ERR10-NEXT: 52 {{B}}AD10: {{\[\[@LINE-x\]\]}}
+55 ERR10-NEXT:        {{^}}              ^{{$}}
+56
+57 BAD11: [[@LINE-1x]]
+58 ERR11: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'x'
+59
 ; RUN: %ProtectFileCheckOutput \
 ; RUN: not FileCheck -check-prefix BAD12 -input-file %s %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix ERR12 %s
-61
-62 BAD12: [[#@LINE-1]] NOT HERE
-63 ERR12: note: with "@LINE-1" equal to "61"
-64
+63
+64 BAD12: [[#@LINE-1]] NOT HERE
+65 ERR12: note: with "@LINE-1" equal to "63"
+66
 ; RUN: %ProtectFileCheckOutput \
 ; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
 ; RUN:   | FileCheck --check-prefix ERR13 %s
-68
-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
+70
+71 BAD13: [[@LINE-0xA]]
+72 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
+73
+74 CHECK: [[#@LINE]] CHECK
+75 CHECK: [[# @LINE]] CHECK
+76 CHECK: [[# @LINE ]] CHECK
+77
+78 CHECK: [[#@LINE-1]]
+79 CHECK: [[# @LINE-1]] CHECK
+80 CHECK: [[# @LINE -1]] CHECK
+81 CHECK: [[# @LINE - 1]] CHECK
+82 CHECK: [[# @LINE - 1 ]] CHECK
index aca8b9d..7859e20 100644 (file)
@@ -154,6 +154,54 @@ USE IMPL FMT IMPL MATCH UNSIGNED IMM
 CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
 CHECK-NEXT: [[#UNSI+0x8000000000000000]]
 
+; Numeric expressions in default matching format and explicit matching rule using
+; variables defined on other lines.
+USE DEF FMT EXPL MATCH  // CHECK-LABEL: USE DEF FMT EXPL MATCH
+11  // CHECK-NEXT: {{^}}[[#==UNSI]]
+11  // CHECK-NEXT: {{^}}[[# == UNSI]]
+12  // CHECK-NEXT: {{^}}[[#UNSI2: == UNSI + 1]]
+12  // CHECK-NEXT: {{^}}[[#==UNSI2]]
+
+; Numeric expressions in default matching format and explicit matching rule using
+; variable defined on other lines with match failure.
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefix NUMEXPR-CONSTRAINT-NOMATCH-MSG --strict-whitespace %s
+
+USE DEF FMT EXPL NO MATCH
+12
+NUMEXPR-CONSTRAINT-NOMATCH-LABEL: USE DEF FMT EXPL NO MATCH
+NUMEXPR-CONSTRAINT-NOMATCH-NEXT: [[#==UNSI]]
+NUMEXPR-CONSTRAINT-NOMATCH-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: expected string not found in input
+NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: {{\[\[#==UNSI\]\]}}
+NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT:   {{^}}                                 ^{{$}}
+
+; Empty numeric expression with matching constraint.
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck --check-prefix EMPTY-NUMEXPR-CONSTRAINT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefix EMPTY-NUMEXPR-CONSTRAINT-MSG --strict-whitespace %s
+
+EMPTY NUMEXPR USE WITH CONSTRAINT
+18
+EMPTY-NUMEXPR-CONSTRAINT-LABEL: EMPTY NUMEXPR USE WITH CONSTRAINT
+EMPTY-NUMEXPR-CONSTRAINT-NEXT: [[# ==]]
+EMPTY-NUMEXPR-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:38: error: empty numeric expression should not have a constraint
+EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMEXPR-CONSTRAINT-NEXT: {{\[\[# ==\]\]}}
+EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT:     {{^}}                                     ^{{$}}
+
+; Definition from empty numeric expression with matching constraint.
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck --check-prefix EMPTY-NUMDEF-CONSTRAINT --input-file %s %s 2>&1 \
+RUN:   | FileCheck --check-prefix EMPTY-NUMDEF-CONSTRAINT-MSG %s
+
+EMPTY NUMEXPR DEF WITH CONSTRAINT
+18
+EMPTY-NUMDEF-CONSTRAINT-LABEL: EMPTY NUMEXPR CONSTRAINT
+EMPTY-NUMDEF-CONSTRAINT-NEXT: [[#VARDEF: ==]]
+EMPTY-NUMDEF-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:44: error: empty numeric expression should not have a constraint
+EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMDEF-CONSTRAINT-NEXT: {{\[\[#VARDEF: ==\]\]}}
+EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT:     {{^}}                                           ^{{$}}
+
 ; Numeric expressions with matching format overriding the implicit format of
 ; variables defined on other lines.
 USE CONV FMT IMPL MATCH  // CHECK-LABEL: USE CONV FMT IMPL MATCH
index 6cdb5f1..3763130 100644 (file)
@@ -902,7 +902,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   Tester.initNextPattern();
 
   // Invalid variable name.
-  expectDiagnosticError("invalid operand format '%VAR'",
+  expectDiagnosticError("invalid matching constraint or operand format",
                         Tester.parseSubst("%VAR").takeError());
 
   expectDiagnosticError("invalid pseudo numeric variable '@FOO'",
@@ -937,6 +937,10 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   // Valid empty expression.
   EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded());
 
+  // Invalid equality matching constraint with empty expression.
+  expectDiagnosticError("empty numeric expression should not have a constraint",
+                        Tester.parseSubst("==").takeError());
+
   // Valid single operand expression.
   EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
   EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded());
@@ -947,6 +951,13 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
   EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)),
                        Succeeded());
 
+  // Valid optional matching constraint.
+  EXPECT_THAT_EXPECTED(Tester.parseSubst("==FOO"), Succeeded());
+
+  // Invalid matching constraint.
+  expectDiagnosticError("invalid matching constraint or operand format",
+                        Tester.parseSubst("+=FOO").takeError());
+
   // Invalid format.
   expectDiagnosticError("invalid matching format specification in expression",
                         Tester.parseSubst("X,FOO:").takeError());
@@ -968,12 +979,12 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
 
   // Errors in RHS operand are bubbled up by parseBinop() to
   // parseNumericSubstitutionBlock().
-  expectDiagnosticError("invalid operand format '%VAR'",
+  expectDiagnosticError("invalid operand format",
                         Tester.parseSubst("@LINE+%VAR").takeError());
 
   // Invalid legacy @LINE expression with non literal rhs.
   expectDiagnosticError(
-      "invalid operand format '@LINE'",
+      "invalid operand format",
       Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());
 
   // Invalid legacy @LINE expression made of a single literal.
@@ -1046,7 +1057,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
 
   // Test more closing than opening parentheses. The diagnostic messages are
   // not ideal, but for now simply check that we reject invalid input.
-  expectDiagnosticError("invalid operand format ')'",
+  expectDiagnosticError("invalid matching constraint or operand format",
                         Tester.parseSubst(")").takeError());
   expectDiagnosticError("unsupported operation ')'",
                         Tester.parseSubst("1)").takeError());