diff --git a/vlib/v/checker/interface.v b/vlib/v/checker/interface.v index fc09bc6cea..8e62cf9ba5 100644 --- a/vlib/v/checker/interface.v +++ b/vlib/v/checker/interface.v @@ -40,6 +40,18 @@ pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { embed.pos) continue } + // Ensure each generic type of the embed was declared in the interface's definition + if node.generic_types.len > 0 && embed.typ.has_flag(.generic) { + embed_generic_names := c.table.generic_type_names(embed.typ) + node_generic_names := node.generic_types.map(c.table.type_to_str(it)) + for name in embed_generic_names { + if name !in node_generic_names { + interface_generic_names := node_generic_names.join(', ') + c.error('generic type name `$name` is not mentioned in interface `$node.name<$interface_generic_names>`', + embed.pos) + } + } + } isym_info := isym.info as ast.Interface for f in isym_info.fields { if !efnames_ds_info[f.name] { @@ -115,6 +127,18 @@ pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { } if method.return_type.has_flag(.generic) { has_generic_types = true + // Ensure each generic type of the method was declared in the interface's definition + if node.generic_types.len > 0 { + method_generic_names := c.table.generic_type_names(method.return_type) + node_generic_names := node.generic_types.map(c.table.type_to_str(it)) + for name in method_generic_names { + if name !in node_generic_names { + interface_generic_names := node_generic_names.join(', ') + c.error('generic type name `$name` is not mentioned in interface `$node.name<$interface_generic_names>`', + method.return_type_pos) + } + } + } } for j, param in method.params { if j == 0 && is_js { @@ -128,6 +152,18 @@ pub fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) { c.error('invalid use of reserved type `$param.name` as a parameter name', param.pos) } + // Ensure each generic type of the method was declared in the interface's definition + if node.generic_types.len > 0 && param.typ.has_flag(.generic) { + method_generic_names := c.table.generic_type_names(param.typ) + node_generic_names := node.generic_types.map(c.table.type_to_str(it)) + for name in method_generic_names { + if name !in node_generic_names { + interface_generic_names := node_generic_names.join(', ') + c.error('generic type name `$name` is not mentioned in interface `$node.name<$interface_generic_names>`', + param.type_pos) + } + } + } if is_js { ptyp := c.table.sym(param.typ) if !ptyp.is_js_compatible() && !(j == method.params.len - 1 diff --git a/vlib/v/checker/tests/generics_interface_decl_no_mention_err.out b/vlib/v/checker/tests/generics_interface_decl_no_mention_err.out new file mode 100644 index 0000000000..25d3e737c4 --- /dev/null +++ b/vlib/v/checker/tests/generics_interface_decl_no_mention_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv:4:2: error: generic type name `T` is not mentioned in interface `Foo` + 2 | + 3 | interface Foo { + 4 | Bar + | ~~~ + 5 | foo(u U, p P) + 6 | bar(u U) []P +vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv:5:13: error: generic type name `P` is not mentioned in interface `Foo` + 3 | interface Foo { + 4 | Bar + 5 | foo(u U, p P) + | ^ + 6 | bar(u U) []P + 7 | } +vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv:6:11: error: generic type name `P` is not mentioned in interface `Foo` + 4 | Bar + 5 | foo(u U, p P) + 6 | bar(u U) []P + | ~~~ + 7 | } + 8 | diff --git a/vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv b/vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv new file mode 100644 index 0000000000..2f934a65d5 --- /dev/null +++ b/vlib/v/checker/tests/generics_interface_decl_no_mention_err.vv @@ -0,0 +1,10 @@ +fn main() {} + +interface Foo { + Bar + foo(u U, p P) + bar(u U) []P +} + +interface Bar { +}