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 <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/errors.h b/src/errors.h
index d8b1ff6..34a0d58 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3508,7 +3508,7 @@
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 c39f18c..0b3ea4a 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -12221,4 +12221,157 @@
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<string> = []
+ 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<string> = []
+ 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 1bc3bcc..c7b0706 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1094,
+/**/
1093,
/**/
1092,
diff --git a/src/vim9class.c b/src/vim9class.c
index e847bf0..47ab236 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -561,20 +561,34 @@
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;
}
}