From afc1f92d0a42d1e8bbdd09007825f6947955a53a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 12:51:29 +0300 Subject: [PATCH] checker: creating array as value of map must be explicit when using `<<` (fixes #18160) --- 01KKE4XM89M2JY46Y8588SA4NG | Bin 0 -> 40232 bytes cmd/tools/vcheck-md.v | 9 ++++ cmd/tools/vdoc/files.v | 3 ++ cmd/tools/vrepeat.v | 5 +- cmd/tools/vrepl.v | 3 ++ cmd/tools/vvet/analyze.v | 9 ++++ vlib/benchmark/benchmark.v | 3 ++ vlib/builtin/js/map_test.js.v | 7 +++ vlib/builtin/map_test.v | 7 +++ vlib/crypto/pem/decode.v | 3 ++ vlib/flag/flag_to.v | 51 ++++++++++-------- vlib/net/http/request.v | 3 ++ vlib/orm/orm.v | 3 ++ vlib/strings/lorem/lorem.v | 3 ++ vlib/v/ast/table.v | 3 ++ vlib/v/builder/builder.v | 17 ++++-- vlib/v/checker/interface.v | 4 +- vlib/v/gen/c/cgen.v | 1 + vlib/v/gen/c/index.v | 5 +- vlib/v/gen/c/infix.v | 6 +++ vlib/v/gen/js/js.v | 6 ++- vlib/v/parser/tmpl.v | 3 ++ .../aliases/unaliased_typ_checking_test.v | 1 + .../tests/builtin_maps/complex_map_op_test.v | 16 ++++++ .../comptime/comptime_if_expr_evaluate_test.v | 2 + .../v/tests/options/option_compvar_val_test.v | 1 + 26 files changed, 140 insertions(+), 34 deletions(-) create mode 100755 01KKE4XM89M2JY46Y8588SA4NG diff --git a/01KKE4XM89M2JY46Y8588SA4NG b/01KKE4XM89M2JY46Y8588SA4NG new file mode 100755 index 0000000000000000000000000000000000000000..4874d5893a5e73758d80934768284f1a0624e401 GIT binary patch literal 40232 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 a5nkIjXaDIXhvv?nnDeWro4&MurTAZv42a$U literal 0 HcmV?d00001 diff --git a/cmd/tools/vcheck-md.v b/cmd/tools/vcheck-md.v index 68b561af1..9f9a3c826 100644 --- a/cmd/tools/vcheck-md.v +++ b/cmd/tools/vcheck-md.v @@ -505,6 +505,9 @@ fn (mut ad AnchorData) add_links(line_number int, line string) { for elem in res { re.match_string(elem) link := re.get_group_by_name(elem, 'link') + if link !in ad.links { + ad.links[link] = []AnchorLink{} + } ad.links[link] << AnchorLink{ line: line_number label: re.get_group_by_name(elem, 'label') @@ -517,6 +520,9 @@ fn (mut ad AnchorData) add_link_targets(line_number int, line string) { if headline_start_pos := line.index(' ') { headline := line.substr(headline_start_pos + 1, line.len) link := create_ref_link(headline) + if link !in ad.anchors { + ad.anchors[link] = []AnchorTarget{} + } ad.anchors[link] << Headline{ line: line_number label: headline @@ -531,6 +537,9 @@ fn (mut ad AnchorData) add_link_targets(line_number int, line string) { for elem in res { re.match_string(elem) link := re.get_group_by_name(elem, 'link') + if link !in ad.anchors { + ad.anchors[link] = []AnchorTarget{} + } ad.anchors[link] << Anchor{ line: line_number } diff --git a/cmd/tools/vdoc/files.v b/cmd/tools/vdoc/files.v index eac27e3d5..077537332 100644 --- a/cmd/tools/vdoc/files.v +++ b/cmd/tools/vdoc/files.v @@ -88,6 +88,9 @@ fn IgnoreRules.get(path string) IgnoreRules { // `/a` should ignore `/a` but not `/b/a`. While `a` should ignore `/a` and `/b/a`. res.paths[os.join_path(p, rule.trim_left('/'))] = true } else { + if p !in res.patterns { + res.patterns[p] = []string{} + } res.patterns[p] << rule } } diff --git a/cmd/tools/vrepeat.v b/cmd/tools/vrepeat.v index 58fe20123..a63ab1f78 100644 --- a/cmd/tools/vrepeat.v +++ b/cmd/tools/vrepeat.v @@ -290,6 +290,9 @@ fn (mut context Context) show_timings_details(si int, icmd int, cmd string) { if x.len > 1 { v := i64(x[0].trim_left(' ').f64() * 1000) k := x[1].all_before(' ') + if k !in m { + m[k] = []i64{} + } m[k] << v } } @@ -311,7 +314,7 @@ fn (mut context Context) show_timings_details(si int, icmd int, cmd string) { if old_ous[k].len == 0 { new_ous[k] = v } else { - new_ous[k] << old_ous[k] + new_ous[k] = old_ous[k].clone() new_ous[k] << v } } diff --git a/cmd/tools/vrepl.v b/cmd/tools/vrepl.v index 22df27c49..01debd1a4 100644 --- a/cmd/tools/vrepl.v +++ b/cmd/tools/vrepl.v @@ -332,6 +332,9 @@ fn (mut r Repl) parse_import(line string) { // set value if line.contains('{') && line.contains('}') { + if mod !in r.modules { + r.modules[mod] = []string{} + } values := line.split('{')[1].split('}')[0] for value in values.split(',') { r.modules[mod] << value diff --git a/cmd/tools/vvet/analyze.v b/cmd/tools/vvet/analyze.v index d155c39a2..1e84a046d 100644 --- a/cmd/tools/vvet/analyze.v +++ b/cmd/tools/vvet/analyze.v @@ -54,6 +54,15 @@ fn (mut vt VetAnalyze) stmt(vet &Vet, stmt ast.Stmt) { // save_expr registers a repeated code occurrence fn (mut vt VetAnalyze) save_expr(cutoff int, expr string, file string, pos token.Pos) { lock vt.repeated_expr { + if vt.cur_fn.name !in vt.repeated_expr { + vt.repeated_expr[vt.cur_fn.name] = map[string]map[string][]token.Pos{} + } + if expr !in vt.repeated_expr[vt.cur_fn.name] { + vt.repeated_expr[vt.cur_fn.name][expr] = map[string][]token.Pos{} + } + if file !in vt.repeated_expr[vt.cur_fn.name][expr] { + vt.repeated_expr[vt.cur_fn.name][expr][file] = []token.Pos{} + } vt.repeated_expr[vt.cur_fn.name][expr][file] << pos } lock vt.repeated_expr_cutoff { diff --git a/vlib/benchmark/benchmark.v b/vlib/benchmark/benchmark.v index 4ca03d627..8f03fc73d 100644 --- a/vlib/benchmark/benchmark.v +++ b/vlib/benchmark/benchmark.v @@ -146,6 +146,9 @@ pub fn (mut b Benchmark) record_measure(label string) i64 { b.ok() res := b.step_timer.elapsed().microseconds() b.measured_steps << b.step_message_with_label(b_spent, 'in ${label}') + if label !in b.step_data { + b.step_data[label] = []f64{} + } b.step_data[label] << f64(res) b.step() return res diff --git a/vlib/builtin/js/map_test.js.v b/vlib/builtin/js/map_test.js.v index 795c81703..e437c7f54 100644 --- a/vlib/builtin/js/map_test.js.v +++ b/vlib/builtin/js/map_test.js.v @@ -391,11 +391,18 @@ fn test_postfix_op_directly() { fn test_map_push_directly() { mut a := map[string][]string{} + a['aaa'] = []string{} a['aaa'] << ['a', 'b', 'c'] assert a['aaa'].len == 3 assert a['aaa'] == ['a', 'b', 'c'] } +fn test_map_push_missing_key_does_not_insert() { + mut a := map[string][]string{} + a['aaa'] << 'a' + assert a == map[string][]string{} +} + fn test_assign_directly() { mut a := map[string]int{} a['aaa'] += 4 diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index ffb1a4407..6c9b02a9d 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -422,11 +422,18 @@ fn test_postfix_op_directly() { fn test_map_push_directly() { mut a := map[string][]string{} + a['aaa'] = []string{} a['aaa'] << ['a', 'b', 'c'] assert a['aaa'].len == 3 assert a['aaa'] == ['a', 'b', 'c'] } +fn test_map_push_missing_key_does_not_insert() { + mut a := map[string][]string{} + a['aaa'] << 'a' + assert a == map[string][]string{} +} + fn test_assign_directly() { mut a := map[string]int{} a['aaa'] += 4 diff --git a/vlib/crypto/pem/decode.v b/vlib/crypto/pem/decode.v index a33584235..8a2b5470e 100644 --- a/vlib/crypto/pem/decode.v +++ b/vlib/crypto/pem/decode.v @@ -83,6 +83,9 @@ fn parse_headers(block string) ?(map[string][]string, string) { } } + if key !in headers { + headers[key] = []string{} + } headers[key] << val } diff --git a/vlib/flag/flag_to.v b/vlib/flag/flag_to.v index ba271e2b8..da7cda20a 100644 --- a/vlib/flag/flag_to.v +++ b/vlib/flag/flag_to.v @@ -171,6 +171,13 @@ fn normalize_attr_value(value string) string { return trimmed } +fn (mut fm FlagMapper) add_array_flag(field_name string, flag_data FlagData) { + if field_name !in fm.array_field_map_flag { + fm.array_field_map_flag[field_name] = []FlagData{} + } + fm.array_field_map_flag[field_name] << flag_data +} + fn (fm FlagMapper) get_struct_info[T]() !StructInfo { mut struct_fields := map[string]StructField{} mut struct_attrs := map[string]string{} @@ -596,12 +603,12 @@ pub fn (mut fm FlagMapper) parse[T]() ! { trace_println('${@FN}: (tail) flag `${arg}` last_handled_pos: ${last_handled_pos} pos: ${pos}') if pos == last_handled_pos + 1 || pos == pos_last_flag + 1 { if field.hints.has(.is_array) { - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: arg field_name: field.name arg: ?string(arg) // .arg is used when assigning at comptime to []XYZ pos: pos - } + }) } else { fm.field_map_flag[field.name] = FlagData{ raw: arg @@ -1082,14 +1089,14 @@ fn (mut fm FlagMapper) map_v(flag_ctx FlagContext, field StructField) !bool { if field.hints.has(.is_array) { trace_println('${@FN}: found match for (V style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for (V style) ${fm.dbg_match(flag_ctx, field, next, '')}') @@ -1146,14 +1153,14 @@ fn (mut fm FlagMapper) map_v_flag_parser_short(flag_ctx FlagContext, field Struc if field.hints.has(.is_array) { trace_println('${@FN}: found match for V (`flag.FlagParser` (short) style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for V (`flag.FlagParser` (short) style) ${fm.dbg_match(flag_ctx, field, next, '')}') @@ -1206,14 +1213,14 @@ fn (mut fm FlagMapper) map_v_flag_parser_long(flag_ctx FlagContext, field Struct if field.hints.has(.is_array) { trace_println('${@FN}: found match for (V `flag.FlagParser` (long) style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for V (`flag.FlagParser` (long) style) ${fm.dbg_match(flag_ctx, field, next, '')}') @@ -1266,14 +1273,14 @@ fn (mut fm FlagMapper) map_go_flag_short(flag_ctx FlagContext, field StructField if field.hints.has(.is_array) { trace_println('${@FN}: found match for (GO short style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for (GO short style) ${fm.dbg_match(flag_ctx, field, next, '')}') @@ -1331,14 +1338,14 @@ fn (mut fm FlagMapper) map_go_flag_long(flag_ctx FlagContext, field StructField) if field.hints.has(.is_array) { trace_println('${@FN}: found match for (GO `flag` style multiple occurrences) ${fm.dbg_match(flag_ctx, field, arg, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(arg) pos: pos - } + }) } else { trace_println('${@FN}: found match for (GO `flag` style) ${fm.dbg_match(flag_ctx, field, arg, '')}') @@ -1393,14 +1400,14 @@ fn (mut fm FlagMapper) map_gnu_long(flag_ctx FlagContext, field StructField) !bo if field.hints.has(.is_array) { trace_println('${@FN}: found match for (GNU style multiple occurrences) ${fm.dbg_match(flag_ctx, field, arg, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(arg) pos: pos - } + }) } else { trace_println('${@FN}: found match for (GNU style) ${fm.dbg_match(flag_ctx, field, arg, '')}') @@ -1482,10 +1489,10 @@ fn (mut fm FlagMapper) map_posix_short_cluster(flag_ctx FlagContext) ! { } } if field.hints.has(.is_array) { - fm.array_field_map_flag[mf.field_name] << FlagData{ + fm.add_array_flag(mf.field_name, FlagData{ ...mf arg: ?string(arg) - } + }) trace_println('${@FN}: found match for (array) ${fm.dbg_match(flag_ctx, field, arg, '')}') } else { @@ -1617,14 +1624,14 @@ fn (mut fm FlagMapper) map_posix_short(flag_ctx FlagContext, field StructField) trace_println('${@FN}: found match for (multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) fm.handled_pos << pos if next_is_handled { fm.handled_pos << pos + 1 // next @@ -1726,14 +1733,14 @@ fn (mut fm FlagMapper) map_cmd_exe(flag_ctx FlagContext, field StructField) !boo if field.hints.has(.is_array) { trace_println('${@FN}: found match for (CMD.EXE style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for (CMD.EXE style) ${fm.dbg_match(flag_ctx, field, next, '')}') @@ -1771,14 +1778,14 @@ fn (mut fm FlagMapper) map_cmd_exe(flag_ctx FlagContext, field StructField) !boo if field.hints.has(.is_array) { trace_println('${@FN}: found match for (CMD.EXE style multiple occurrences) ${fm.dbg_match(flag_ctx, field, next, '')}') - fm.array_field_map_flag[field.name] << FlagData{ + fm.add_array_flag(field.name, FlagData{ raw: flag_raw field_name: field.name delimiter: used_delimiter name: flag_name arg: ?string(next) pos: pos - } + }) } else { trace_println('${@FN}: found match for (CMD.EXE style) ${fm.dbg_match(flag_ctx, field, next, '')}') diff --git a/vlib/net/http/request.v b/vlib/net/http/request.v index da24bf5f3..61caab03c 100644 --- a/vlib/net/http/request.v +++ b/vlib/net/http/request.v @@ -806,6 +806,9 @@ pub fn parse_multipart_form(body string, boundary string) (map[string]string, ma data := field[line_segments[4].start..field.len - 4] // each multipart field ends with \r\n-- // dump(data.limit(20).bytes()) // dump(data.len) + if name !in files { + files[name] = []FileData{} + } files[name] << FileData{ filename: filename content_type: content_type diff --git a/vlib/orm/orm.v b/vlib/orm/orm.v index aad38c957..c3235cfde 100644 --- a/vlib/orm/orm.v +++ b/vlib/orm/orm.v @@ -916,6 +916,9 @@ pub fn orm_table_gen(sql_dialect SQLDialect, table Table, q string, defaults boo 'unique' { if attr.arg != '' { if attr.kind == .string { + if attr.arg !in unique { + unique[attr.arg] = []string{} + } unique[attr.arg] << field_name continue } else if attr.kind == .number { diff --git a/vlib/strings/lorem/lorem.v b/vlib/strings/lorem/lorem.v index 2b3cfa629..bbfee197f 100644 --- a/vlib/strings/lorem/lorem.v +++ b/vlib/strings/lorem/lorem.v @@ -215,6 +215,9 @@ fn lorem_build_markov(tokens []string, order int) map[string][]string { mut model := map[string][]string{} for i in 0 .. tokens.len - order { key := tokens[i..i + order].join('\u0001') + if key !in model { + model[key] = []string{} + } model[key] << tokens[i + order] } return model diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 2a8fd46e7..a32163d8a 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1777,6 +1777,9 @@ pub fn (mut t Table) complete_interface_check() { $if trace_types_implementing_each_interface ? { eprintln('>>> tsym.mod: ${tsym.mod} | tsym.name: ${tsym.name} | tk: ${tk} | idecl.name: ${idecl.name} | idecl.typ: ${idecl.typ}') } + if idecl.name !in t.iface_types { + t.iface_types[idecl.name] = []Type{} + } t.iface_types[idecl.name] << tk_typ } } diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 35d388bc0..eecc37718 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -18,6 +18,13 @@ import v.callgraph import v.dotgraph // import x.json2 +fn append_map_array(mut items map[string][]string, key string, value string) { + if key !in items { + items[key] = []string{} + } + items[key] << value +} + pub struct Builder { pub: compiled_dir string // contains os.real_path() of the dir of the final file being compiled, or the dir itself when doing `v .` @@ -219,15 +226,15 @@ pub fn (mut b Builder) parse_imports() { // so we can not use the shorter `for in` form. for i := 0; i < b.parsed_files.len; i++ { ast_file := b.parsed_files[i] - b.path_invalidates_mods[ast_file.path] << ast_file.mod.name + append_map_array(mut b.path_invalidates_mods, ast_file.path, ast_file.mod.name) if ast_file.mod.name != 'builtin' { - b.mod_invalidates_paths['builtin'] << ast_file.path - b.mod_invalidates_mods['builtin'] << ast_file.mod.name + append_map_array(mut b.mod_invalidates_paths, 'builtin', ast_file.path) + append_map_array(mut b.mod_invalidates_mods, 'builtin', ast_file.mod.name) } for imp in ast_file.imports { mod := imp.mod - b.mod_invalidates_paths[mod] << ast_file.path - b.mod_invalidates_mods[mod] << ast_file.mod.name + append_map_array(mut b.mod_invalidates_paths, mod, ast_file.path) + append_map_array(mut b.mod_invalidates_mods, mod, ast_file.mod.name) if mod == 'builtin' { b.parsed_files[i].errors << b.error_with_pos('cannot import module "builtin"', ast_file.path, imp.pos) diff --git a/vlib/v/checker/interface.v b/vlib/v/checker/interface.v index e7a313992..cc073ffa6 100644 --- a/vlib/v/checker/interface.v +++ b/vlib/v/checker/interface.v @@ -378,9 +378,7 @@ fn (mut c Checker) unwrap_generic_interface(typ ast.Type, interface_type ast.Typ // add concrete types to method for imethod in inter_sym.info.methods { im_fkey := imethod.fkey() - if inferred_types !in c.table.fn_generic_types[im_fkey] { - c.table.fn_generic_types[im_fkey] << inferred_types - } + c.table.register_fn_concrete_types(im_fkey, inferred_types) } result_type := c.table.unwrap_generic_type(interface_type, generic_names, inferred_types) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e68a4f1bb..219ec0368 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -129,6 +129,7 @@ mut: inside_ternary int // ?: comma separated statements on a single line inside_map_postfix bool // inside map++/-- postfix expr inside_map_infix bool // inside map<val') } @@ -1111,7 +1114,10 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { g.write('&') } } + old_inside_left_shift := g.inside_left_shift + g.inside_left_shift = true g.expr(node.left) + g.inside_left_shift = old_inside_left_shift if node.left_type.has_flag(.shared_f) { g.write('->val') } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 7c796976a..0b071bbcd 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -54,6 +54,7 @@ mut: inside_ternary bool inside_or bool inside_loop bool + inside_left_shift bool inside_map_set bool // map.set(key, value) inside_builtin bool inside_if_option bool @@ -2967,7 +2968,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { } else if left_sym.kind == .map { g.expr(expr.left) - if expr.is_setter { + if expr.is_setter && !g.inside_left_shift { g.inside_map_set = true g.write('.getOrSet(') } else { @@ -3133,7 +3134,10 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { } } else if l_sym.kind == .array && it.op == .left_shift { // arr << 1 g.write('array_push(') + old_inside_left_shift := g.inside_left_shift + g.inside_left_shift = true g.expr(it.left) + g.inside_left_shift = old_inside_left_shift mut ltyp := it.left_type for ltyp.is_ptr() { g.write('.val') diff --git a/vlib/v/parser/tmpl.v b/vlib/v/parser/tmpl.v index 25c408111..340d3b003 100644 --- a/vlib/v/parser/tmpl.v +++ b/vlib/v/parser/tmpl.v @@ -423,6 +423,9 @@ fn (mut p Parser) process_includes(calling_file string, line_number int, line st } // If file hasnt been called before then add to dependency tree + if file_path !in dc.dependencies { + dc.dependencies[file_path] = []string{} + } if !dc.dependencies[file_path].contains(calling_file) { dc.dependencies[file_path] << calling_file } diff --git a/vlib/v/tests/aliases/unaliased_typ_checking_test.v b/vlib/v/tests/aliases/unaliased_typ_checking_test.v index e227830c2..13a348bef 100644 --- a/vlib/v/tests/aliases/unaliased_typ_checking_test.v +++ b/vlib/v/tests/aliases/unaliased_typ_checking_test.v @@ -15,6 +15,7 @@ struct Foo { fn test_main() { mut out := map[string][]string{} $for field in Foo.fields { + out[field.name] = []string{} print('${field.name} is ') $if field.unaliased_typ is $int { println('numeric') diff --git a/vlib/v/tests/builtin_maps/complex_map_op_test.v b/vlib/v/tests/builtin_maps/complex_map_op_test.v index d317b5d96..7d1d93e78 100644 --- a/vlib/v/tests/builtin_maps/complex_map_op_test.v +++ b/vlib/v/tests/builtin_maps/complex_map_op_test.v @@ -1,6 +1,16 @@ fn test_complex_map_op1() { mut test_map := map[string]Point_map{} + test_map['test1'] = Point_map{ + points: { + 'point3': []Point{} + } + } + test_map['test2'] = Point_map{ + points: { + 'point4': []Point{} + } + } test_map['test1'].points['point3'] << Point{10, 20} test_map['test2'].points['point4'] << Point{50, 60} @@ -16,6 +26,12 @@ fn test_complex_map_op1() { fn test_complex_map_op2() { mut test_map := map[string]map[string][]Point{} + test_map['test1'] = { + 'point3': []Point{} + } + test_map['test2'] = { + 'point4': []Point{} + } test_map['test1']['point3'] << Point{10, 20} test_map['test2']['point4'] << Point{50, 60} diff --git a/vlib/v/tests/comptime/comptime_if_expr_evaluate_test.v b/vlib/v/tests/comptime/comptime_if_expr_evaluate_test.v index 9a29fd3ba..4fe57c86d 100644 --- a/vlib/v/tests/comptime/comptime_if_expr_evaluate_test.v +++ b/vlib/v/tests/comptime/comptime_if_expr_evaluate_test.v @@ -7,6 +7,7 @@ pub: fn encode_struct[T](val T) { mut result := map[string][]string{} $for field in T.fields { + result[field.name] = []string{} $if field.is_array == false { result[field.name] << '> is not array' } $else { @@ -30,6 +31,7 @@ fn encode_struct[T](val T) { result[field.name] << '>>>> is pub' } } + result['bool'] = []string{} $if !false { result['bool'] << '1' } diff --git a/vlib/v/tests/options/option_compvar_val_test.v b/vlib/v/tests/options/option_compvar_val_test.v index 5deef5db6..ae7951f8c 100644 --- a/vlib/v/tests/options/option_compvar_val_test.v +++ b/vlib/v/tests/options/option_compvar_val_test.v @@ -25,6 +25,7 @@ fn encode_struct[T](val T) map[string][]string { mut out := map[string][]string{} $if T is $struct { $for field in T.fields { + out[field.name] = []string{} value := val.$(field.name) $if field.typ is ?int { // work if comment lines 27 and 28 -- 2.39.5