From: Yegappan Lakshmanan Date: Sun, 9 Feb 2025 18:39:52 +0000 (+0100) Subject: patch 9.1.1094: Vim9: problem finding implemented method in type hierarchy X-Git-Tag: v9.1.1094^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68d08588928b29fe0b19e3513cd689486260ab1c;p=thirdparty%2Fvim.git patch 9.1.1094: Vim9: problem finding implemented method in type hierarchy Problem: Vim9: problem finding implemented method for abstract method in type hierarchy (Aliaksei Budavei) Solution: When checking for abstract methods in an extended class, check whether an abstract method is implemented in one of the parent classes (Yegappan Lakshmanan) fixes: #16495 closes: #16497 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- diff --git a/src/errors.h b/src/errors.h index d8b1ff68f0..34a0d5832f 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3508,7 +3508,7 @@ EXTERN char e_abstract_must_be_followed_by_def[] INIT(= N_("E1371: Abstract must be followed by \"def\"")); EXTERN char e_abstract_method_in_concrete_class[] INIT(= N_("E1372: Abstract method \"%s\" cannot be defined in a concrete class")); -EXTERN char e_abstract_method_str_not_found[] +EXTERN char e_abstract_method_str_not_implemented[] INIT(= N_("E1373: Abstract method \"%s\" is not implemented")); EXTERN char e_class_variable_str_accessible_only_inside_class_str[] INIT(= N_("E1374: Class variable \"%s\" accessible only inside class \"%s\"")); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index c39f18c4d7..0b3ea4a10e 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -12221,4 +12221,157 @@ def Test_constructor_init_compound_member_var() v9.CheckSourceSuccess(lines) enddef +" Test for using a concrete method in an abstract extended class which is +" further extended +def Test_abstract_method_across_hierarchy() + var lines =<< trim END + vim9script + + abstract class A + abstract def Foo(): string + endclass + + abstract class B extends A + abstract def Bar(): string + endclass + + class C extends B + def Foo(): string + return 'foo' + enddef + + def Bar(): string + return 'bar' + enddef + endclass + + def Fn1(a: A): string + return a.Foo() + enddef + + def Fn2(b: B): string + return b.Bar() + enddef + + var c = C.new() + assert_equal('foo', Fn1(c)) + assert_equal('bar', Fn2(c)) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + abstract class A + abstract def Foo(): string + endclass + + abstract class B extends A + abstract def Bar(): string + endclass + + class C extends B + def Bar(): string + return 'bar' + enddef + endclass + + defcompile + END + v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented') + + lines =<< trim END + vim9script + + abstract class A + abstract def M1(): string + abstract def M2(): string + endclass + + abstract class B extends A + def M1(): string + return 'B: M1' + enddef + + def M2(): string + return 'B: M2' + enddef + endclass + + class C1 extends B + def M1(): string + return 'C1: M1' + enddef + endclass + + class C2 extends B + def M2(): string + return 'C2: M2' + enddef + endclass + + class D1 extends C1 + endclass + + class D2 extends C2 + endclass + + var l: list = [] + for Type in ['C1', 'C2', 'D1', 'D2'] + l->add(eval($'{Type}.new().M1()')) + l->add(eval($'{Type}.new().M2()')) + endfor + assert_equal(['C1: M1', 'B: M2', 'B: M1', 'C2: M2', 'C1: M1', 'B: M2', 'B: M1', 'C2: M2'], l) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + abstract class A + abstract def M1(): string + abstract def M2(): string + endclass + + class B extends A + def M1(): string + return 'B: M1' + enddef + + def M2(): string + return 'B: M2' + enddef + endclass + + abstract class C extends B + endclass + + class D1 extends C + def M1(): string + return 'D1: M1' + enddef + endclass + + class D2 extends C + def M2(): string + return 'D2: M2' + enddef + endclass + + class E1 extends D1 + endclass + + class E2 extends D2 + endclass + + var l: list = [] + for Type in ['B', 'D1', 'D2', 'E1', 'E2'] + l->add(eval($'{Type}.new().M1()')) + l->add( eval($'{Type}.new().M2()')) + endfor + assert_equal(['B: M1', 'B: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2'], l) + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 1bc3bcccbc..c7b0706bbf 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1094, /**/ 1093, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index e847bf0864..47ab2369d2 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -561,20 +561,34 @@ validate_abstract_class_methods( if (!IS_ABSTRACT_METHOD(uf)) continue; - int method_found = FALSE; + int concrete_method_found = FALSE; + int j = 0; - for (int j = 0; j < method_count; j++) + // Check if the abstract method is already implemented in one of + // the parent classes. + for (j = 0; !concrete_method_found && j < i; j++) + { + ufunc_T *uf2 = extends_methods[j]; + if (!IS_ABSTRACT_METHOD(uf2) && + STRCMP(uf->uf_name, uf2->uf_name) == 0) + concrete_method_found = TRUE; + } + + if (concrete_method_found) + continue; + + for (j = 0; j < method_count; j++) { if (STRCMP(uf->uf_name, cl_fp[j]->uf_name) == 0) { - method_found = TRUE; + concrete_method_found = TRUE; break; } } - if (!method_found) + if (!concrete_method_found) { - semsg(_(e_abstract_method_str_not_found), uf->uf_name); + semsg(_(e_abstract_method_str_not_implemented), uf->uf_name); return FALSE; } }