diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index dc1b8c2d90..d1b7325c81 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -210,13 +210,14 @@ pub fn (mut f Fmt) short_module(name string) string { return f.mod2alias[name] } if name.ends_with('>') { - x := name.trim_suffix('>').split('<') - if x.len == 2 { - main := f.short_module(x[0]) - genlist := x[1].split(',') - genshorts := genlist.map(f.short_module(it)).join(',') - return '$main<$genshorts>' + generic_levels := name.trim_suffix('>').split('<') + mut res := '${f.short_module(generic_levels[0])}' + for i in 1 .. generic_levels.len { + genshorts := generic_levels[i].split(',').map(f.short_module(it)).join(',') + res += '<$genshorts' } + res += '>' + return res } vals := name.split('.') if vals.len < 2 { @@ -1585,15 +1586,11 @@ fn (mut f Fmt) write_generic_call_if_require(node ast.CallExpr) { if node.concrete_types.len > 0 { f.write('<') for i, concrete_type in node.concrete_types { - f.write(f.table.type_to_str_using_aliases(concrete_type, f.mod2alias)) + f.write(f.short_module(f.table.type_to_str_using_aliases(concrete_type, f.mod2alias))) if i != node.concrete_types.len - 1 { f.write(', ') } } - // avoid `>` => ` >` - if f.out.last_n(1) == '>' { - f.write(' ') - } f.write('>') } } diff --git a/vlib/v/fmt/tests/generics_cascade_types_keep.vv b/vlib/v/fmt/tests/generics_cascade_types_keep.vv index 82fd8dd3f2..3e30adc357 100644 --- a/vlib/v/fmt/tests/generics_cascade_types_keep.vv +++ b/vlib/v/fmt/tests/generics_cascade_types_keep.vv @@ -23,5 +23,5 @@ fn main() { // generic assert multi_generic_args(0, 's') assert multi_generic_args(Foo1{}, Foo2{}) - assert multi_generic_args, Foo >(Foo{}, Foo{}) + assert multi_generic_args, Foo>(Foo{}, Foo{}) } diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 583cb8b966..e7fe9fd207 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -916,9 +916,14 @@ fn (mut s Scanner) text_scan() token.Token { s.pos++ return s.new_token(.ge, '', 2) } else if nextc == `>` { - if s.pos + 2 < s.text.len && s.text[s.pos + 2] == `=` { - s.pos += 2 - return s.new_token(.right_shift_assign, '', 3) + if s.pos + 2 < s.text.len { + if s.text[s.pos + 2] == `=` { + s.pos += 2 + return s.new_token(.right_shift_assign, '', 3) + } else if s.text[s.pos + 2] in [`(`, `)`, `{`, `>`, `,`] { + // multi-level generics such as Foo>{ }, func>( ), etc + return s.new_token(.gt, '', 1) + } } s.pos++ return s.new_token(.right_shift, '', 2) diff --git a/vlib/v/tests/generics_test.v b/vlib/v/tests/generics_test.v index 01bd47ee3d..8a6a9fb5ca 100644 --- a/vlib/v/tests/generics_test.v +++ b/vlib/v/tests/generics_test.v @@ -481,6 +481,35 @@ fn return_one(rec int, useless T) T { return T(0) } +struct MultiLevel { + foo T +} + +fn get_multilevel_foo(bar MultiLevel) int { + return bar.foo.foo +} + +fn get_multilevel_foo_2(bar T, baz U) int { + return bar.foo.foo + baz.foo.foo +} + +fn test_multi_level_generics() { + one := MultiLevel{ + foo: 10 + } + two := MultiLevel>{ + foo: one + } + assert two.foo.foo == 10 + three := MultiLevel>>{ + foo: two + } + assert three.foo.foo.foo == 10 + assert get_multilevel_foo>(two) == 10 + assert get_multilevel_foo_2>, MultiLevel>>(two, + two) == 20 +} + fn test_generic_detection() { v1, v2 := -1, 1 @@ -493,7 +522,7 @@ fn test_generic_detection() { // generic assert multi_generic_args(0, 's') assert multi_generic_args(Foo1{}, Foo2{}) - assert multi_generic_args, Foo >(Foo{}, Foo{}) + assert multi_generic_args, Foo>(Foo{}, Foo{}) // TODO: assert multi_generic_args, Foo>(Foo1{}, Foo2{}) assert multi_generic_args(simplemodule.Data{}, 0)