1
0
forked from aniani/vim

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 <errael@raelity.com>
Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com>
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Ernie Rael
2025-01-19 10:03:00 +01:00
committed by Christian Brabandt
parent 5abc44e3c1
commit bce60c4742
4 changed files with 223 additions and 54 deletions

View File

@@ -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")); INIT(= N_("E1429: Class can only be used in a script"));
EXTERN char e_uninitialized_object_var_reference[] EXTERN char e_uninitialized_object_var_reference[]
INIT(= N_("E1430: Uninitialized object variable '%s' referenced")); 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 #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[] EXTERN char e_cannot_mix_positional_and_non_positional_str[]
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s")); INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
EXTERN char e_fmt_arg_nr_unused_str[] EXTERN char e_fmt_arg_nr_unused_str[]

View File

@@ -3268,21 +3268,22 @@ def Test_using_base_class()
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
enddef enddef
" Test for using a method from the super class
def Test_super_dispatch() def Test_super_dispatch()
# See #15448 and #15463 # See #15448 and #15463
var lines =<< trim END var lines =<< trim END
vim9script vim9script
class A class A
def String(): string def String(): string
return 'A' return 'A'
enddef enddef
endclass endclass
class B extends A class B extends A
def String(): string def String(): string
return super.String() return super.String()
enddef enddef
endclass endclass
class C extends B class C extends B
@@ -3296,30 +3297,30 @@ def Test_super_dispatch()
vim9script vim9script
class A class A
def F(): string def F(): string
return 'AA' return 'AA'
enddef enddef
endclass endclass
class B extends A class B extends A
def F(): string def F(): string
return 'BB' return 'BB'
enddef enddef
def S(): string def S(): string
return super.F() return super.F()
enddef enddef
def S0(): string def S0(): string
return this.S() return this.S()
enddef enddef
endclass endclass
class C extends B class C extends B
def F(): string def F(): string
return 'CC' return 'CC'
enddef enddef
def ToB(): string def ToB(): string
return super.F() return super.F()
enddef enddef
endclass endclass
assert_equal('AA', B.new().S()) assert_equal('AA', B.new().S())
@@ -3341,51 +3342,51 @@ def Test_super_dispatch()
var call_chain: list<string> var call_chain: list<string>
abstract class A abstract class A
abstract def _G(): string abstract def _G(): string
def F(): string def F(): string
call_chain->add('A.F()') call_chain->add('A.F()')
return this._G() return this._G()
enddef enddef
def _H(): string def _H(): string
call_chain->add('A._H()') call_chain->add('A._H()')
return this.F() return this.F()
enddef enddef
endclass endclass
class B extends A class B extends A
def _G(): string def _G(): string
call_chain->add('B.G()') call_chain->add('B.G()')
return 'BBB' return 'BBB'
enddef enddef
def SF(): string def SF(): string
call_chain->add('B.SF()') call_chain->add('B.SF()')
return super._H() return super._H()
enddef enddef
endclass endclass
class C extends B class C extends B
endclass endclass
class D extends C class D extends C
def SF(): string def SF(): string
call_chain->add('D.SF()') call_chain->add('D.SF()')
return super.SF() return super.SF()
enddef enddef
endclass endclass
class E extends D class E extends D
def SF(): string def SF(): string
call_chain->add('E.SF()') call_chain->add('E.SF()')
return super.SF() return super.SF()
enddef enddef
endclass endclass
class F extends E class F extends E
def _G(): string def _G(): string
call_chain->add('F._G()') call_chain->add('F._G()')
return 'FFF' return 'FFF'
enddef enddef
endclass endclass
# E.new() -> A.F() -> B._G() # 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) assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
END END
v9.CheckSourceSuccess(lines) 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 enddef
def Test_class_import() def Test_class_import()

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1037,
/**/ /**/
1036, 1036,
/**/ /**/

View File

@@ -373,6 +373,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
break; break;
} }
} }
ocmember_T *ocm = NULL; ocmember_T *ocm = NULL;
if (ufunc == 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 // A private object method can be used only inside the class where it
// is defined or in one of the child classes. // is defined or in one of the child classes.
// A private class method can be used only in the class where it is // A private class method can be used only in the class where it is