1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: fix nested struct ref field init check for embedded structs (fix #15768) (#15785)

This commit is contained in:
shove 2022-09-17 04:39:15 +08:00 committed by GitHub
parent 0992914bd6
commit 289f8f2f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 32 deletions

View File

@ -482,12 +482,23 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
} }
// Check uninitialized refs/sum types // Check uninitialized refs/sum types
for i, field in info.fields { // The variable `fields` contains two parts, the first part is the same as info.fields,
// and the second part is all fields embedded in the structure
// If the return value data composition form in `c.table.struct_fields()` is modified,
// need to modify here accordingly.
fields := c.table.struct_fields(type_sym)
mut checked_structs := []ast.Type{}
for i, field in fields {
if field.name in inited_fields { if field.name in inited_fields {
continue continue
} }
sym := c.table.sym(field.typ)
if field.name.len > 0 && field.name[0].is_capital() && sym.info is ast.Struct
&& sym.language == .v {
continue
}
if field.has_default_expr { if field.has_default_expr {
if field.default_expr_typ == 0 { if i < info.fields.len && field.default_expr_typ == 0 {
if field.default_expr is ast.StructInit { if field.default_expr is ast.StructInit {
idx := c.table.find_type_idx(field.default_expr.typ_str) idx := c.table.find_type_idx(field.default_expr.typ_str)
if idx != 0 { if idx != 0 {
@ -503,11 +514,15 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr
&& !c.pref.translated && !c.file.is_translated { && !c.pref.translated && !c.file.is_translated {
c.error('reference field `${type_sym.name}.$field.name` must be initialized', c.warn('reference field `${type_sym.name}.$field.name` must be initialized',
node.pos) node.pos)
continue
}
if sym.kind == .struct_ && !(sym.info as ast.Struct).is_typedef {
c.check_ref_fields_initialized(sym, mut checked_structs, '${type_sym.name}.$field.name',
node)
} }
// Do not allow empty uninitialized interfaces // Do not allow empty uninitialized interfaces
sym := c.table.sym(field.typ)
mut has_noinit := false mut has_noinit := false
for attr in field.attrs { for attr in field.attrs {
if attr.name == 'noinit' { if attr.name == 'noinit' {
@ -542,13 +557,6 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
node.pos) node.pos)
} }
} }
// Check for struct type
if sym.kind == .struct_ && !(sym.info as ast.Struct).is_typedef {
mut checked_structs := []ast.Type{}
checked_structs << field.typ
c.check_ref_fields_initialized(sym.info as ast.Struct, mut checked_structs,
'${node.typ_str}.$field.name', node)
}
} }
} }
else {} else {}
@ -575,24 +583,29 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
// Recursively check whether the struct type field is initialized // Recursively check whether the struct type field is initialized
fn (mut c Checker) check_ref_fields_initialized(struct_ &ast.Struct, mut checked_structs []ast.Type, linked_name string, node &ast.StructInit) { fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut checked_structs []ast.Type, linked_name string, node &ast.StructInit) {
if c.pref.translated || c.file.is_translated { if c.pref.translated || c.file.is_translated {
return return
} }
for field in struct_.fields { 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 {
continue
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.has_default_expr { if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.has_default_expr {
c.warn('reference field `${linked_name}.$field.name` must be initialized', c.warn('reference field `${linked_name}.$field.name` must be initialized',
node.pos) node.pos)
continue continue
} }
sym := c.table.sym(field.typ)
if sym.kind == .struct_ { if sym.kind == .struct_ {
info := sym.info as ast.Struct info := sym.info as ast.Struct
if info.is_typedef || field.typ in checked_structs { if info.is_typedef || field.typ in checked_structs {
continue continue
} }
checked_structs << field.typ checked_structs << field.typ
c.check_ref_fields_initialized(info, mut checked_structs, '${linked_name}.$field.name', c.check_ref_fields_initialized(sym, mut checked_structs, '${linked_name}.$field.name',
node) node)
} }
} }

View File

@ -1,11 +1,4 @@
vlib/v/checker/tests/reference_field_must_be_initialized.vv:8:7: warning: reference field `Node.next.next` must be initialized vlib/v/checker/tests/reference_field_must_be_initialized.vv:8:7: warning: reference field `Node.next` must be initialized
6 |
7 | fn main(){
8 | n := Node{ data: 123 }
| ~~~~~~~~~~~~~~~~~
9 | eprintln('n.data: $n.data')
10 | }
vlib/v/checker/tests/reference_field_must_be_initialized.vv:8:7: error: reference field `Node.next` must be initialized
6 | 6 |
7 | fn main(){ 7 | fn main(){
8 | n := Node{ data: 123 } 8 | n := Node{ data: 123 }

View File

@ -1,7 +1,13 @@
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:11:7: warning: reference field `Outer.c.b` must be initialized vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:22:7: warning: reference field `Outer.c.b` must be initialized
9 | 20 |
10 | fn main() { 21 | fn main() {
11 | o := Outer{} 22 | _ := Outer{}
| ~~~~~~~ | ~~~~~~~
12 | dump(o) 23 | _ := Struct{}
13 | } 24 | }
vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:23:7: warning: reference field `Struct.ref2` must be initialized
21 | fn main() {
22 | _ := Outer{}
23 | _ := Struct{}
| ~~~~~~~~
24 | }

View File

@ -7,7 +7,18 @@ struct Outer {
c ContainsRef c ContainsRef
} }
fn main() { struct Ref {}
o := Outer{}
dump(o) struct EmbedStruct {
ref1 &Ref = unsafe { nil }
ref2 &Ref
}
struct Struct {
EmbedStruct
}
fn main() {
_ := Outer{}
_ := Struct{}
} }