From a50f2ca5e8e8512e74c2932384b1933d46a02be3 Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Wed, 3 Mar 2021 15:02:10 +1100 Subject: [PATCH] interfaces: error on implemention of own interface method & on duplicate normal methods --- vlib/v/checker/checker.v | 14 +++++++++--- ...face_implementing_own_interface_method.out | 7 ++++++ ...rface_implementing_own_interface_method.vv | 7 ++++++ vlib/v/parser/fn.v | 22 +++++++++++++------ .../interface_duplicate_interface_method.out | 6 +++++ .../interface_duplicate_interface_method.vv | 5 +++++ .../tests/interface_duplicate_method.out | 11 +++++----- .../tests/interface_duplicate_method.vv | 9 ++++---- vlib/v/table/types.v | 16 ++++++++++++++ 9 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 vlib/v/checker/tests/interface_implementing_own_interface_method.out create mode 100644 vlib/v/checker/tests/interface_implementing_own_interface_method.vv create mode 100644 vlib/v/parser/tests/interface_duplicate_interface_method.out create mode 100644 vlib/v/parser/tests/interface_duplicate_interface_method.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 1fe16b4fda..ed49a4415b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5841,9 +5841,17 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { c.error('unknown type `$sym.name`', node.receiver_pos) return } - // if sym.has_method(node.name) { - // c.warn('duplicate method `$node.name`', node.pos) - // } + // make sure interface does not implement its own interface methods + if sym.kind == .interface_ && sym.has_method(node.name) { + if sym.info is table.Interface { + info := sym.info as table.Interface + // if the method is in info.methods then it is an interface method + if info.has_method(node.name) { + c.error('interface `$sym.name` cannot implement its own interface method `$node.name`', + node.pos) + } + } + } // needed for proper error reporting during vweb route checking sym.methods[node.method_idx].source_fn = voidptr(node) } diff --git a/vlib/v/checker/tests/interface_implementing_own_interface_method.out b/vlib/v/checker/tests/interface_implementing_own_interface_method.out new file mode 100644 index 0000000000..afa487650c --- /dev/null +++ b/vlib/v/checker/tests/interface_implementing_own_interface_method.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/interface_implementing_own_interface_method.vv:5:1: error: interface `Animal` cannot implement its own interface method `speak` + 3 | } + 4 | + 5 | fn (a Animal) speak() { + | ~~~~~~~~~~~~~~~~~~~~~ + 6 | println('speaking') + 7 | } diff --git a/vlib/v/checker/tests/interface_implementing_own_interface_method.vv b/vlib/v/checker/tests/interface_implementing_own_interface_method.vv new file mode 100644 index 0000000000..9881bd7900 --- /dev/null +++ b/vlib/v/checker/tests/interface_implementing_own_interface_method.vv @@ -0,0 +1,7 @@ +interface Animal { + speak() +} + +fn (a Animal) speak() { + println('speaking') +} \ No newline at end of file diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index e3eb079466..c40696c53b 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -221,11 +221,20 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } } type_sym := p.table.get_type_symbol(rec.typ) - // interfaces are handled in the checker, methods can not be defined on them this way - if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) { - p.error_with_pos('duplicate method `$name`', pos) - return ast.FnDecl{ - scope: 0 + if is_method { + mut is_duplicate := type_sym.has_method(name) + // make sure this is a normal method and not an interface method + if type_sym.kind == .interface_ && is_duplicate { + if type_sym.info is table.Interface { + // if the method is in info then its an interface method + is_duplicate = !type_sym.info.has_method(name) + } + } + if is_duplicate { + p.error_with_pos('duplicate method `$name`', pos) + return ast.FnDecl{ + scope: 0 + } } } // cannot redefine buildin function @@ -276,7 +285,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl { }) } } - mut end_pos := p.prev_tok.position() // Return type mut return_type := table.void_type // don't confuse token on the next line: fn decl, [attribute] @@ -287,6 +295,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } mut type_sym_method_idx := 0 no_body := p.tok.kind != .lcbr + end_pos := p.prev_tok.position() // Register if is_method { mut type_sym := p.table.get_type_symbol(rec.typ) @@ -348,7 +357,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl { language: language }) } - end_pos = p.prev_tok.position() // Body p.cur_fn_name = name mut stmts := []ast.Stmt{} diff --git a/vlib/v/parser/tests/interface_duplicate_interface_method.out b/vlib/v/parser/tests/interface_duplicate_interface_method.out new file mode 100644 index 0000000000..eaee38effa --- /dev/null +++ b/vlib/v/parser/tests/interface_duplicate_interface_method.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/interface_duplicate_interface_method.vv:4:2: error: duplicate method `fun` + 2 | interface Abc { + 3 | fun() + 4 | fun() + | ~~~ + 5 | } diff --git a/vlib/v/parser/tests/interface_duplicate_interface_method.vv b/vlib/v/parser/tests/interface_duplicate_interface_method.vv new file mode 100644 index 0000000000..4d293838e0 --- /dev/null +++ b/vlib/v/parser/tests/interface_duplicate_interface_method.vv @@ -0,0 +1,5 @@ +// duplicate interface methods in decleration +interface Abc { + fun() + fun() +} diff --git a/vlib/v/parser/tests/interface_duplicate_method.out b/vlib/v/parser/tests/interface_duplicate_method.out index a45c470819..f83ad2ab59 100644 --- a/vlib/v/parser/tests/interface_duplicate_method.out +++ b/vlib/v/parser/tests/interface_duplicate_method.out @@ -1,6 +1,5 @@ -vlib/v/parser/tests/interface_duplicate_method.vv:3:2: error: duplicate method `fun` - 1 | interface Abc { - 2 | fun() - 3 | fun() - | ~~~ - 4 | } +vlib/v/parser/tests/interface_duplicate_method.vv:5:12: error: duplicate method `foo` + 3 | // duplicate normal method definitions on interface + 4 | fn (a Abc) foo() {} + 5 | fn (a Abc) foo() {} + | ~~~ diff --git a/vlib/v/parser/tests/interface_duplicate_method.vv b/vlib/v/parser/tests/interface_duplicate_method.vv index 96fdc37143..ab223301ac 100644 --- a/vlib/v/parser/tests/interface_duplicate_method.vv +++ b/vlib/v/parser/tests/interface_duplicate_method.vv @@ -1,4 +1,5 @@ -interface Abc { - fun() - fun() -} +interface Abc {} + +// duplicate normal method definitions on interface +fn (a Abc) foo() {} +fn (a Abc) foo() {} \ No newline at end of file diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 973ef673f8..9a846e8192 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -993,6 +993,22 @@ pub fn (i &Interface) find_field(name string) ?Field { return none } +pub fn (i &Interface) find_method(name string) ?Fn { + for method in i.methods { + if method.name == name { + return method + } + } + return none +} + +pub fn (i &Interface) has_method(name string) bool { + if _ := i.find_method(name) { + return true + } + return false +} + pub fn (s Struct) find_field(name string) ?Field { for field in s.fields { if field.name == name {