diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -670,7 +670,8 @@ 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 ``[[#%,]]`` where: +The syntax of a numeric substitution is +``[[#%: ]]`` where: * ``%`` 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 @@ is used. In case of conflict between matching formats of several numeric variables the format specifier is mandatory. +* ```` 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 + ```` is not provided. No matching constraint must be specified + when the ```` is empty. + * ```` is an expression. An expression is in turn recursively defined as: @@ -747,11 +754,12 @@ 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 ``[[#%,: ]]`` -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 +``[[#%,: ]]`` 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 diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -448,8 +448,9 @@ } Expected> Pattern::parseNumericOperand( - StringRef &Expr, AllowedOperand AO, Optional LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM) { + StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint, + Optional LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM) { if (Expr.startswith("(")) { if (AO != AllowedOperand::Any) return ErrorDiagnostic::get( @@ -497,8 +498,11 @@ return std::make_unique(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> @@ -514,8 +518,9 @@ return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); // Note: parseNumericOperand handles nested opening parentheses. - Expected> SubExprResult = - parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM); + Expected> 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 @@ AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; Expected> RightOpResult = - parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM); + parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false, + LineNumber, Context, SM); if (!RightOpResult) return RightOpResult; @@ -606,8 +612,9 @@ // Parse the argument, which is an arbitary expression. StringRef OuterBinOpExpr = Expr; - Expected> Arg = - parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM); + Expected> 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 @@ 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> ParseResult = - parseNumericOperand(Expr, AO, LineNumber, Context, SM); + Expected> ParseResult = parseNumericOperand( + Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM); while (ParseResult && !Expr.empty()) { ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), IsLegacyLineExpr, LineNumber, Context, SM); diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -728,13 +728,14 @@ /// 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> - parseNumericOperand(StringRef &Expr, AllowedOperand AO, + parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); /// Parses and updates \p RemainingExpr for a binary operation at line diff --git a/llvm/test/FileCheck/line-count.txt b/llvm/test/FileCheck/line-count.txt --- a/llvm/test/FileCheck/line-count.txt +++ b/llvm/test/FileCheck/line-count.txt @@ -50,31 +50,33 @@ 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 diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -154,6 +154,54 @@ 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 diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -902,7 +902,7 @@ 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 @@ // 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 @@ 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 @@ // 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 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());