checker: add check for strict .str() method signature

pull/5656/head
Delyan Angelov 2020-07-04 14:14:24 +03:00
parent 4403c76406
commit 35431d457b
10 changed files with 69 additions and 25 deletions

View File

@ -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 ')

View File

@ -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)

View File

@ -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 | }

View File

@ -0,0 +1,11 @@
struct Zzz {
x int
}
fn (z Zzz) str(x int) string {
return 'z: $z.x'
}
fn main() {
println(Zzz{123})
}

View File

@ -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 | }

View File

@ -0,0 +1,11 @@
struct Zzz {
x int
}
fn (z Zzz) str(x int) int {
return z.x
}
fn main() {
println(Zzz{123})
}

View File

@ -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)

View File

@ -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(' {')

View File

@ -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<it.len; ++i) {')
if has_str_method {
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${str_fn_name}(it.args[i]));')
} else {
// autogenerated str methods take the indent level as a second argument:
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${str_fn_name}(it.args[i], 0));')
}
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${str_fn_name}(it.args[i]));')
g.auto_str_funcs.writeln('\t\tif (i < it.len-1) {')
g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos_lit(", "));')
g.auto_str_funcs.writeln('\t\t}')
@ -4313,7 +4315,7 @@ fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp, str_fn_nam
arg_str_fn_name = styp_to_str_fn_name(field_styp)
}
if sym.kind == .struct_ && !sym_has_str_method {
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, ${str_fn_name}(a.arg$i,0));')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, ${str_fn_name}(a.arg$i));')
} else if sym.kind in [.f32, .f64] {
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _STR("%g", 1, a.arg$i));')
} else if sym.kind == .string {

View File

@ -515,10 +515,6 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
g.write('*')
}
g.expr(expr)
if !typ.has_flag(.variadic) && sym.kind == .struct_ &&
styp != 'ptr' && !sym.has_method('str') {
g.write(', 0') // trailing 0 is initial struct indent count
}
}
g.write('))')
}