mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.1012: Vim9: class interface inheritance not correctly working
Problem: Vim9: class interface inheritance not correctly working Solution: make the class inherit the interfaces of the super class fixes: #16395 closes: #16412 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
3a0cc36c69
commit
8e92db4ea2
@@ -6096,44 +6096,151 @@ enddef
|
|||||||
|
|
||||||
" Test for using an interface method using a child object when it is overridden
|
" Test for using an interface method using a child object when it is overridden
|
||||||
" by the child class.
|
" by the child class.
|
||||||
" FIXME: This test fails.
|
def Test_interface_overridden_method_from_child()
|
||||||
" def Test_interface_overridden_method_from_child()
|
var lines =<< trim END
|
||||||
" var lines =<< trim END
|
vim9script
|
||||||
" vim9script
|
|
||||||
"
|
interface A
|
||||||
" interface A
|
def Foo(): string
|
||||||
" def Foo(): string
|
endinterface
|
||||||
" endinterface
|
|
||||||
"
|
class B implements A
|
||||||
" class B implements A
|
def Foo(): string
|
||||||
" def Foo(): string
|
return 'b-foo'
|
||||||
" return 'b-foo'
|
enddef
|
||||||
" enddef
|
endclass
|
||||||
" endclass
|
|
||||||
"
|
class C extends B
|
||||||
" class C extends B
|
def Bar(): string
|
||||||
" def Bar(): string
|
return 'bar'
|
||||||
" return 'bar'
|
enddef
|
||||||
" enddef
|
def Foo(): string
|
||||||
" def Foo(): string
|
return 'c-foo'
|
||||||
" return 'c-foo'
|
enddef
|
||||||
" enddef
|
endclass
|
||||||
" endclass
|
|
||||||
"
|
def T1(a: A)
|
||||||
" def T1(a: A)
|
assert_equal('c-foo', a.Foo())
|
||||||
" assert_equal('c-foo', a.Foo())
|
enddef
|
||||||
" enddef
|
|
||||||
"
|
def T2(b: B)
|
||||||
" def T2(b: B)
|
assert_equal('c-foo', b.Foo())
|
||||||
" assert_equal('c-foo', b.Foo())
|
enddef
|
||||||
" enddef
|
|
||||||
"
|
var c = C.new()
|
||||||
" var c = C.new()
|
T1(c)
|
||||||
" T1(c)
|
T2(c)
|
||||||
" T2(c)
|
END
|
||||||
" END
|
v9.CheckSourceSuccess(lines)
|
||||||
" v9.CheckSourceSuccess(lines)
|
enddef
|
||||||
" enddef
|
|
||||||
|
" Test for interface inheritance
|
||||||
|
def Test_interface_inheritance()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
interface A
|
||||||
|
def A_Fn(): string
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface B
|
||||||
|
def B_Fn(): string
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface C
|
||||||
|
def C_Fn(): string
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class C1 implements A
|
||||||
|
def A_Fn(): string
|
||||||
|
return 'c1-a'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C2 extends C1 implements B
|
||||||
|
def B_Fn(): string
|
||||||
|
return 'c2-b'
|
||||||
|
enddef
|
||||||
|
def A_Fn(): string
|
||||||
|
return 'c2-a'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C3 extends C2 implements C
|
||||||
|
def C_Fn(): string
|
||||||
|
return 'c3-c'
|
||||||
|
enddef
|
||||||
|
def A_Fn(): string
|
||||||
|
return 'c3-a'
|
||||||
|
enddef
|
||||||
|
def B_Fn(): string
|
||||||
|
return 'c3-b'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def T1(a: A, s: string)
|
||||||
|
assert_equal(s, a.A_Fn())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def T2(b: B, s: string)
|
||||||
|
assert_equal(s, b.B_Fn())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def T3(c: C, s: string)
|
||||||
|
assert_equal(s, c.C_Fn())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def T4(c1: C1)
|
||||||
|
T1(c1, 'c3-a')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def T5(c2: C2)
|
||||||
|
T1(c2, 'c3-a')
|
||||||
|
T2(c2, 'c3-b')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def T6(c3: C3)
|
||||||
|
T1(c3, 'c3-a')
|
||||||
|
T2(c3, 'c3-b')
|
||||||
|
T3(c3, 'c3-c')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var o3 = C3.new()
|
||||||
|
T4(o3)
|
||||||
|
T5(o3)
|
||||||
|
T6(o3)
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
# Both the parent and child classes implement the same interface
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
interface I
|
||||||
|
def Foo(): string
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class A implements I
|
||||||
|
def Foo(): string
|
||||||
|
return 'A-foo'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B implements I
|
||||||
|
def Foo(): string
|
||||||
|
return 'B-foo'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Bar(i1: I): string
|
||||||
|
return i1.Foo()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var b = B.new()
|
||||||
|
assert_equal('B-foo', Bar(b))
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
" Test for abstract methods
|
" Test for abstract methods
|
||||||
def Test_abstract_method()
|
def Test_abstract_method()
|
||||||
@@ -7282,6 +7389,44 @@ def Test_implement_interface_with_different_variable_order()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for inheriting interfaces from an imported super class
|
||||||
|
def Test_interface_inheritance_with_imported_super()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
export interface I
|
||||||
|
def F(): string
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
export class A implements I
|
||||||
|
def F(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
writefile(lines, 'Xinheritintfimportclass.vim', 'D')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
import './Xinheritintfimportclass.vim' as i_imp
|
||||||
|
|
||||||
|
# class C extends i_imp.A
|
||||||
|
class C extends i_imp.A implements i_imp.I
|
||||||
|
def F(): string
|
||||||
|
return 'C'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def TestI(i: i_imp.I): string
|
||||||
|
return i.F()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
assert_equal('C', TestI(C.new()))
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
" Test for using "any" type for a variable in a sub-class while it has a
|
" Test for using "any" type for a variable in a sub-class while it has a
|
||||||
" concrete type in the interface
|
" concrete type in the interface
|
||||||
def Test_implements_using_var_type_any()
|
def Test_implements_using_var_type_any()
|
||||||
|
@@ -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 */
|
||||||
|
/**/
|
||||||
|
1012,
|
||||||
/**/
|
/**/
|
||||||
1011,
|
1011,
|
||||||
/**/
|
/**/
|
||||||
|
105
src/vim9class.c
105
src/vim9class.c
@@ -782,7 +782,7 @@ validate_interface_methods(
|
|||||||
static int
|
static int
|
||||||
validate_implements_classes(
|
validate_implements_classes(
|
||||||
garray_T *impl_gap,
|
garray_T *impl_gap,
|
||||||
class_T **intf_classes,
|
garray_T *intf_classes_gap,
|
||||||
garray_T *objmethods_gap,
|
garray_T *objmethods_gap,
|
||||||
garray_T *objmembers_gap,
|
garray_T *objmembers_gap,
|
||||||
class_T *extends_cl)
|
class_T *extends_cl)
|
||||||
@@ -812,7 +812,15 @@ validate_implements_classes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class_T *ifcl = tv.vval.v_class;
|
class_T *ifcl = tv.vval.v_class;
|
||||||
intf_classes[i] = ifcl;
|
if (ga_grow(intf_classes_gap, 1) == FAIL)
|
||||||
|
{
|
||||||
|
success = FALSE;
|
||||||
|
clear_tv(&tv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
((class_T **)intf_classes_gap->ga_data)[intf_classes_gap->ga_len]
|
||||||
|
= ifcl;
|
||||||
|
intf_classes_gap->ga_len++;
|
||||||
++ifcl->class_refcount;
|
++ifcl->class_refcount;
|
||||||
|
|
||||||
// check the variables of the interface match the members of the class
|
// check the variables of the interface match the members of the class
|
||||||
@@ -830,6 +838,80 @@ validate_implements_classes(
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns TRUE if the interface class "ifcl" is already present in the
|
||||||
|
* "intf_classes_gap" grow array.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_interface_class_present(garray_T *intf_classes_gap, class_T *ifcl)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < intf_classes_gap->ga_len; j++)
|
||||||
|
{
|
||||||
|
if (((class_T **)intf_classes_gap)[j] == ifcl)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add interface "ifcl" from a super class to "intf_classes_gap" and the class
|
||||||
|
* name to "impl_gap".
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_interface_from_super_class(
|
||||||
|
class_T *ifcl,
|
||||||
|
garray_T *impl_gap,
|
||||||
|
garray_T *intf_classes_gap)
|
||||||
|
{
|
||||||
|
char_u *intf_name;
|
||||||
|
|
||||||
|
// Add the interface name to "impl_gap"
|
||||||
|
intf_name = vim_strsave(ifcl->class_name);
|
||||||
|
if (intf_name == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (ga_grow(impl_gap, 1) == FAIL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
char_u **intf_names = (char_u **)impl_gap->ga_data;
|
||||||
|
intf_names[impl_gap->ga_len] = intf_name;
|
||||||
|
impl_gap->ga_len++;
|
||||||
|
|
||||||
|
// Add the interface class to "intf_classes_gap"
|
||||||
|
if (ga_grow(intf_classes_gap, 1) == FAIL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
class_T **intf_classes = (class_T **)intf_classes_gap->ga_data;
|
||||||
|
intf_classes[intf_classes_gap->ga_len] = ifcl;
|
||||||
|
intf_classes_gap->ga_len++;
|
||||||
|
++ifcl->class_refcount;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add "super" class interfaces to "intf_classes_gap" (if not present already)
|
||||||
|
* Add the interface class names to "impl_gap".
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_super_class_interfaces(
|
||||||
|
class_T *super,
|
||||||
|
garray_T *impl_gap,
|
||||||
|
garray_T *intf_classes_gap)
|
||||||
|
{
|
||||||
|
// Iterate through all the interfaces implemented by "super"
|
||||||
|
for (int i = 0; i < super->class_interface_count; i++)
|
||||||
|
{
|
||||||
|
class_T *ifcl = super->class_interfaces_cl[i];
|
||||||
|
|
||||||
|
if (!is_interface_class_present(intf_classes_gap, ifcl))
|
||||||
|
add_interface_from_super_class(ifcl, impl_gap, intf_classes_gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check no function argument name is used as a class member.
|
* Check no function argument name is used as a class member.
|
||||||
* (Object members are always accessed with "this." prefix, so no need
|
* (Object members are always accessed with "this." prefix, so no need
|
||||||
@@ -2427,14 +2509,23 @@ early_ret:
|
|||||||
success = validate_abstract_class_methods(&classfunctions,
|
success = validate_abstract_class_methods(&classfunctions,
|
||||||
&objmethods, extends_cl);
|
&objmethods, extends_cl);
|
||||||
|
|
||||||
|
// Process the "implements" entries
|
||||||
// Check all "implements" entries are valid.
|
// Check all "implements" entries are valid.
|
||||||
if (success && ga_impl.ga_len > 0)
|
garray_T intf_classes_ga;
|
||||||
{
|
|
||||||
intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
|
|
||||||
|
|
||||||
success = validate_implements_classes(&ga_impl, intf_classes,
|
ga_init2(&intf_classes_ga, sizeof(class_T *), 5);
|
||||||
|
|
||||||
|
if (success && ga_impl.ga_len > 0)
|
||||||
|
success = validate_implements_classes(&ga_impl, &intf_classes_ga,
|
||||||
&objmethods, &objmembers, extends_cl);
|
&objmethods, &objmembers, extends_cl);
|
||||||
}
|
|
||||||
|
// inherit the super class interfaces
|
||||||
|
if (success && extends_cl != NULL)
|
||||||
|
success = add_super_class_interfaces(extends_cl, &ga_impl,
|
||||||
|
&intf_classes_ga);
|
||||||
|
|
||||||
|
intf_classes = intf_classes_ga.ga_data;
|
||||||
|
intf_classes_ga.ga_len = 0;
|
||||||
|
|
||||||
// Check no function argument name is used as a class member.
|
// Check no function argument name is used as a class member.
|
||||||
if (success)
|
if (success)
|
||||||
|
Reference in New Issue
Block a user