patch 9.1.1037: Vim9: confusing error when using abstract method via super

Commit: 
https://github.com/vim/vim/commit/bce60c4742a125c070c3c4214332842cb861061c
Author: Ernie Rael <err...@raelity.com>
Date:   Sun Jan 19 10:03:00 2025 +0100

    patch 9.1.1037: Vim9: confusing error when using abstract method via super
    
    Problem:  Vim9: confusing error when using abstract method via super
    Solution: Display an error when an abstract method is invoked using
              super (Ernie Rael)
    
    fixes: #15514
    closes: #16478
    
    Signed-off-by: Ernie Rael <err...@raelity.com>
    Signed-off-by: Aliaksei Budavei <0x000...@gmail.com>
    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 6bab82650..4d2818a81 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3619,8 +3619,10 @@ EXTERN char e_class_can_only_be_used_in_script[]
        INIT(= N_("E1429: Class can only be used in a script"));
 EXTERN char e_uninitialized_object_var_reference[]
        INIT(= N_("E1430: Uninitialized object variable '%s' referenced"));
+EXTERN char e_abstract_method_str_direct[]
+       INIT(= N_("E1431: Abstract method \"%s\" in class \"%s\" cannot be 
accessed directly"));
 #endif
-// E1431 - E1499 unused (reserved for Vim9 class support)
+// E1432 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
        INIT(= N_("E1500: Cannot mix positional and non-positional arguments: 
%s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index cac8b63c7..3b3ea2015 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3268,21 +3268,22 @@ def Test_using_base_class()
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Test for using a method from the super class
 def Test_super_dispatch()
   # See #15448 and #15463
   var lines =<< trim END
     vim9script
 
     class A
-        def String(): string
-            return 'A'
-        enddef
+      def String(): string
+        return 'A'
+      enddef
     endclass
 
     class B extends A
-        def String(): string
-            return super.String()
-        enddef
+      def String(): string
+        return super.String()
+      enddef
     endclass
 
     class C extends B
@@ -3296,30 +3297,30 @@ def Test_super_dispatch()
     vim9script
 
     class A
-        def F(): string
-            return 'AA'
-        enddef
+      def F(): string
+        return 'AA'
+      enddef
     endclass
 
     class B extends A
-        def F(): string
-            return 'BB'
-        enddef
-        def S(): string
-            return super.F()
-        enddef
-        def S0(): string
-            return this.S()
-        enddef
+      def F(): string
+        return 'BB'
+      enddef
+      def S(): string
+        return super.F()
+      enddef
+      def S0(): string
+        return this.S()
+      enddef
     endclass
 
     class C extends B
-        def F(): string
-            return 'CC'
-        enddef
-        def ToB(): string
-            return super.F()
-        enddef
+      def F(): string
+        return 'CC'
+      enddef
+      def ToB(): string
+        return super.F()
+      enddef
     endclass
 
     assert_equal('AA', B.new().S())
@@ -3341,51 +3342,51 @@ def Test_super_dispatch()
     var call_chain: list<string>
 
     abstract class A
-        abstract def _G(): string
+      abstract def _G(): string
 
-        def F(): string
-            call_chain->add('A.F()')
-            return this._G()
-        enddef
-        def _H(): string
-            call_chain->add('A._H()')
-            return this.F()
-        enddef
+      def F(): string
+        call_chain->add('A.F()')
+        return this._G()
+      enddef
+      def _H(): string
+        call_chain->add('A._H()')
+        return this.F()
+      enddef
     endclass
 
     class B extends A
-        def _G(): string
-            call_chain->add('B.G()')
-            return 'BBB'
-        enddef
-        def SF(): string
-            call_chain->add('B.SF()')
-            return super._H()
-        enddef
+      def _G(): string
+        call_chain->add('B.G()')
+        return 'BBB'
+      enddef
+      def SF(): string
+        call_chain->add('B.SF()')
+        return super._H()
+      enddef
     endclass
 
     class C extends B
     endclass
 
     class D extends C
-        def SF(): string
-            call_chain->add('D.SF()')
-            return super.SF()
-        enddef
+      def SF(): string
+        call_chain->add('D.SF()')
+        return super.SF()
+      enddef
     endclass
 
     class E extends D
-        def SF(): string
-            call_chain->add('E.SF()')
-            return super.SF()
-        enddef
+      def SF(): string
+        call_chain->add('E.SF()')
+        return super.SF()
+      enddef
     endclass
 
     class F extends E
-        def _G(): string
-            call_chain->add('F._G()')
-            return 'FFF'
-        enddef
+      def _G(): string
+        call_chain->add('F._G()')
+        return 'FFF'
+      enddef
     endclass
 
     # E.new() -> A.F() -> B._G()
@@ -3401,6 +3402,160 @@ def Test_super_dispatch()
     assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], 
call_chain)
   END
   v9.CheckSourceSuccess(lines)
+
+  # problems with method dispatch: super -> abstract
+  # https://github.com/vim/vim/issues/15514
+  lines =<< trim END
+    vim9script
+    abstract class B
+      abstract def ToString(): string
+    endclass
+
+    class C extends B
+      def ToString(): string
+        return super.ToString()
+      enddef
+    endclass
+
+    try
+      defcompile C.ToString
+      call assert_false(1, 'command should have failed')
+    catch
+      call assert_exception('E1431: Abstract method "ToString" in class "B" 
cannot be accessed directly')
+    endtry
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # problems with method dispatch: super -> abstract -> concrete
+  lines =<< trim END
+    vim9script
+
+    class A
+      def ToString()
+        echo 'A'
+      enddef
+    endclass
+
+    abstract class B extends A
+      abstract def ToString()
+    endclass
+
+    class C extends B
+      def ToString()
+        super.ToString()
+      enddef
+    endclass
+
+    try
+      defcompile C.ToString
+      call assert_false(1, 'command should have failed')
+    catch
+      call assert_exception('E1431: Abstract method "ToString" in class "B" 
cannot be accessed directly')
+    endtry
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Invoking a super method and an interface method which have the same name.
+  lines =<< trim END
+    vim9script
+
+    interface I
+      def ToString(): string
+    endinterface
+
+    # Note that A does not implement I.
+    class A
+      def ToString(): string
+        return 'A'
+      enddef
+    endclass
+
+    class B extends A implements I
+      def ToString(): string
+        return super.ToString()
+      enddef
+    endclass
+
+     def TestI(i: I): string
+       return i.ToString()
+     enddef
+
+     assert_equal('A', B.new().ToString())
+     assert_equal('A', TestI(B.new()))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # super and an abstract class with no abstract methods
+  lines =<< trim END
+    vim9script
+
+    class A
+      def ToString(): string
+        return 'A'
+      enddef
+    endclass
+
+    # An abstract class with no abstract methods.
+    abstract class B extends A
+    endclass
+
+    class C extends B
+      def ToString(): string
+        return super.ToString()
+      enddef
+    endclass
+
+    def TestA(a: A): string
+      return a.ToString()
+    enddef
+
+    def TestB(b: B): string
+      return b.ToString()
+    enddef
+
+    assert_equal('A', C.new().ToString())
+    assert_equal('A', TestA(A.new()))
+    assert_equal('A', TestA(C.new()))
+    assert_equal('A', TestB(C.new()))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # super and an abstract class with no abstract methods and the initial
+  # implements clause
+  lines =<< trim END
+    vim9script
+
+    interface I
+      def ToString(): string
+    endinterface
+
+    # Note that A does not implement I.
+    class A
+      def ToString(): string
+        return 'A'
+      enddef
+    endclass
+
+    # An abstract class with no abstract methods.
+    abstract class B extends A implements I
+    endclass
+
+    class C extends B implements I
+      def ToString(): string
+        return super.ToString()
+      enddef
+    endclass
+
+    # Note that A.ToString() is different from I.ToString().
+    def TestA(a: A): string
+      return a.ToString()
+    enddef
+
+    assert_equal('A', C.new().ToString())
+    assert_equal('A', TestA(A.new()))
+    assert_equal('A', TestA(C.new()))
+  END
+  v9.CheckSourceSuccess(lines)
 enddef
 
 def Test_class_import()
diff --git a/src/version.c b/src/version.c
index 9542bcb94..9bd759932 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 */
+/**/
+    1037,
 /**/
     1036,
 /**/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 9a9603454..7b8c96adb 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -373,6 +373,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
                break;
            }
        }
+
        ocmember_T  *ocm = NULL;
        if (ufunc == NULL)
        {
@@ -405,6 +406,15 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
            }
        }
 
+       if (is_super && IS_ABSTRACT_METHOD(ufunc))
+       {
+           // Trying to invoke an abstract method in a super class is not
+           // allowed.
+           semsg(_(e_abstract_method_str_direct), ufunc->uf_name,
+                   ufunc->uf_defclass->class_name);
+           return FAIL;
+       }
+
        // A private object method can be used only inside the class where it
        // is defined or in one of the child classes.
        // A private class method can be used only in the class where it is

-- 
-- 
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/E1tZROa-004b0P-Q3%40256bit.org.

Raspunde prin e-mail lui