gen: fix str gen for pointers in structs (#6462)
							parent
							
								
									e384dea8ac
								
							
						
					
					
						commit
						90d1a689db
					
				| 
						 | 
				
			
			@ -291,7 +291,11 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
 | 
			
		|||
	for field in info.fields {
 | 
			
		||||
		sym := g.table.get_type_symbol(field.typ)
 | 
			
		||||
		if !sym.has_method('str') {
 | 
			
		||||
			field_styp := g.typ(field.typ)
 | 
			
		||||
			mut typ := field.typ 
 | 
			
		||||
			if typ.is_ptr() {
 | 
			
		||||
				typ = typ.deref()
 | 
			
		||||
			}
 | 
			
		||||
			field_styp := g.typ(typ)
 | 
			
		||||
			field_fn_name := g.gen_str_for_type_with_styp(field.typ, field_styp)
 | 
			
		||||
			fnames2strfunc[field_styp] = field_fn_name
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -318,10 +322,13 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
 | 
			
		|||
	g.auto_str_funcs.writeln('\t}')
 | 
			
		||||
	g.auto_str_funcs.writeln('\treturn _STR("$clean_struct_v_type_name {\\n"')
 | 
			
		||||
	for field in info.fields {
 | 
			
		||||
		fmt := g.type_to_fmt(field.typ)
 | 
			
		||||
		g.auto_str_funcs.writeln('\t\t"%.*s\\000    ' + '$field.name: $fmt\\n"')
 | 
			
		||||
		mut fmt := g.type_to_fmt(field.typ)
 | 
			
		||||
		if field.typ.is_ptr() {
 | 
			
		||||
			fmt = '&$fmt'
 | 
			
		||||
		}
 | 
			
		||||
		g.auto_str_funcs.writeln('\t\t"%.*s\\000    $field.name: $fmt\\n"')
 | 
			
		||||
	}
 | 
			
		||||
	g.auto_str_funcs.write('\t\t"%.*s\\000}", ${2*(info.fields.len+1)}')
 | 
			
		||||
	g.auto_str_funcs.write('\t\t"%.*s\\000}", ${2 * (info.fields.len + 1)}')
 | 
			
		||||
	if info.fields.len > 0 {
 | 
			
		||||
		g.auto_str_funcs.write(',\n\t\t')
 | 
			
		||||
		for i, field in info.fields {
 | 
			
		||||
| 
						 | 
				
			
			@ -332,27 +339,19 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
 | 
			
		|||
				field_styp = field_styp.replace('*', '')
 | 
			
		||||
			}
 | 
			
		||||
			field_styp_fn_name := if has_custom_str { '${field_styp}_str' } else { fnames2strfunc[field_styp] }
 | 
			
		||||
			if sym.kind == .enum_ {
 | 
			
		||||
				g.auto_str_funcs.write('indents, ')
 | 
			
		||||
				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, ')
 | 
			
		||||
				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)}) ')
 | 
			
		||||
			} else if sym.kind == .sum_type {
 | 
			
		||||
				g.auto_str_funcs.write('indents, indent_${field_styp_fn_name}(it->${c_name(field.name)}, indent_count + 1)')
 | 
			
		||||
			} else {
 | 
			
		||||
				g.auto_str_funcs.write('indents, it->${c_name(field.name)}')
 | 
			
		||||
				if field.typ == table.bool_type {
 | 
			
		||||
					g.auto_str_funcs.write(' ? _SLIT("true") : _SLIT("false")')
 | 
			
		||||
 | 
			
		||||
			g.auto_str_funcs.write('indents, ')
 | 
			
		||||
			func := struct_auto_str_func(sym, field.typ, field_styp_fn_name, field.name)
 | 
			
		||||
			if field.typ.is_ptr() {
 | 
			
		||||
				g.auto_str_funcs.write('isnil(it->${c_name(field.name)})')
 | 
			
		||||
				g.auto_str_funcs.write(' ? tos_lit("nil") : ')
 | 
			
		||||
				// struct, floats and ints have a special case through the _str function
 | 
			
		||||
				if sym.kind != .struct_ && !field.typ.is_int() && !field.typ.is_float() {
 | 
			
		||||
					g.auto_str_funcs.write('*')
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			g.auto_str_funcs.write(func)
 | 
			
		||||
 | 
			
		||||
			if i < info.fields.len - 1 {
 | 
			
		||||
				g.auto_str_funcs.write(',\n\t\t')
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -363,6 +362,43 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
 | 
			
		|||
	g.auto_str_funcs.writeln('}')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn struct_auto_str_func(sym table.TypeSymbol, field_type table.Type, fn_name, field_name string) string {
 | 
			
		||||
	has_custom_str := sym.has_method('str')
 | 
			
		||||
	if sym.kind == .enum_ {
 | 
			
		||||
		return '${fn_name}(it->${c_name(field_name)})'
 | 
			
		||||
	} else if sym.kind == .struct_ {
 | 
			
		||||
		mut obj := 'it->${c_name(field_name)}'
 | 
			
		||||
		if field_type.is_ptr() {
 | 
			
		||||
			obj = '*$obj'
 | 
			
		||||
		}
 | 
			
		||||
		if has_custom_str {
 | 
			
		||||
			return '${fn_name}($obj)'
 | 
			
		||||
		} else {
 | 
			
		||||
			return 'indent_${fn_name}($obj, indent_count + 1)'
 | 
			
		||||
		}
 | 
			
		||||
	} else if sym.kind in [.array, .array_fixed, .map] {
 | 
			
		||||
		return '${fn_name}(it->${c_name(field_name)})'
 | 
			
		||||
	} else if sym.kind == .sum_type {
 | 
			
		||||
		return 'indent_${fn_name}(it->${c_name(field_name)}, indent_count + 1)'
 | 
			
		||||
	} else {
 | 
			
		||||
		mut method_str := 'it->${c_name(field_name)}'
 | 
			
		||||
		if sym.kind == .bool {
 | 
			
		||||
			method_str += ' ? _SLIT("true") : _SLIT("false")'
 | 
			
		||||
		} else if (field_type.is_int() || field_type.is_float()) && field_type.is_ptr() {
 | 
			
		||||
			// ptr int can be "nil", so this needs to be castet to a string
 | 
			
		||||
			fmt := if sym.kind in [.f32, .f64] {
 | 
			
		||||
				'%g\\000'
 | 
			
		||||
			} else if sym.kind == .u64 {
 | 
			
		||||
				'%lld\\000'
 | 
			
		||||
			} else {
 | 
			
		||||
				'%d\\000'
 | 
			
		||||
			}
 | 
			
		||||
			method_str = '_STR("$fmt", 2, *$method_str)'
 | 
			
		||||
		}
 | 
			
		||||
		return method_str
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) {
 | 
			
		||||
	s := util.no_dots(styp)
 | 
			
		||||
	g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5062,17 +5062,19 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
 | 
			
		|||
 | 
			
		||||
fn (g &Gen) type_to_fmt(typ table.Type) string {
 | 
			
		||||
	sym := g.table.get_type_symbol(typ)
 | 
			
		||||
	if sym.kind in [.struct_, .array, .array_fixed, .map] {
 | 
			
		||||
	if (typ.is_int() || typ.is_float()) && typ.is_ptr() {
 | 
			
		||||
		return '%.*s\\000'
 | 
			
		||||
	} else if typ == table.string_type {
 | 
			
		||||
	} else if sym.kind in [.struct_, .array, .array_fixed, .map] {
 | 
			
		||||
		return '%.*s\\000'
 | 
			
		||||
	} else if sym.kind == .string {
 | 
			
		||||
		return "\'%.*s\\000\'"
 | 
			
		||||
	} else if typ == table.bool_type {
 | 
			
		||||
	} else if sym.kind == .bool {
 | 
			
		||||
		return '%.*s\\000'
 | 
			
		||||
	} else if sym.kind == .enum_ {
 | 
			
		||||
		return '%.*s\\000'
 | 
			
		||||
	} else if typ in [table.f32_type, table.f64_type] {
 | 
			
		||||
	} else if sym.kind in [.f32, .f64] {
 | 
			
		||||
		return '%g\\000' // g removes trailing zeros unlike %f
 | 
			
		||||
	} else if typ == table.u64_type {
 | 
			
		||||
	} else if sym.kind == .u64 {
 | 
			
		||||
		return '%lld\\000'
 | 
			
		||||
	} else if sym.kind == .sum_type {
 | 
			
		||||
		return '%.*s\\000'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,3 +139,60 @@ fn test_fixed_array_of_strings() {
 | 
			
		|||
	assert aa.str() == "['aa', 'bb', 'cc']"
 | 
			
		||||
	assert '$aa' == "['aa', 'bb', 'cc']"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Wrapper {
 | 
			
		||||
	foo &string
 | 
			
		||||
}
 | 
			
		||||
fn test_struct_with_string_pointer() {
 | 
			
		||||
	s := 'test'
 | 
			
		||||
	w := Wrapper{&s}
 | 
			
		||||
	assert '$w' == 'Wrapper {\n    foo: &\'test\'\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper {\n    foo: &\'test\'\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Wrapper2 {
 | 
			
		||||
	foo &int
 | 
			
		||||
}
 | 
			
		||||
fn test_struct_with_int_pointer() {
 | 
			
		||||
	i := 5
 | 
			
		||||
	w := Wrapper2{&i}
 | 
			
		||||
	assert '$w' == 'Wrapper2 {\n    foo: &5\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper2 {\n    foo: &5\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Wrapper3 {
 | 
			
		||||
	foo &bool
 | 
			
		||||
}
 | 
			
		||||
fn test_struct_with_bool_pointer() {
 | 
			
		||||
	b := true
 | 
			
		||||
	w := Wrapper3{&b}
 | 
			
		||||
	assert '$w' == 'Wrapper3 {\n    foo: &true\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper3 {\n    foo: &true\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Foo {}
 | 
			
		||||
struct Wrapper4 {
 | 
			
		||||
	foo &Foo
 | 
			
		||||
}
 | 
			
		||||
fn test_struct_with_struct_pointer() {
 | 
			
		||||
	b := Foo{}
 | 
			
		||||
	w := Wrapper4{&b}
 | 
			
		||||
	assert '$w' == 'Wrapper4 {\n    foo: &Foo {\n    }\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper4 {\n    foo: &Foo {\n    }\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_struct_with_nil() {
 | 
			
		||||
	w := Wrapper4{}
 | 
			
		||||
	assert '$w' == 'Wrapper4 {\n    foo: &nil\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper4 {\n    foo: &nil\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Wrapper5 {
 | 
			
		||||
	foo &f32
 | 
			
		||||
}
 | 
			
		||||
fn test_struct_with_f32_pointer() {
 | 
			
		||||
	i := f32(5.1)
 | 
			
		||||
	w := Wrapper5{&i}
 | 
			
		||||
	assert '$w' == 'Wrapper5 {\n    foo: &5.1\n}'
 | 
			
		||||
	assert w.str() == 'Wrapper5 {\n    foo: &5.1\n}'
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,25 +9,24 @@ type ST = int | string | bool | Abc
 | 
			
		|||
fn test_int_st_str() {
 | 
			
		||||
	a := ST(0)
 | 
			
		||||
	assert '$a' == 'ST(0)'
 | 
			
		||||
	assert a.str() == 'ST(0)'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_string_st_str() {
 | 
			
		||||
	a := ST('test')
 | 
			
		||||
	assert '$a' == 'ST(\'test\')'
 | 
			
		||||
	assert a.str() == 'ST(\'test\')'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_struct_st_str() {
 | 
			
		||||
	a := ST(Abc{})
 | 
			
		||||
	assert '$a' == 'ST(Abc {\n    foo: 0\n    bar: false\n    str: \'\'\n})'
 | 
			
		||||
	assert a.str() == 'ST(Abc {\n    foo: 0\n    bar: false\n    str: \'\'\n})'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_bool_st_str() {
 | 
			
		||||
	a := ST(false)
 | 
			
		||||
	assert '$a' == 'ST(false)'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_str() {
 | 
			
		||||
	a := ST(false)
 | 
			
		||||
	assert a.str() == 'ST(false)'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,17 +37,20 @@ struct Container {
 | 
			
		|||
fn test_in_struct() {
 | 
			
		||||
	c := Container{ST(0)}
 | 
			
		||||
	assert '$c' == 'Container {\n    st: ST(0)\n}'
 | 
			
		||||
	assert c.str() == 'Container {\n    st: ST(0)\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_unknown_value() {
 | 
			
		||||
	c := Container{}
 | 
			
		||||
	assert '$c' == 'Container {\n    st: unknown sum type value\n}'
 | 
			
		||||
	assert c.str() == 'Container {\n    st: unknown sum type value\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_nested_in_struct() {
 | 
			
		||||
	abc := Abc{}
 | 
			
		||||
	c := Container{ST(abc)}
 | 
			
		||||
	assert '$c' == 'Container {\n    st: ST(Abc {\n        foo: 0\n        bar: false\n        str: \'\'\n    })\n}'
 | 
			
		||||
	assert c.str() == 'Container {\n    st: ST(Abc {\n        foo: 0\n        bar: false\n        str: \'\'\n    })\n}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_pointer() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue