1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[FileCheck] Add function call support to numerical expressions.

This patch extends numerical expressions to allow calls to
predefined functions. These calls can be combined with the
existing numerical operators, which includes nesting calls.

The call syntax is:

  <func>(<args>)

Where <func> is a predefined string literal, currently limited to
one of add, max, min and sub. <arg> is a comma seperated list of
numerical expressions.

Subscribers: arichardson, hiraditya, thopre, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D79936
This commit is contained in:
Paul Walker 2020-05-14 10:32:58 +00:00
parent 612fb0f532
commit a8c59c6124
5 changed files with 338 additions and 21 deletions

View File

@ -686,12 +686,27 @@ The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
* 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 and have a 64-bit precision. The supported operators are ``+`` and
``-``. Spaces are accepted before, after and between any of these elements.
Overflow and underflow are rejected. There is currently no support for
operator precendence, but parentheses can be used to change the evaluation
order.
A numeric operand is a previously defined numeric variable, an integer
literal, or a function. Spaces are accepted before, after and between any of
these elements. Numeric operands have 64-bit precision. Overflow and underflow
are rejected. There is no support for operator precendence, but parentheses
can be used to change the evaluation order.
The supported operators are:
* ``+`` - Returns the sum of its two operands.
* ``-`` - Returns the difference of its two operands.
The syntax of a function call is ``<name>(<arguments>)`` where:
* ``name`` is a predefined string literal. Accepted values are:
* add - Returns the sum of its two operands.
* max - Returns the largest of its two operands.
* min - Returns the smallest of its two operands.
* sub - Returns the difference of its two operands.
* ``<arguments>`` is a comma seperated list of expressions.
For example:

View File

@ -230,6 +230,34 @@ Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand,
}
}
Expected<ExpressionValue> llvm::max(const ExpressionValue &LeftOperand,
const ExpressionValue &RightOperand) {
if (LeftOperand.isNegative() && RightOperand.isNegative()) {
int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
int64_t RightValue = cantFail(RightOperand.getSignedValue());
return ExpressionValue(std::max(LeftValue, RightValue));
}
if (!LeftOperand.isNegative() && !RightOperand.isNegative()) {
uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
return ExpressionValue(std::max(LeftValue, RightValue));
}
if (LeftOperand.isNegative())
return RightOperand;
return LeftOperand;
}
Expected<ExpressionValue> llvm::min(const ExpressionValue &LeftOperand,
const ExpressionValue &RightOperand) {
if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand)
return RightOperand;
return LeftOperand;
}
Expected<ExpressionValue> NumericVariableUse::eval() const {
Optional<ExpressionValue> Value = Variable->getValue();
if (Value)
@ -309,23 +337,20 @@ Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
if (Str.empty())
return ErrorDiagnostic::get(SM, Str, "empty variable name");
bool ParsedOneChar = false;
unsigned I = 0;
size_t I = 0;
bool IsPseudo = Str[0] == '@';
// Global vars start with '$'.
if (Str[0] == '$' || IsPseudo)
++I;
for (unsigned E = Str.size(); I != E; ++I) {
if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
return ErrorDiagnostic::get(SM, Str, "invalid variable name");
if (!isValidVarNameStart(Str[I++]))
return ErrorDiagnostic::get(SM, Str, "invalid variable name");
for (size_t E = Str.size(); I != E; ++I)
// Variable names are composed of alphanumeric characters and underscores.
if (Str[I] != '_' && !isAlnum(Str[I]))
break;
ParsedOneChar = true;
}
StringRef Name = Str.take_front(I);
Str = Str.substr(I);
@ -436,10 +461,22 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
// Try to parse as a numeric variable use.
Expected<Pattern::VariableProperties> ParseVarResult =
parseVariable(Expr, SM);
if (ParseVarResult)
if (ParseVarResult) {
// Try to parse a function call.
if (Expr.ltrim(SpaceChars).startswith("(")) {
if (AO != AllowedOperand::Any)
return ErrorDiagnostic::get(SM, ParseVarResult->Name,
"unexpected function call");
return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context,
SM);
}
return parseNumericVariableUse(ParseVarResult->Name,
ParseVarResult->IsPseudo, LineNumber,
Context, SM);
}
if (AO == AllowedOperand::LineVar)
return ParseVarResult.takeError();
// Ignore the error and retry parsing as a literal.
@ -540,6 +577,79 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
std::move(*RightOpResult));
}
Expected<std::unique_ptr<ExpressionAST>>
Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
Expr = Expr.ltrim(SpaceChars);
assert(Expr.startswith("("));
auto OptFunc = StringSwitch<Optional<binop_eval_t>>(FuncName)
.Case("add", operator+)
.Case("max", max)
.Case("min", min)
.Case("sub", operator-)
.Default(None);
if (!OptFunc)
return ErrorDiagnostic::get(
SM, FuncName, Twine("call to undefined function '") + FuncName + "'");
Expr.consume_front("(");
Expr = Expr.ltrim(SpaceChars);
// Parse call arguments, which are comma separated.
SmallVector<std::unique_ptr<ExpressionAST>, 4> Args;
while (!Expr.empty() && !Expr.startswith(")")) {
if (Expr.startswith(","))
return ErrorDiagnostic::get(SM, Expr, "missing argument");
// Parse the argument, which is an arbitary expression.
StringRef OuterBinOpExpr = Expr;
Expected<std::unique_ptr<ExpressionAST>> Arg =
parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
while (Arg && !Expr.empty()) {
Expr = Expr.ltrim(SpaceChars);
// Have we reached an argument terminator?
if (Expr.startswith(",") || Expr.startswith(")"))
break;
// Arg = Arg <op> <expr>
Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber,
Context, SM);
}
// Prefer an expression error over a generic invalid argument message.
if (!Arg)
return Arg.takeError();
Args.push_back(std::move(*Arg));
// Have we parsed all available arguments?
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front(","))
break;
Expr = Expr.ltrim(SpaceChars);
if (Expr.startswith(")"))
return ErrorDiagnostic::get(SM, Expr, "missing argument");
}
if (!Expr.consume_front(")"))
return ErrorDiagnostic::get(SM, Expr,
"missing ')' at end of call expression");
const unsigned NumArgs = Args.size();
if (NumArgs == 2)
return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]),
std::move(Args[1]));
// TODO: Support more than binop_eval_t.
return ErrorDiagnostic::get(SM, FuncName,
Twine("function '") + FuncName +
Twine("' takes 2 arguments but ") +
Twine(NumArgs) + " given");
}
Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
@ -549,9 +659,10 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
DefinedNumericVariable = None;
ExpressionFormat ExplicitFormat = ExpressionFormat();
// Parse format specifier.
// Parse format specifier (NOTE: ',' is also an argument seperator).
size_t FormatSpecEnd = Expr.find(',');
if (FormatSpecEnd != StringRef::npos) {
size_t FunctionStart = Expr.find('(');
if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front("%"))
return ErrorDiagnostic::get(

View File

@ -152,6 +152,10 @@ Expected<ExpressionValue> operator+(const ExpressionValue &Lhs,
const ExpressionValue &Rhs);
Expected<ExpressionValue> operator-(const ExpressionValue &Lhs,
const ExpressionValue &Rhs);
Expected<ExpressionValue> max(const ExpressionValue &Lhs,
const ExpressionValue &Rhs);
Expected<ExpressionValue> min(const ExpressionValue &Lhs,
const ExpressionValue &Rhs);
/// Base class representing the AST of a given expression.
class ExpressionAST {
@ -722,10 +726,10 @@ private:
FileCheckPatternContext *Context, const SourceMgr &SM);
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
/// \p Context points to the class instance holding the live string and
/// numeric variables. \returns the class representing that operand in the
/// 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.
@ -757,6 +761,18 @@ private:
static Expected<std::unique_ptr<ExpressionAST>>
parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
/// Parses \p Expr for an argument list belonging to a call to function \p
/// FuncName at line \p LineNumber, or before input is parsed if \p LineNumber
/// is None. Parameter \p FuncLoc is the source location used for diagnostics.
/// Parameter \p Context points to the class instance holding the live string
/// and numeric variables. \returns the class representing that call in the
/// AST of the expression or an error holding a diagnostic against \p SM
/// otherwise.
static Expected<std::unique_ptr<ExpressionAST>>
parseCallExpr(StringRef &Expr, StringRef FuncName,
Optional<size_t> LineNumber, FileCheckPatternContext *Context,
const SourceMgr &SM);
};
//===----------------------------------------------------------------------===//

View File

@ -55,20 +55,38 @@ USE EXPL FMT IMPL MATCH // CHECK-LABEL: USE EXPL FMT IMPL MATCH
11 // CHECK-NEXT: {{^}}[[#%u,UNSI]]
12 // CHECK-NEXT: {{^}}[[#%u,UNSI+1]]
10 // CHECK-NEXT: {{^}}[[#%u,UNSI-1]]
15 // CHECK-NEXT: {{^}}[[#%u,add(UNSI,4)]]
11 // CHECK-NEXT: {{^}}[[#%u,max(UNSI,7)]]
99 // CHECK-NEXT: {{^}}[[#%u,max(UNSI,99)]]
7 // CHECK-NEXT: {{^}}[[#%u,min(UNSI,7)]]
11 // CHECK-NEXT: {{^}}[[#%u,min(UNSI,99)]]
8 // CHECK-NEXT: {{^}}[[#%u,sub(UNSI,3)]]
c // CHECK-NEXT: {{^}}[[#%x,LHEX]]
d // CHECK-NEXT: {{^}}[[#%x,LHEX+1]]
b // CHECK-NEXT: {{^}}[[#%x,LHEX-1]]
1a // CHECK-NEXT: {{^}}[[#%x,LHEX+0xe]]
1a // CHECK-NEXT: {{^}}[[#%x,LHEX+0xE]]
e // CHECK-NEXT: {{^}}[[#%x,add(LHEX,2)]]
ff // CHECK-NEXT: {{^}}[[#%x,max(LHEX,0xff)]]
a // CHECK-NEXT: {{^}}[[#%x,min(LHEX,0xa)]]
a // CHECK-NEXT: {{^}}[[#%x,sub(LHEX,2)]]
D // CHECK-NEXT: {{^}}[[#%X,UHEX]]
E // CHECK-NEXT: {{^}}[[#%X,UHEX+1]]
C // CHECK-NEXT: {{^}}[[#%X,UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xE]]
F // CHECK-NEXT: {{^}}[[#%X,add(UHEX,2)]]
FF // CHECK-NEXT: {{^}}[[#%X,max(UHEX,0xff)]]
A // CHECK-NEXT: {{^}}[[#%X,min(UHEX,0xa)]]
B // CHECK-NEXT: {{^}}[[#%X,sub(UHEX,2)]]
-30 // CHECK-NEXT: {{^}}[[#%d,SIGN]]
-29 // CHECK-NEXT: {{^}}[[#%d,SIGN+1]]
-31 // CHECK-NEXT: {{^}}[[#%d,SIGN-1]]
42 // CHECK-NEXT: {{^}}[[#%d,SIGN+72]]
-29 // CHECK-NEXT: {{^}}[[#%d,add(SIGN,1)]]
-17 // CHECK-NEXT: {{^}}[[#%d,max(SIGN,-17)]]
-30 // CHECK-NEXT: {{^}}[[#%d,min(SIGN,-17)]]
-31 // CHECK-NEXT: {{^}}[[#%d,sub(SIGN,1)]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIa]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIb]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIc]]
@ -92,6 +110,15 @@ USE EXPL FMT IMPL MATCH SPC // CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
10 // CHECK-NEXT: {{^}}[[# %u , UNSI -1]]
10 // CHECK-NEXT: {{^}}[[# %u , UNSI - 1]]
10 // CHECK-NEXT: {{^}}[[# %u , UNSI - 1 ]]
13 // CHECK-NEXT: {{^}}[[#%u, add(UNSI,2)]]
13 // CHECK-NEXT: {{^}}[[# %u, add(UNSI,2)]]
13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI,2)]]
13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI, 2)]]
13 // CHECK-NEXT: {{^}}[[# %u , add( UNSI, 2)]]
13 // CHECK-NEXT: {{^}}[[# %u , add( UNSI,2)]]
13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI,2) ]]
13 // CHECK-NEXT: {{^}}[[# %u , add (UNSI,2)]]
104 // CHECK-NEXT: {{^}}[[# %u , UNSI + sub( add (100 , UNSI+ 1 ), 20) +1 ]]
; Numeric expressions in implicit matching format and default matching rule using
; variables defined on other lines.
@ -99,16 +126,22 @@ USE IMPL FMT IMPL MATCH // CHECK-LABEL: USE IMPL FMT IMPL MATCH
11 // CHECK-NEXT: {{^}}[[#UNSI]]
12 // CHECK-NEXT: {{^}}[[#UNSI+1]]
10 // CHECK-NEXT: {{^}}[[#UNSI-1]]
99 // CHECK-NEXT: {{^}}[[#max(UNSI,99)]]
7 // CHECK-NEXT: {{^}}[[#min(UNSI,7)]]
c // CHECK-NEXT: {{^}}[[#LHEX]]
d // CHECK-NEXT: {{^}}[[#LHEX+1]]
b // CHECK-NEXT: {{^}}[[#LHEX-1]]
1a // CHECK-NEXT: {{^}}[[#LHEX+0xe]]
1a // CHECK-NEXT: {{^}}[[#LHEX+0xE]]
ff // CHECK-NEXT: {{^}}[[#max(LHEX,255)]]
a // CHECK-NEXT: {{^}}[[#min(LHEX,10)]]
D // CHECK-NEXT: {{^}}[[#UHEX]]
E // CHECK-NEXT: {{^}}[[#UHEX+1]]
C // CHECK-NEXT: {{^}}[[#UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xE]]
FF // CHECK-NEXT: {{^}}[[#max(UHEX,255)]]
A // CHECK-NEXT: {{^}}[[#min(UHEX,10)]]
-30 // CHECK-NEXT: {{^}}[[#SIGN]]
-29 // CHECK-NEXT: {{^}}[[#SIGN+1]]
-31 // CHECK-NEXT: {{^}}[[#SIGN-1]]
@ -367,3 +400,51 @@ UNDERFLOW-NEXT: TINYVAR: [[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000]]
UNDERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:29: error: unable to substitute variable or numeric expression
UNDERFLOW-MSG-NEXT: {{U}}NDERFLOW-NEXT: TINYVAR: {{\[\[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000\]\]}}
UNDERFLOW-MSG-NEXT: {{^}} ^{{$}}
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-MISSING-CLOSING-BRACKET --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CALL-MISSING-CLOSING-BRACKET-MSG %s
CALL MISSING CLOSING BRACKET
30
CALL-MISSING-CLOSING-BRACKET-LABEL: CALL MISSING CLOSING BRACKET
CALL-MISSING-CLOSING-BRACKET-NEXT: [[#add(NUMVAR,3]]
CALL-MISSING-CLOSING-BRACKET-MSG: numeric-expression.txt:[[#@LINE-1]]:51: error: missing ')' at end of call expression
CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{C}}ALL-MISSING-CLOSING-BRACKET-NEXT: {{\[\[#add\(NUMVAR,3\]\]}}
CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{^}} ^{{$}}
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-MISSING-ARGUMENT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CALL-MISSING-ARGUMENT-MSG %s
CALL MISSING ARGUMENT
30
CALL-MISSING-ARGUMENT-LABEL: CALL MISSING ARGUMENT
CALL-MISSING-ARGUMENT-NEXT: [[#add(NUMVAR,)]]
CALL-MISSING-ARGUMENT-MSG: numeric-expression.txt:[[#@LINE-1]]:43: error: missing argument
CALL-MISSING-ARGUMENT-MSG-NEXT: {{C}}ALL-MISSING-ARGUMENT-NEXT: {{\[\[#add\(NUMVAR,\)\]\]}}
CALL-MISSING-ARGUMENT-MSG-NEXT: {{^}} ^{{$}}
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-WRONG-ARGUMENT-COUNT --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CALL-WRONG-ARGUMENT-COUNT-MSG %s
CALL WRONG ARGUMENT COUNT
30
CALL-WRONG-ARGUMENT-COUNT-LABEL: CALL WRONG ARGUMENT COUNT
CALL-WRONG-ARGUMENT-COUNT-NEXT: [[#add(NUMVAR)]]
CALL-WRONG-ARGUMENT-COUNT-MSG: numeric-expression.txt:[[#@LINE-1]]:36: error: function 'add' takes 2 arguments but 1 given
CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{C}}ALL-WRONG-ARGUMENT-COUNT-NEXT: {{\[\[#add\(NUMVAR\)\]\]}}
CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{^}} ^{{$}}
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-UNDEFINED-FUNCTION --input-file %s %s 2>&1 \
RUN: | FileCheck --strict-whitespace --check-prefix CALL-UNDEFINED-FUNCTION-MSG %s
CALL UNDEFINED FUNCTION
30
CALL-UNDEFINED-FUNCTION-LABEL: CALL UNDEFINED FUNCTION
CALL-UNDEFINED-FUNCTION-NEXT: [[#bogus_function(NUMVAR)]]
CALL-UNDEFINED-FUNCTION-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: call to undefined function 'bogus_function'
CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{C}}ALL-UNDEFINED-FUNCTION-NEXT: {{\[\[#bogus_function\(NUMVAR\)\]\]}}
CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{^}} ^{{$}}

View File

@ -812,7 +812,7 @@ private:
public:
PatternTester() {
std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ"};
std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ", "#add=7"};
// An ASSERT_FALSE would make more sense but cannot be used in a
// constructor.
EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
@ -1056,6 +1056,60 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
Tester.parseSubst("(2))").takeError());
expectDiagnosticError("unsupported operation ')'",
Tester.parseSubst("(1))(").takeError());
// Valid expression with function call.
EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO,3)"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("add (FOO,3)"), Succeeded());
// Valid expression with nested function call.
EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min(BAR,10))"), Succeeded());
// Valid expression with function call taking expression as argument.
EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, (BAR+10) + 3)"),
Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min (BAR,10) + 3)"),
Succeeded());
// Valid expression with variable named the same as a function.
EXPECT_THAT_EXPECTED(Tester.parseSubst("add"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("add+FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+add"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("add(add,add)+add"), Succeeded());
// Malformed call syntax.
expectDiagnosticError("missing ')' at end of call expression",
Tester.parseSubst("add(FOO,(BAR+7)").takeError());
expectDiagnosticError("missing ')' at end of call expression",
Tester.parseSubst("add(FOO,min(BAR,7)").takeError());
expectDiagnosticError("missing argument",
Tester.parseSubst("add(FOO,)").takeError());
expectDiagnosticError("missing argument",
Tester.parseSubst("add(,FOO)").takeError());
expectDiagnosticError("missing argument",
Tester.parseSubst("add(FOO,,3)").takeError());
// Valid call, but to an unknown function.
expectDiagnosticError("call to undefined function 'bogus_function'",
Tester.parseSubst("bogus_function(FOO,3)").takeError());
expectDiagnosticError("call to undefined function '@add'",
Tester.parseSubst("@add(2,3)").takeError());
expectDiagnosticError("call to undefined function '$add'",
Tester.parseSubst("$add(2,3)").takeError());
expectDiagnosticError("call to undefined function 'FOO'",
Tester.parseSubst("FOO(2,3)").takeError());
expectDiagnosticError("call to undefined function 'FOO'",
Tester.parseSubst("FOO (2,3)").takeError());
// Valid call, but with incorrect argument count.
expectDiagnosticError("function 'add' takes 2 arguments but 1 given",
Tester.parseSubst("add(FOO)").takeError());
expectDiagnosticError("function 'add' takes 2 arguments but 3 given",
Tester.parseSubst("add(FOO,3,4)").takeError());
// Valid call, but not part of a valid expression.
expectDiagnosticError("unsupported operation 'a'",
Tester.parseSubst("2add(FOO,2)").takeError());
expectDiagnosticError("unsupported operation 'a'",
Tester.parseSubst("FOO add(FOO,2)").takeError());
expectDiagnosticError("unsupported operation 'a'",
Tester.parseSubst("add(FOO,2)add(FOO,2)").takeError());
}
TEST_F(FileCheckTest, ParsePattern) {
@ -1229,6 +1283,46 @@ TEST_F(FileCheckTest, MatchParen) {
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
}
TEST_F(FileCheckTest, MatchBuiltinFunctions) {
PatternTester Tester;
// Esnure #NUMVAR has the expected value.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
expectNotFoundError(Tester.match("FAIL").takeError());
expectNotFoundError(Tester.match("").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
// Check each builtin function generates the expected result.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#add(NUMVAR,13)]]"));
EXPECT_THAT_EXPECTED(Tester.match("31"), Succeeded());
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#sub(NUMVAR,7)]]"));
EXPECT_THAT_EXPECTED(Tester.match("11"), Succeeded());
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,5)]]"));
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,99)]]"));
EXPECT_THAT_EXPECTED(Tester.match("99"), Succeeded());
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,5)]]"));
EXPECT_THAT_EXPECTED(Tester.match("5"), Succeeded());
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,99)]]"));
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
// Check nested function calls.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#add(min(7,2),max(4,10))]]"));
EXPECT_THAT_EXPECTED(Tester.match("12"), Succeeded());
// Check function call that uses a variable of the same name.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#add(add,add)+min (add,3)+add]]"));
EXPECT_THAT_EXPECTED(Tester.match("24"), Succeeded());
}
TEST_F(FileCheckTest, Substitution) {
SourceMgr SM;
FileCheckPatternContext Context;