checker: allow omitting `{}` in trailing struct args (#10832)

pull/10841/head
spaceface 2021-07-17 14:44:44 +02:00 committed by GitHub
parent ec47cda386
commit 22fcbe70a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 26 deletions

View File

@ -1964,6 +1964,47 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, call_exp
} }
} }
pub fn (mut c Checker) check_expected_arg_count(mut call_expr ast.CallExpr, f &ast.Fn) ? {
nr_args := call_expr.args.len
nr_params := if call_expr.is_method && f.params.len > 0 {
f.params.len - 1
} else {
f.params.len
}
mut min_required_params := f.params.len
if call_expr.is_method {
min_required_params--
}
if f.is_variadic {
min_required_params--
}
if min_required_params < 0 {
min_required_params = 0
}
if nr_args < min_required_params {
if min_required_params == nr_args + 1 {
last_typ := f.params.last().typ
last_sym := c.table.get_type_symbol(last_typ)
if last_sym.kind == .struct_ {
// allow empty trailing struct syntax arg (`f()` where `f` is `fn(ConfigStruct)`)
call_expr.args << {
expr: ast.StructInit{
typ: last_typ
}
typ: last_typ
}
return
}
}
c.error('expected $min_required_params arguments, but got $nr_args', call_expr.pos)
return error('')
} else if !f.is_variadic && nr_args > nr_params {
unexpected_args_pos := call_expr.args[min_required_params].pos.extend(call_expr.args.last().pos)
c.error('expected $min_required_params arguments, but got $nr_args', unexpected_args_pos)
return error('')
}
}
pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
left_type := c.expr(call_expr.left) left_type := c.expr(call_expr.left)
c.expected_type = left_type c.expected_type = left_type
@ -2124,21 +2165,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
if method.return_type == ast.void_type && method.is_conditional && method.ctdefine_idx != -1 { if method.return_type == ast.void_type && method.is_conditional && method.ctdefine_idx != -1 {
call_expr.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut method.attrs[method.ctdefine_idx]) call_expr.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut method.attrs[method.ctdefine_idx])
} }
nr_args := if method.params.len == 0 { 0 } else { method.params.len - 1 } c.check_expected_arg_count(mut call_expr, method) or { return method.return_type }
min_required_args := method.params.len - if method.is_variadic && method.params.len > 1 {
2
} else {
1
}
if call_expr.args.len < min_required_args {
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
call_expr.pos)
} else if !method.is_variadic && call_expr.args.len > nr_args {
unexpected_arguments := call_expr.args[min_required_args..]
unexpected_arguments_pos := unexpected_arguments[0].pos.extend(unexpected_arguments.last().pos)
c.error('expected $nr_args arguments, but got $call_expr.args.len', unexpected_arguments_pos)
return method.return_type
}
mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods
mut param_is_mut := false mut param_is_mut := false
mut no_type_promotion := false mut no_type_promotion := false
@ -2696,17 +2723,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
} }
// dont check number of args for JS functions since arguments are not required // dont check number of args for JS functions since arguments are not required
if call_expr.language != .js { if call_expr.language != .js {
min_required_args := if func.is_variadic { func.params.len - 1 } else { func.params.len } c.check_expected_arg_count(mut call_expr, func) or { return func.return_type }
if call_expr.args.len < min_required_args {
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
call_expr.pos)
} else if !func.is_variadic && call_expr.args.len > func.params.len {
unexpected_arguments := call_expr.args[min_required_args..]
unexpected_arguments_pos := unexpected_arguments[0].pos.extend(unexpected_arguments.last().pos)
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
unexpected_arguments_pos)
return func.return_type
}
} }
// println / eprintln / panic can print anything // println / eprintln / panic can print anything
if fn_name in ['println', 'print', 'eprintln', 'eprint', 'panic'] && call_expr.args.len > 0 { if fn_name in ['println', 'print', 'eprintln', 'eprint', 'panic'] && call_expr.args.len > 0 {

View File

@ -0,0 +1,24 @@
struct Foo {
x int
}
fn foo(f Foo) int {
return f.x
}
struct Bar {
x int
y int = 1234
}
fn bar(b Bar) Bar {
return b
}
fn test_missing_config_struct_arg() {
assert foo() == 0
assert bar() == {
x: 0
y: 1234
}
}