Skip to content

Commit bce60c4

Browse files
erraelchrisbra
authored andcommitted
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>
1 parent 5abc44e commit bce60c4

4 files changed

Lines changed: 223 additions & 54 deletions

File tree

src/errors.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3619,8 +3619,10 @@ EXTERN char e_class_can_only_be_used_in_script[]
36193619
INIT(= N_("E1429: Class can only be used in a script"));
36203620
EXTERN char e_uninitialized_object_var_reference[]
36213621
INIT(= N_("E1430: Uninitialized object variable '%s' referenced"));
3622+
EXTERN char e_abstract_method_str_direct[]
3623+
INIT(= N_("E1431: Abstract method \"%s\" in class \"%s\" cannot be accessed directly"));
36223624
#endif
3623-
// E1431 - E1499 unused (reserved for Vim9 class support)
3625+
// E1432 - E1499 unused (reserved for Vim9 class support)
36243626
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
36253627
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
36263628
EXTERN char e_fmt_arg_nr_unused_str[]

src/testdir/test_vim9_class.vim

Lines changed: 208 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3268,21 +3268,22 @@ def Test_using_base_class()
32683268
v9.CheckSourceSuccess(lines)
32693269
enddef
32703270

3271+
" Test for using a method from the super class
32713272
def Test_super_dispatch()
32723273
# See #15448 and #15463
32733274
var lines =<< trim END
32743275
vim9script
32753276

32763277
class A
3277-
def String(): string
3278-
return 'A'
3279-
enddef
3278+
def String(): string
3279+
return 'A'
3280+
enddef
32803281
endclass
32813282

32823283
class B extends A
3283-
def String(): string
3284-
return super.String()
3285-
enddef
3284+
def String(): string
3285+
return super.String()
3286+
enddef
32863287
endclass
32873288

32883289
class C extends B
@@ -3296,30 +3297,30 @@ def Test_super_dispatch()
32963297
vim9script
32973298

32983299
class A
3299-
def F(): string
3300-
return 'AA'
3301-
enddef
3300+
def F(): string
3301+
return 'AA'
3302+
enddef
33023303
endclass
33033304

33043305
class B extends A
3305-
def F(): string
3306-
return 'BB'
3307-
enddef
3308-
def S(): string
3309-
return super.F()
3310-
enddef
3311-
def S0(): string
3312-
return this.S()
3313-
enddef
3306+
def F(): string
3307+
return 'BB'
3308+
enddef
3309+
def S(): string
3310+
return super.F()
3311+
enddef
3312+
def S0(): string
3313+
return this.S()
3314+
enddef
33143315
endclass
33153316

33163317
class C extends B
3317-
def F(): string
3318-
return 'CC'
3319-
enddef
3320-
def ToB(): string
3321-
return super.F()
3322-
enddef
3318+
def F(): string
3319+
return 'CC'
3320+
enddef
3321+
def ToB(): string
3322+
return super.F()
3323+
enddef
33233324
endclass
33243325

33253326
assert_equal('AA', B.new().S())
@@ -3341,51 +3342,51 @@ def Test_super_dispatch()
33413342
var call_chain: list<string>
33423343

33433344
abstract class A
3344-
abstract def _G(): string
3345+
abstract def _G(): string
33453346

3346-
def F(): string
3347-
call_chain->add('A.F()')
3348-
return this._G()
3349-
enddef
3350-
def _H(): string
3351-
call_chain->add('A._H()')
3352-
return this.F()
3353-
enddef
3347+
def F(): string
3348+
call_chain->add('A.F()')
3349+
return this._G()
3350+
enddef
3351+
def _H(): string
3352+
call_chain->add('A._H()')
3353+
return this.F()
3354+
enddef
33543355
endclass
33553356

33563357
class B extends A
3357-
def _G(): string
3358-
call_chain->add('B.G()')
3359-
return 'BBB'
3360-
enddef
3361-
def SF(): string
3362-
call_chain->add('B.SF()')
3363-
return super._H()
3364-
enddef
3358+
def _G(): string
3359+
call_chain->add('B.G()')
3360+
return 'BBB'
3361+
enddef
3362+
def SF(): string
3363+
call_chain->add('B.SF()')
3364+
return super._H()
3365+
enddef
33653366
endclass
33663367

33673368
class C extends B
33683369
endclass
33693370

33703371
class D extends C
3371-
def SF(): string
3372-
call_chain->add('D.SF()')
3373-
return super.SF()
3374-
enddef
3372+
def SF(): string
3373+
call_chain->add('D.SF()')
3374+
return super.SF()
3375+
enddef
33753376
endclass
33763377

33773378
class E extends D
3378-
def SF(): string
3379-
call_chain->add('E.SF()')
3380-
return super.SF()
3381-
enddef
3379+
def SF(): string
3380+
call_chain->add('E.SF()')
3381+
return super.SF()
3382+
enddef
33823383
endclass
33833384

33843385
class F extends E
3385-
def _G(): string
3386-
call_chain->add('F._G()')
3387-
return 'FFF'
3388-
enddef
3386+
def _G(): string
3387+
call_chain->add('F._G()')
3388+
return 'FFF'
3389+
enddef
33893390
endclass
33903391

33913392
# E.new() -> A.F() -> B._G()
@@ -3401,6 +3402,160 @@ def Test_super_dispatch()
34013402
assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
34023403
END
34033404
v9.CheckSourceSuccess(lines)
3405+
3406+
# problems with method dispatch: super -> abstract
3407+
# https://github.com/vim/vim/issues/15514
3408+
lines =<< trim END
3409+
vim9script
3410+
abstract class B
3411+
abstract def ToString(): string
3412+
endclass
3413+
3414+
class C extends B
3415+
def ToString(): string
3416+
return super.ToString()
3417+
enddef
3418+
endclass
3419+
3420+
try
3421+
defcompile C.ToString
3422+
call assert_false(1, 'command should have failed')
3423+
catch
3424+
call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
3425+
endtry
3426+
END
3427+
v9.CheckSourceSuccess(lines)
3428+
3429+
# problems with method dispatch: super -> abstract -> concrete
3430+
lines =<< trim END
3431+
vim9script
3432+
3433+
class A
3434+
def ToString()
3435+
echo 'A'
3436+
enddef
3437+
endclass
3438+
3439+
abstract class B extends A
3440+
abstract def ToString()
3441+
endclass
3442+
3443+
class C extends B
3444+
def ToString()
3445+
super.ToString()
3446+
enddef
3447+
endclass
3448+
3449+
try
3450+
defcompile C.ToString
3451+
call assert_false(1, 'command should have failed')
3452+
catch
3453+
call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
3454+
endtry
3455+
END
3456+
v9.CheckSourceSuccess(lines)
3457+
3458+
# Invoking a super method and an interface method which have the same name.
3459+
lines =<< trim END
3460+
vim9script
3461+
3462+
interface I
3463+
def ToString(): string
3464+
endinterface
3465+
3466+
# Note that A does not implement I.
3467+
class A
3468+
def ToString(): string
3469+
return 'A'
3470+
enddef
3471+
endclass
3472+
3473+
class B extends A implements I
3474+
def ToString(): string
3475+
return super.ToString()
3476+
enddef
3477+
endclass
3478+
3479+
def TestI(i: I): string
3480+
return i.ToString()
3481+
enddef
3482+
3483+
assert_equal('A', B.new().ToString())
3484+
assert_equal('A', TestI(B.new()))
3485+
END
3486+
v9.CheckSourceSuccess(lines)
3487+
3488+
# super and an abstract class with no abstract methods
3489+
lines =<< trim END
3490+
vim9script
3491+
3492+
class A
3493+
def ToString(): string
3494+
return 'A'
3495+
enddef
3496+
endclass
3497+
3498+
# An abstract class with no abstract methods.
3499+
abstract class B extends A
3500+
endclass
3501+
3502+
class C extends B
3503+
def ToString(): string
3504+
return super.ToString()
3505+
enddef
3506+
endclass
3507+
3508+
def TestA(a: A): string
3509+
return a.ToString()
3510+
enddef
3511+
3512+
def TestB(b: B): string
3513+
return b.ToString()
3514+
enddef
3515+
3516+
assert_equal('A', C.new().ToString())
3517+
assert_equal('A', TestA(A.new()))
3518+
assert_equal('A', TestA(C.new()))
3519+
assert_equal('A', TestB(C.new()))
3520+
END
3521+
v9.CheckSourceSuccess(lines)
3522+
3523+
# super and an abstract class with no abstract methods and the initial
3524+
# implements clause
3525+
lines =<< trim END
3526+
vim9script
3527+
3528+
interface I
3529+
def ToString(): string
3530+
endinterface
3531+
3532+
# Note that A does not implement I.
3533+
class A
3534+
def ToString(): string
3535+
return 'A'
3536+
enddef
3537+
endclass
3538+
3539+
# An abstract class with no abstract methods.
3540+
abstract class B extends A implements I
3541+
endclass
3542+
3543+
class C extends B implements I
3544+
def ToString(): string
3545+
return super.ToString()
3546+
enddef
3547+
endclass
3548+
3549+
# Note that A.ToString() is different from I.ToString().
3550+
def TestA(a: A): string
3551+
return a.ToString()
3552+
enddef
3553+
3554+
assert_equal('A', C.new().ToString())
3555+
assert_equal('A', TestA(A.new()))
3556+
assert_equal('A', TestA(C.new()))
3557+
END
3558+
v9.CheckSourceSuccess(lines)
34043559
enddef
34053560

34063561
def Test_class_import()

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,8 @@ static char *(features[]) =
704704

705705
static int included_patches[] =
706706
{ /* Add new patch number below this line */
707+
/**/
708+
1037,
707709
/**/
708710
1036,
709711
/**/

src/vim9expr.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
373373
break;
374374
}
375375
}
376+
376377
ocmember_T *ocm = NULL;
377378
if (ufunc == NULL)
378379
{
@@ -405,6 +406,15 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
405406
}
406407
}
407408

409+
if (is_super && IS_ABSTRACT_METHOD(ufunc))
410+
{
411+
// Trying to invoke an abstract method in a super class is not
412+
// allowed.
413+
semsg(_(e_abstract_method_str_direct), ufunc->uf_name,
414+
ufunc->uf_defclass->class_name);
415+
return FAIL;
416+
}
417+
408418
// A private object method can be used only inside the class where it
409419
// is defined or in one of the child classes.
410420
// A private class method can be used only in the class where it is

0 commit comments

Comments
 (0)