checker: disallow structs with @[params] attribute as mutable function parameters (#21206)

This commit is contained in:
Turiiya 2024-05-04 16:09:45 +02:00 committed by GitHub
parent 206441c9df
commit f0abc452fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 16 deletions

View file

@ -254,6 +254,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.error('generic struct `${pure_sym_name}` in fn declaration must specify the generic type names, e.g. ${pure_sym_name}[T]', c.error('generic struct `${pure_sym_name}` in fn declaration must specify the generic type names, e.g. ${pure_sym_name}[T]',
param.type_pos) param.type_pos)
} }
if param.is_mut && arg_typ_sym.info.attrs.any(it.name == 'params') {
c.error('declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed',
param.type_pos)
}
} else if arg_typ_sym.info is ast.Interface { } else if arg_typ_sym.info is ast.Interface {
if arg_typ_sym.info.generic_types.len > 0 && !param.typ.has_flag(.generic) if arg_typ_sym.info.generic_types.len > 0 && !param.typ.has_flag(.generic)
&& arg_typ_sym.info.concrete_types.len == 0 { && arg_typ_sym.info.concrete_types.len == 0 {
@ -1278,8 +1282,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
} else { } else {
if param.is_mut { if param.is_mut {
tok := param.specifier() tok := param.specifier()
c.error('function `${node.name}` parameter `${param.name}` is `${tok}`, so use `${tok} ${call_arg.expr}` instead', param_sym := c.table.sym(param.typ)
call_arg.expr.pos()) if !(param_sym.info is ast.Struct && param_sym.info.attrs.any(it.name == 'params')) {
c.error('function `${node.name}` parameter `${param.name}` is `${tok}`, so use `${tok} ${call_arg.expr}` instead',
call_arg.expr.pos())
}
} else { } else {
c.fail_if_unreadable(call_arg.expr, arg_typ, 'argument') c.fail_if_unreadable(call_arg.expr, arg_typ, 'argument')
} }

View file

@ -0,0 +1,7 @@
vlib/v/checker/tests/mut_parms_struct_param_err.vv:8:17: error: declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed
6 | }
7 |
8 | fn foo(mut opts Params) bool {
| ~~~~~~
9 | return opts.a
10 | }

View file

@ -0,0 +1,12 @@
@[params]
struct Params {
mut:
a bool
x int
}
fn foo(mut opts Params) bool {
return opts.a
}
foo(a: true)

View file

@ -25,7 +25,8 @@ struct Lol {
struct User { struct User {
name string name string
age int mut:
age int
} }
struct Foo { struct Foo {
@ -257,14 +258,12 @@ fn bar_config(c Config, def int) {
assert c.def == def assert c.def == def
} }
fn mut_bar_config(mut c Config, def int) &Config {
c.n = c.def
assert c.n == def
return unsafe { c }
}
fn foo_user(u User) {} fn foo_user(u User) {}
fn foo_mut_user(mut u User) {
u.age++
}
fn test_struct_literal_args() { fn test_struct_literal_args() {
foo_config(20, foo_config(20,
n: 10 n: 10
@ -277,17 +276,14 @@ fn test_struct_literal_args() {
bar_config(Config{}, 10) bar_config(Config{}, 10)
bar_config(Config{ def: 4 }, 4) bar_config(Config{ def: 4 }, 4)
mut c_ := Config{
def: 10
}
c := mut_bar_config(mut c_, 10)
assert c.n == 10
assert c.def == 10
foo_user(name: 'Peter') foo_user(name: 'Peter')
foo_user(name: 'Peter') foo_user(name: 'Peter')
foo_user(age: 7) foo_user(age: 7)
foo_user(name: 'Stew', age: 50) foo_user(name: 'Stew', age: 50)
mut user := User{'Stew', 50}
foo_mut_user(mut user)
assert user.age == 51
} }
struct City { struct City {