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.