From 90591eb813dbb9df9becc40516aa2ed5d11ac0f2 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sat, 11 Feb 2023 17:22:31 +0800 Subject: [PATCH] checker: check nested struct field with required attr (fix #10913) (#17277) --- vlib/clipboard/x11/clipboard.c.v | 4 ++-- vlib/net/http/header.v | 1 - vlib/v/checker/checker.v | 2 +- vlib/v/checker/struct.v | 16 +++++++++++++--- .../nested_struct_with_required_attr_err.out | 7 +++++++ .../nested_struct_with_required_attr_err.vv | 15 +++++++++++++++ ...uct_field_generic_struct_unknown_type_err.out | 6 ++++++ .../tests/struct_field_reference_type_err.out | 11 +++++++++-- .../struct_ref_fields_uninitialized_err.out | 11 +++++++++-- vlib/vweb/csrf/csrf_test.v | 2 +- 10 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 vlib/v/checker/tests/nested_struct_with_required_attr_err.out create mode 100644 vlib/v/checker/tests/nested_struct_with_required_attr_err.vv diff --git a/vlib/clipboard/x11/clipboard.c.v b/vlib/clipboard/x11/clipboard.c.v index 35365efa93..d4ffa2d9cc 100644 --- a/vlib/clipboard/x11/clipboard.c.v +++ b/vlib/clipboard/x11/clipboard.c.v @@ -71,7 +71,7 @@ fn todo_del() {} [typedef] struct C.XSelectionRequestEvent { mut: - display &C.Display // Display the event was read from + display &C.Display = unsafe { nil } // Display the event was read from owner Window requestor Window selection Atom @@ -84,7 +84,7 @@ mut: struct C.XSelectionEvent { mut: @type int - display &C.Display // Display the event was read from + display &C.Display = unsafe { nil } // Display the event was read from requestor Window selection Atom target Atom diff --git a/vlib/net/http/header.v b/vlib/net/http/header.v index ac82813338..f742b1ff6e 100644 --- a/vlib/net/http/header.v +++ b/vlib/net/http/header.v @@ -6,7 +6,6 @@ module http import strings // Header represents the key-value pairs in an HTTP header -[noinit] pub struct Header { mut: data map[string][]string diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 93a9e1e99a..c8ae36bc63 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2638,7 +2638,7 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type { if node.unresolved { return c.expr(ast.resolve_init(node, c.unwrap_generic(node.typ), c.table)) } - return c.struct_init(mut node) + return c.struct_init(mut node, false) } ast.TypeNode { if !c.inside_x_is_type && node.typ.has_flag(.generic) && unsafe { c.table.cur_fn != 0 } diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 79b5da6210..62ace6a029 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -254,7 +254,7 @@ fn minify_sort_fn(a &ast.StructField, b &ast.StructField) int { } } -fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { +fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_init bool) ast.Type { util.timing_start(@METHOD) defer { util.timing_measure_cumulative(@METHOD) @@ -327,7 +327,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 { c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types) } - c.ensure_type_exists(node.typ, node.pos) or {} + if !is_field_zero_struct_init { + c.ensure_type_exists(node.typ, node.pos) or {} + } type_sym := c.table.sym(node.typ) if !c.inside_unsafe && type_sym.kind == .sum_type { c.note('direct sum type init (`x := SumType{}`) will be removed soon', node.pos) @@ -361,7 +363,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { // allow init structs from generic if they're private except the type is from builtin module if !node.has_update_expr && !type_sym.is_pub && type_sym.kind != .placeholder && type_sym.language != .c && (type_sym.mod != c.mod && !(node.typ.has_flag(.generic) - && type_sym.mod != 'builtin')) { + && type_sym.mod != 'builtin')) && !is_field_zero_struct_init { c.error('type `${type_sym.name}` is private', node.pos) } if type_sym.kind == .struct_ { @@ -627,6 +629,14 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { node.pos) } } + if !field.has_default_expr && field.name !in inited_fields && !field.typ.is_ptr() + && c.table.final_sym(field.typ).kind == .struct_ { + mut zero_struct_init := ast.StructInit{ + pos: node.pos + typ: field.typ + } + c.struct_init(mut zero_struct_init, true) + } } // println('>> checked_types.len: $checked_types.len | checked_types: $checked_types | type_sym: $type_sym.name ') } diff --git a/vlib/v/checker/tests/nested_struct_with_required_attr_err.out b/vlib/v/checker/tests/nested_struct_with_required_attr_err.out new file mode 100644 index 0000000000..6dbbc37d41 --- /dev/null +++ b/vlib/v/checker/tests/nested_struct_with_required_attr_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/nested_struct_with_required_attr_err.vv:13:7: error: field `Egg.name` must be initialized + 11 | + 12 | fn main() { + 13 | f := Foo{} + | ~~~~~ + 14 | println(f) + 15 | } diff --git a/vlib/v/checker/tests/nested_struct_with_required_attr_err.vv b/vlib/v/checker/tests/nested_struct_with_required_attr_err.vv new file mode 100644 index 0000000000..5e579d2435 --- /dev/null +++ b/vlib/v/checker/tests/nested_struct_with_required_attr_err.vv @@ -0,0 +1,15 @@ +struct Foo { + bar Bar +} +struct Bar { + egg Egg +} + +struct Egg { + name string [required] +} + +fn main() { + f := Foo{} + println(f) +} diff --git a/vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.out b/vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.out index 4de8f8ecb5..6def995c12 100644 --- a/vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.out +++ b/vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.out @@ -5,3 +5,9 @@ vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.vv:9:7: error: | ~~~~~~~~~~~~~~~~~~ 10 | } 11 | +vlib/v/checker/tests/struct_field_generic_struct_unknown_type_err.vv:13:9: error: unknown type `UnknownType` + 11 | + 12 | fn main() { + 13 | _ = MyType {} + | ~~~~~~~~~ + 14 | } diff --git a/vlib/v/checker/tests/struct_field_reference_type_err.out b/vlib/v/checker/tests/struct_field_reference_type_err.out index 5cdceea2f3..8e168619d5 100644 --- a/vlib/v/checker/tests/struct_field_reference_type_err.out +++ b/vlib/v/checker/tests/struct_field_reference_type_err.out @@ -1,12 +1,19 @@ vlib/v/checker/tests/struct_field_reference_type_err.vv:12:16: warning: reference field `Animal.duck.age` must be initialized (part of struct `Duck`) - 10 | + 10 | + 11 | fn main() { + 12 | mut animal := Animal{ + | ~~~~~~~ + 13 | ageee: 20 + 14 | } +vlib/v/checker/tests/struct_field_reference_type_err.vv:12:16: warning: reference field `Duck.age` must be initialized + 10 | 11 | fn main() { 12 | mut animal := Animal{ | ~~~~~~~ 13 | ageee: 20 14 | } vlib/v/checker/tests/struct_field_reference_type_err.vv:17:3: error: reference field must be initialized with reference - 15 | + 15 | 16 | animal.duck = Duck{ 17 | age: animal.ageee | ~~~~~~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/struct_ref_fields_uninitialized_err.out b/vlib/v/checker/tests/struct_ref_fields_uninitialized_err.out index b70de3a8c3..697e2f1800 100644 --- a/vlib/v/checker/tests/struct_ref_fields_uninitialized_err.out +++ b/vlib/v/checker/tests/struct_ref_fields_uninitialized_err.out @@ -1,12 +1,19 @@ vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: warning: reference field `Outer.c1.b` must be initialized (part of struct `ContainsRef`) - 23 | + 23 | + 24 | fn main() { + 25 | _ := Outer{} + | ~~~~~~~ + 26 | _ := Struct{} + 27 | } +vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: warning: reference field `ContainsRef.b` must be initialized + 23 | 24 | fn main() { 25 | _ := Outer{} | ~~~~~~~ 26 | _ := Struct{} 27 | } vlib/v/checker/tests/struct_ref_fields_uninitialized_err.vv:25:7: warning: reference field `Outer.c2.b` must be initialized (part of struct `ContainsRef`) - 23 | + 23 | 24 | fn main() { 25 | _ := Outer{} | ~~~~~~~ diff --git a/vlib/vweb/csrf/csrf_test.v b/vlib/vweb/csrf/csrf_test.v index b72b0266d5..0e11ed8cf4 100644 --- a/vlib/vweb/csrf/csrf_test.v +++ b/vlib/vweb/csrf/csrf_test.v @@ -21,7 +21,7 @@ fn (mut app App) index() vweb.Result { fn test_send_a_request_to_homepage_expecting_a_csrf_cookie() { spawn vweb.run_at(&App{}, vweb.RunParams{ port: sport }) time.sleep(500 * time.millisecond) - res := http.get('http://localhost:${sport}/')? + res := http.get('http://localhost:${sport}/')! if res.header.str().contains('__Host-Csrf-Token') { assert true } else {