patch 9.1.1094: Vim9: problem finding implemented method in type hierarchy

Commit: 
https://github.com/vim/vim/commit/68d08588928b29fe0b19e3513cd689486260ab1c
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Sun Feb 9 19:39:52 2025 +0100

    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 <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/errors.h b/src/errors.h
index d8b1ff68f..34a0d5832 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 c39f18c4d..0b3ea4a10 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<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 1bc3bcccb..c7b0706bb 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 e847bf086..47ab2369d 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;
            }
        }

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1thCIh-00Go7P-Sc%40256bit.org.

Raspunde prin e-mail lui