From 35431d457b0563a658dc891e63dbbe2f310e4a00 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 4 Jul 2020 14:14:24 +0300 Subject: [PATCH] checker: add check for strict .str() method signature --- vlib/v/ast/str.v | 2 +- vlib/v/checker/checker.v | 10 ++++++ .../checker/tests/str_method_0_arguments.out | 7 ++++ .../v/checker/tests/str_method_0_arguments.vv | 11 ++++++ .../tests/str_method_return_string.out | 7 ++++ .../checker/tests/str_method_return_string.vv | 11 ++++++ vlib/v/doc/doc.v | 2 +- vlib/v/fmt/fmt.v | 4 +-- vlib/v/gen/cgen.v | 36 ++++++++++--------- vlib/v/gen/fn.v | 4 --- 10 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 vlib/v/checker/tests/str_method_0_arguments.out create mode 100644 vlib/v/checker/tests/str_method_0_arguments.vv create mode 100644 vlib/v/checker/tests/str_method_return_string.out create mode 100644 vlib/v/checker/tests/str_method_return_string.vv diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index b57df39a8c..acf2575e30 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -24,7 +24,7 @@ pub fn (node &FnDecl) modname() string { // These methods are used only by vfmt, vdoc, and for debugging. -pub fn (node &FnDecl) str(t &table.Table) string { +pub fn (node &FnDecl) stringify(t &table.Table) string { mut f := strings.new_builder(30) if node.is_pub { f.write('pub ') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0bb0a6d412..d830d892de 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2950,6 +2950,16 @@ fn (mut c Checker) fn_decl(node ast.FnDecl) { } } } + + if node.language == .v && node.is_method && node.name == 'str' { + if node.return_type != table.string_type { + c.error('.str() methods should return `string`', node.pos) + } + if node.args.len != 1 { + c.error('.str() methods should have 0 arguments', node.pos) + } + } + c.expected_type = table.void_type c.cur_fn = &node c.stmts(node.stmts) diff --git a/vlib/v/checker/tests/str_method_0_arguments.out b/vlib/v/checker/tests/str_method_0_arguments.out new file mode 100644 index 0000000000..0a4f715225 --- /dev/null +++ b/vlib/v/checker/tests/str_method_0_arguments.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/str_method_0_arguments.v:5:1: error: .str() methods should have 0 arguments + 3 | } + 4 | + 5 | fn (z Zzz) str(x int) string { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | return 'z: $z.x' + 7 | } diff --git a/vlib/v/checker/tests/str_method_0_arguments.vv b/vlib/v/checker/tests/str_method_0_arguments.vv new file mode 100644 index 0000000000..28ef0ef757 --- /dev/null +++ b/vlib/v/checker/tests/str_method_0_arguments.vv @@ -0,0 +1,11 @@ +struct Zzz { + x int +} + +fn (z Zzz) str(x int) string { + return 'z: $z.x' +} + +fn main() { + println(Zzz{123}) +} diff --git a/vlib/v/checker/tests/str_method_return_string.out b/vlib/v/checker/tests/str_method_return_string.out new file mode 100644 index 0000000000..e949e9ac94 --- /dev/null +++ b/vlib/v/checker/tests/str_method_return_string.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/str_method_return_string.v:5:1: error: .str() methods should return `string` + 3 | } + 4 | + 5 | fn (z Zzz) str(x int) int { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | return z.x + 7 | } diff --git a/vlib/v/checker/tests/str_method_return_string.vv b/vlib/v/checker/tests/str_method_return_string.vv new file mode 100644 index 0000000000..a3f695299b --- /dev/null +++ b/vlib/v/checker/tests/str_method_return_string.vv @@ -0,0 +1,11 @@ +struct Zzz { + x int +} + +fn (z Zzz) str(x int) int { + return z.x +} + +fn main() { + println(Zzz{123}) +} diff --git a/vlib/v/doc/doc.v b/vlib/v/doc/doc.v index 71f13fb969..7f7831f05d 100644 --- a/vlib/v/doc/doc.v +++ b/vlib/v/doc/doc.v @@ -119,7 +119,7 @@ pub fn (mut d Doc) get_signature(stmt ast.Stmt, file &ast.File) string { return 'module $stmt.name' } ast.FnDecl { - return stmt.str(d.table).replace(d.fmt.cur_mod + '.', '') + return stmt.stringify(d.table).replace(d.fmt.cur_mod + '.', '') } else { d.fmt.out = strings.new_builder(1000) diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index f4c72901b9..6efb4d39ec 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -421,7 +421,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { f.writeln('interface $it.name {') for method in it.methods { f.write('\t') - f.writeln(method.str(f.table).after('fn ')) + f.writeln(method.stringify(f.table).after('fn ')) } f.writeln('}\n') } @@ -1134,7 +1134,7 @@ pub fn (mut f Fmt) comments(some_comments []ast.Comment, remove_last_new_line bo pub fn (mut f Fmt) fn_decl(node ast.FnDecl) { // println('$it.name find_comment($it.pos.line_nr)') // f.find_comment(it.pos.line_nr) - s := node.str(f.table) + s := node.stringify(f.table) f.write(s.replace(f.cur_mod + '.', '')) // `Expr` instead of `ast.Expr` in mod ast if node.language == .v { f.writeln(' {') diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index e0e94ab871..f5930476b8 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -3232,9 +3232,9 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { g.expr(expr) if sym.kind == .struct_ && !sym_has_str_method { if is_p { - g.write('),0))') + g.write(')))') } else { - g.write(',0)') + g.write(')') } } else { if is_p { @@ -4062,8 +4062,12 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { fnames2strfunc[field_styp] = field_fn_name } } - g.type_definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto') - g.auto_str_funcs.writeln('string ${str_fn_name}($styp x, int indent_count) {') + // _str() functions should have a single argument, the indenting ones take 2: + g.type_definitions.writeln('string ${str_fn_name}($styp x); // auto') + g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x,0);}') + // + g.type_definitions.writeln('string indent_${str_fn_name}($styp x, int indent_count); // auto') + g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp x, int indent_count) {') mut clean_struct_v_type_name := styp.replace('__', '.') if styp.ends_with('*') { deref_typ := styp.replace('*', '') @@ -4090,7 +4094,6 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { for i, field in info.fields { sym := g.table.get_type_symbol(field.typ) has_custom_str := sym.has_method('str') - second_str_param := if has_custom_str { '' } else { ', indent_count + 1' } mut field_styp := g.typ(field.typ) if field_styp.ends_with('*') { field_styp = field_styp.replace('*', '') @@ -4101,7 +4104,11 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)} ) ') } else if sym.kind == .struct_ { g.auto_str_funcs.write('indents, ') - g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)}$second_str_param ) ') + if has_custom_str { + g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)} ) ') + }else{ + g.auto_str_funcs.write('indent_${field_styp_fn_name}( it->${c_name(field.name)}, indent_count + 1 ) ') + } } else if sym.kind in [.array, .array_fixed, .map] { g.auto_str_funcs.write('indents, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)}) ') @@ -4145,9 +4152,9 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) { g.auto_str_funcs.writeln('\t\t$field_styp it = (*($field_styp*)array_get(a, i));') if sym.kind == .struct_ && !sym_has_str_method { if is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(*it,0);') + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(*it);') } else { - g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it,0);') + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it);') } } else if sym.kind in [.f32, .f64] { g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);') @@ -4201,7 +4208,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, str_fn_name g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));') g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {') if sym.kind == .struct_ && !sym_has_str_method { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i],0));') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i]));') } else if sym.kind in [.f32, .f64] { g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, a[i]));') } else if sym.kind == .string { @@ -4252,7 +4259,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) { if val_sym.kind == .string { g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, it));') } else if val_sym.kind == .struct_ && !val_sym.has_method('str') { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(it,0));') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(it));') } else if val_sym.kind in [.f32, .f64] { g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, it));') } else { @@ -4273,12 +4280,7 @@ fn (mut g Gen) gen_str_for_varg(styp, str_fn_name string, has_str_method bool) { g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(it.len);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));') g.auto_str_funcs.writeln('\tfor(int i=0; i