Skip to content

Commit

Permalink
checker: improved notification messages when struct ref fields are un…
Browse files Browse the repository at this point in the history
…initialized(fix #20245)
  • Loading branch information
shove70 committed Dec 22, 2023
1 parent 06a536e commit 0fe5420
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 67 deletions.
17 changes: 9 additions & 8 deletions vlib/v/checker/containers.v
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
node.pos)
}
// `&Struct{} check
if node.has_len {
if node.has_len && !c.inside_unsafe {
c.check_elements_ref_fields_initialized(node.elem_type, node.pos)
}
return node.typ
Expand All @@ -116,7 +116,9 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
c.warn('fixed arrays of references need to be initialized right away (unless inside `unsafe`)',
node.pos)
}
c.check_elements_ref_fields_initialized(node.elem_type, node.pos)
if !c.inside_unsafe {
c.check_elements_ref_fields_initialized(node.elem_type, node.pos)
}
}
// `a = []`
if node.exprs.len == 0 {
Expand Down Expand Up @@ -405,7 +407,9 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
c.ensure_type_exists(info.value_type, node.pos)
node.key_type = info.key_type
node.value_type = info.value_type
c.check_elements_ref_fields_initialized(node.typ, node.pos)
if !c.inside_unsafe {
c.check_elements_ref_fields_initialized(node.typ, node.pos)
}
return node.typ
}

Expand Down Expand Up @@ -508,7 +512,7 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {

// check the element, and its children for ref uninitialized fields
fn (mut c Checker) check_elements_ref_fields_initialized(typ ast.Type, pos &token.Pos) {
if typ == 0 || c.inside_const {
if typ == 0 || c.inside_const || c.inside_unsafe {
return
}
sym := c.table.sym(typ)
Expand All @@ -520,10 +524,7 @@ fn (mut c Checker) check_elements_ref_fields_initialized(typ ast.Type, pos &toke
fn (mut c Checker) do_check_elements_ref_fields_initialized(sym &ast.TypeSymbol, mut checked_types []ast.Type, pos &token.Pos) {
if sym.info is ast.Struct {
linked_name := sym.name
// For now, let's call this method and give a notice instead of an error.
// After some time, we remove the check_ref_fields_initialized_note() method and
// simply call check_ref_fields_initialized()
c.check_ref_fields_initialized_note(sym, mut checked_types, linked_name, pos)
c.check_ref_fields_initialized(sym, mut checked_types, linked_name, pos)
return
}
match sym.info {
Expand Down
52 changes: 2 additions & 50 deletions vlib/v/checker/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
node.pos)
continue
}
if !field.typ.has_flag(.option) {
if !field.typ.has_flag(.option) && !c.inside_unsafe {
if sym.kind == .struct_ {
c.check_ref_fields_initialized(sym, mut checked_types, '${type_sym.name}.${field.name}',
node.pos)
Expand Down Expand Up @@ -943,55 +943,7 @@ fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.typ.has_flag(.option)
&& !field.has_default_expr {
c.error('reference field `${linked_name}.${field.name}` must be initialized (part of struct `${struct_sym.name}`)',
pos)
continue
}
if sym.kind == .struct_ {
if sym.language == .c && (sym.info as ast.Struct).is_typedef {
continue
}
if field.typ in checked_types {
continue
}
checked_types << field.typ
c.check_ref_fields_initialized(sym, mut checked_types, '${linked_name}.${field.name}',
pos)
} else if sym.kind == .alias {
psym := c.table.sym((sym.info as ast.Alias).parent_type)
if psym.kind == .struct_ {
checked_types << field.typ
c.check_ref_fields_initialized(psym, mut checked_types, '${linked_name}.${field.name}',
pos)
}
}
}
}

// Recursively check whether the struct type field is initialized
// NOTE:
// This method is temporary and will only be called by the do_check_elements_ref_fields_initialized() method.
// The goal is to give only a notice, not an error, for now. After a while,
// when we change the notice to error, we can remove this temporary method.
fn (mut c Checker) check_ref_fields_initialized_note(struct_sym &ast.TypeSymbol, mut checked_types []ast.Type, linked_name string, pos &token.Pos) {
if c.pref.translated || c.file.is_translated {
return
}
if struct_sym.kind == .struct_ && struct_sym.language == .c
&& (struct_sym.info as ast.Struct).is_typedef {
return
}
fields := c.table.struct_fields(struct_sym)
for field in fields {
sym := c.table.sym(field.typ)
if field.name.len > 0 && field.name[0].is_capital() && sym.info is ast.Struct
&& sym.language == .v {
// an embedded struct field
continue
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.typ.has_flag(.option)
&& !field.has_default_expr {
c.note('reference field `${linked_name}.${field.name}` must be initialized (part of struct `${struct_sym.name}`)',
c.error('reference field `${linked_name}.${field.name}` must be initialized (part of struct `${struct_sym.name}`), or put it inside `unsafe{}` blocks',
pos)
continue
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:8:6: notice: reference field `Foo.n` must be initialized (part of struct `Foo`)
6 |
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:8:6: error: reference field `Foo.n` must be initialized (part of struct `Foo`), or put it inside `unsafe{}` blocks
6 |
7 | fn main() {
8 | _ = []Foo{len: 1}
| ~~~~~~
9 | _ = [1]Foo{}
10 | _ = map[string]Foo{}
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:9:6: notice: reference field `Foo.n` must be initialized (part of struct `Foo`)
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:9:6: error: reference field `Foo.n` must be initialized (part of struct `Foo`), or put it inside `unsafe{}` blocks
7 | fn main() {
8 | _ = []Foo{len: 1}
9 | _ = [1]Foo{}
| ~~~~~~~~
10 | _ = map[string]Foo{}
11 | _ = map[string][]Foo{}
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:10:6: notice: reference field `Foo.n` must be initialized (part of struct `Foo`)
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:10:6: error: reference field `Foo.n` must be initialized (part of struct `Foo`), or put it inside `unsafe{}` blocks
8 | _ = []Foo{len: 1}
9 | _ = [1]Foo{}
10 | _ = map[string]Foo{}
| ~~~~~~~~~~~~~~~~
11 | _ = map[string][]Foo{}
12 | _ = []AliasFoo{len: 1}
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:11:6: notice: reference field `Foo.n` must be initialized (part of struct `Foo`)
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:11:6: error: reference field `Foo.n` must be initialized (part of struct `Foo`), or put it inside `unsafe{}` blocks
9 | _ = [1]Foo{}
10 | _ = map[string]Foo{}
11 | _ = map[string][]Foo{}
| ~~~~~~~~~~~~~~~~~~
12 | _ = []AliasFoo{len: 1}
13 | }
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:12:6: notice: reference field `Foo.n` must be initialized (part of struct `Foo`)
vlib/v/checker/tests/array_map_elements_ref_fields_uninitialized_err.vv:12:6: error: reference field `Foo.n` must be initialized (part of struct `Foo`), or put it inside `unsafe{}` blocks
10 | _ = map[string]Foo{}
11 | _ = map[string][]Foo{}
12 | _ = []AliasFoo{len: 1}
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/struct_field_reference_type_err.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
vlib/v/checker/tests/struct_field_reference_type_err.vv:12:16: error: reference field `Animal.duck.age` must be initialized (part of struct `Duck`)
vlib/v/checker/tests/struct_field_reference_type_err.vv:12:16: error: reference field `Animal.duck.age` must be initialized (part of struct `Duck`), or put it inside `unsafe{}` blocks
10 |
11 | fn main() {
12 | mut animal := Animal{
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/tests/struct_ref_fields_uninitialized_err.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: error: reference field `Outer.c1.b` must be initialized (part of struct `ContainsRef`)
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: error: reference field `Outer.c1.b` must be initialized (part of struct `ContainsRef`), or put it inside `unsafe{}` blocks
23 |
24 | fn main() {
25 | _ := Outer{}
Expand All @@ -12,7 +12,7 @@ vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: error: referen
| ~~~~~~~
26 | _ := Struct{}
27 | }
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: error: reference field `Outer.c2.b` must be initialized (part of struct `ContainsRef`)
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: error: reference field `Outer.c2.b` must be initialized (part of struct `ContainsRef`), or put it inside `unsafe{}` blocks
23 |
24 | fn main() {
25 | _ := Outer{}
Expand Down

0 comments on commit 0fe5420

Please sign in to comment.