checker: add check for strict .str() method signature
							parent
							
								
									4403c76406
								
							
						
					
					
						commit
						35431d457b
					
				|  | @ -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 ') | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | } | ||||
|  | @ -0,0 +1,11 @@ | |||
| struct Zzz { | ||||
| 	x int | ||||
| } | ||||
| 
 | ||||
| fn (z Zzz) str(x int) string { | ||||
|     return 'z: $z.x' | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	println(Zzz{123}) | ||||
| } | ||||
|  | @ -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 | } | ||||
|  | @ -0,0 +1,11 @@ | |||
| struct Zzz { | ||||
| 	x int | ||||
| } | ||||
| 
 | ||||
| fn (z Zzz) str(x int) int { | ||||
| 	return z.x | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	println(Zzz{123}) | ||||
| } | ||||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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(' {') | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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('))') | ||||
| 		} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue