forked from aniani/vim
patch 9.0.1159: extends argument for class not implemented yet
Problem: Extends argument for class not implemented yet. Solution: Basic implementation of "extends".
This commit is contained in:
@@ -3426,4 +3426,10 @@ EXTERN char e_duplicate_implements[]
|
|||||||
INIT(= N_("E1350: Duplicate \"implements\""));
|
INIT(= N_("E1350: Duplicate \"implements\""));
|
||||||
EXTERN char e_duplicate_interface_after_implements_str[]
|
EXTERN char e_duplicate_interface_after_implements_str[]
|
||||||
INIT(= N_("E1351: Duplicate interface after \"implements\": %s"));
|
INIT(= N_("E1351: Duplicate interface after \"implements\": %s"));
|
||||||
|
EXTERN char e_duplicate_extends[]
|
||||||
|
INIT(= N_("E1352: Duplicate \"extends\""));
|
||||||
|
EXTERN char e_class_name_not_found_str[]
|
||||||
|
INIT(= N_("E1353: Class name not found: %s"));
|
||||||
|
EXTERN char e_cannot_extend_str[]
|
||||||
|
INIT(= N_("E1354: Cannot extend %s"));
|
||||||
#endif
|
#endif
|
||||||
|
@@ -56,6 +56,7 @@ int has_varargs(ufunc_T *ufunc);
|
|||||||
int function_exists(char_u *name, int no_deref);
|
int function_exists(char_u *name, int no_deref);
|
||||||
char_u *get_expanded_name(char_u *name, int check);
|
char_u *get_expanded_name(char_u *name, int check);
|
||||||
char_u *get_user_func_name(expand_T *xp, int idx);
|
char_u *get_user_func_name(expand_T *xp, int idx);
|
||||||
|
ufunc_T *copy_function(ufunc_T *fp);
|
||||||
void ex_delfunction(exarg_T *eap);
|
void ex_delfunction(exarg_T *eap);
|
||||||
void func_unref(char_u *name);
|
void func_unref(char_u *name);
|
||||||
void func_ptr_unref(ufunc_T *fp);
|
void func_ptr_unref(ufunc_T *fp);
|
||||||
|
@@ -1494,6 +1494,8 @@ struct class_S
|
|||||||
int class_refcount;
|
int class_refcount;
|
||||||
int class_copyID; // used by garbage collection
|
int class_copyID; // used by garbage collection
|
||||||
|
|
||||||
|
class_T *class_extends; // parent class or NULL
|
||||||
|
|
||||||
// interfaces declared for the class
|
// interfaces declared for the class
|
||||||
int class_interface_count;
|
int class_interface_count;
|
||||||
char_u **class_interfaces; // allocated array of names
|
char_u **class_interfaces; // allocated array of names
|
||||||
|
@@ -753,5 +753,71 @@ def Test_class_used_as_type()
|
|||||||
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got string')
|
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got string')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_class_extends()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Base
|
||||||
|
this.one = 1
|
||||||
|
def GetOne(): number
|
||||||
|
return this.one
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
class Child extends Base
|
||||||
|
this.two = 2
|
||||||
|
def GetTotal(): number
|
||||||
|
return this.one + this.two
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var o = Child.new()
|
||||||
|
assert_equal(1, o.one)
|
||||||
|
assert_equal(2, o.two)
|
||||||
|
assert_equal(1, o.GetOne())
|
||||||
|
assert_equal(3, o.GetTotal())
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Base
|
||||||
|
this.one = 1
|
||||||
|
endclass
|
||||||
|
class Child extends Base
|
||||||
|
this.two = 2
|
||||||
|
endclass
|
||||||
|
var o = Child.new(3, 44)
|
||||||
|
assert_equal(3, o.one)
|
||||||
|
assert_equal(44, o.two)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Base
|
||||||
|
this.one = 1
|
||||||
|
endclass
|
||||||
|
class Child extends Base extends Base
|
||||||
|
this.two = 2
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1352: Duplicate "extends"')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Child extends BaseClass
|
||||||
|
this.two = 2
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1353: Class name not found: BaseClass')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
var SomeVar = 99
|
||||||
|
class Child extends SomeVar
|
||||||
|
this.two = 2
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1354: Cannot extend SomeVar')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -5515,6 +5515,74 @@ get_user_func_name(expand_T *xp, int idx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a copy of a function.
|
||||||
|
* Intended to be used for a function defined on a base class that has a copy
|
||||||
|
* on the child class.
|
||||||
|
* The copy has uf_refcount set to one.
|
||||||
|
* Returns NULL when out of memory.
|
||||||
|
*/
|
||||||
|
ufunc_T *
|
||||||
|
copy_function(ufunc_T *fp)
|
||||||
|
{
|
||||||
|
// The struct may have padding, make sure we allocate at least the size of
|
||||||
|
// the struct.
|
||||||
|
size_t len = offsetof(ufunc_T, uf_name) + STRLEN(fp->uf_name) + 1;
|
||||||
|
ufunc_T *ufunc = alloc_clear(len < sizeof(ufunc_T) ? sizeof(ufunc_T) : len);
|
||||||
|
if (ufunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Most things can just be copied.
|
||||||
|
*ufunc = *fp;
|
||||||
|
|
||||||
|
ufunc->uf_def_status = UF_TO_BE_COMPILED;
|
||||||
|
ufunc->uf_dfunc_idx = 0;
|
||||||
|
ufunc->uf_class = NULL;
|
||||||
|
|
||||||
|
ga_copy_strings(&fp->uf_args, &ufunc->uf_args);
|
||||||
|
ga_copy_strings(&fp->uf_def_args, &ufunc->uf_def_args);
|
||||||
|
|
||||||
|
if (ufunc->uf_arg_types != NULL)
|
||||||
|
{
|
||||||
|
// "uf_arg_types" is an allocated array, make a copy.
|
||||||
|
type_T **at = ALLOC_CLEAR_MULT(type_T *, ufunc->uf_args.ga_len);
|
||||||
|
if (at != NULL)
|
||||||
|
{
|
||||||
|
mch_memmove(at, ufunc->uf_arg_types,
|
||||||
|
sizeof(type_T *) * ufunc->uf_args.ga_len);
|
||||||
|
ufunc->uf_arg_types = at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: how about the types themselves? they can be freed when the
|
||||||
|
// original function is freed:
|
||||||
|
// type_T **uf_arg_types;
|
||||||
|
// type_T *uf_ret_type;
|
||||||
|
|
||||||
|
ufunc->uf_type_list.ga_len = 0;
|
||||||
|
ufunc->uf_type_list.ga_data = NULL;
|
||||||
|
|
||||||
|
// TODO: partial_T *uf_partial;
|
||||||
|
|
||||||
|
if (ufunc->uf_va_name != NULL)
|
||||||
|
ufunc->uf_va_name = vim_strsave(ufunc->uf_va_name);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// type_T *uf_va_type;
|
||||||
|
// type_T *uf_func_type;
|
||||||
|
|
||||||
|
ufunc->uf_block_depth = 0;
|
||||||
|
ufunc->uf_block_ids = NULL;
|
||||||
|
|
||||||
|
ga_copy_strings(&fp->uf_lines, &ufunc->uf_lines);
|
||||||
|
|
||||||
|
ufunc->uf_refcount = 1;
|
||||||
|
ufunc->uf_name_exp = NULL;
|
||||||
|
STRCPY(ufunc->uf_name, fp->uf_name);
|
||||||
|
|
||||||
|
return ufunc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ":delfunction {name}"
|
* ":delfunction {name}"
|
||||||
*/
|
*/
|
||||||
|
@@ -695,6 +695,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 */
|
||||||
|
/**/
|
||||||
|
1159,
|
||||||
/**/
|
/**/
|
||||||
1158,
|
1158,
|
||||||
/**/
|
/**/
|
||||||
|
155
src/vim9class.c
155
src/vim9class.c
@@ -160,6 +160,8 @@ add_member(
|
|||||||
/*
|
/*
|
||||||
* Move the class or object members found while parsing a class into the class.
|
* Move the class or object members found while parsing a class into the class.
|
||||||
* "gap" contains the found members.
|
* "gap" contains the found members.
|
||||||
|
* "parent_members" points to the members in the parent class (if any)
|
||||||
|
* "parent_count" is the number of members in the parent class
|
||||||
* "members" will be set to the newly allocated array of members and
|
* "members" will be set to the newly allocated array of members and
|
||||||
* "member_count" set to the number of members.
|
* "member_count" set to the number of members.
|
||||||
* Returns OK or FAIL.
|
* Returns OK or FAIL.
|
||||||
@@ -167,15 +169,28 @@ add_member(
|
|||||||
static int
|
static int
|
||||||
add_members_to_class(
|
add_members_to_class(
|
||||||
garray_T *gap,
|
garray_T *gap,
|
||||||
|
ocmember_T *parent_members,
|
||||||
|
int parent_count,
|
||||||
ocmember_T **members,
|
ocmember_T **members,
|
||||||
int *member_count)
|
int *member_count)
|
||||||
{
|
{
|
||||||
*member_count = gap->ga_len;
|
*member_count = parent_count + gap->ga_len;
|
||||||
*members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len);
|
*members = *member_count == 0 ? NULL
|
||||||
if (gap->ga_len > 0 && *members == NULL)
|
: ALLOC_MULT(ocmember_T, *member_count);
|
||||||
|
if (*member_count > 0 && *members == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
for (int i = 0; i < parent_count; ++i)
|
||||||
|
{
|
||||||
|
// parent members need to be copied
|
||||||
|
*members[i] = parent_members[i];
|
||||||
|
members[i]->ocm_name = vim_strsave(members[i]->ocm_name);
|
||||||
|
if (members[i]->ocm_init != NULL)
|
||||||
|
members[i]->ocm_init = vim_strsave(members[i]->ocm_init);
|
||||||
|
}
|
||||||
if (gap->ga_len > 0)
|
if (gap->ga_len > 0)
|
||||||
mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
|
// new members are moved
|
||||||
|
mch_memmove(*members + parent_count,
|
||||||
|
gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
|
||||||
VIM_CLEAR(gap->ga_data);
|
VIM_CLEAR(gap->ga_data);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -233,6 +248,9 @@ ex_class(exarg_T *eap)
|
|||||||
// generics: <Tkey, Tentry>
|
// generics: <Tkey, Tentry>
|
||||||
// handle "is_export" if it is set
|
// handle "is_export" if it is set
|
||||||
|
|
||||||
|
// Name for "extends BaseClass"
|
||||||
|
char_u *extends = NULL;
|
||||||
|
|
||||||
// Names for "implements SomeInterface"
|
// Names for "implements SomeInterface"
|
||||||
garray_T ga_impl;
|
garray_T ga_impl;
|
||||||
ga_init2(&ga_impl, sizeof(char_u *), 5);
|
ga_init2(&ga_impl, sizeof(char_u *), 5);
|
||||||
@@ -241,9 +259,29 @@ ex_class(exarg_T *eap)
|
|||||||
while (*arg != NUL && *arg != '#' && *arg != '\n')
|
while (*arg != NUL && *arg != '#' && *arg != '\n')
|
||||||
{
|
{
|
||||||
// TODO:
|
// TODO:
|
||||||
// extends SomeClass
|
|
||||||
// specifies SomeInterface
|
// specifies SomeInterface
|
||||||
if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10]))
|
if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
|
||||||
|
{
|
||||||
|
if (extends != NULL)
|
||||||
|
{
|
||||||
|
emsg(_(e_duplicate_extends));
|
||||||
|
goto early_ret;
|
||||||
|
}
|
||||||
|
arg = skipwhite(arg + 7);
|
||||||
|
char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
|
||||||
|
if (!IS_WHITE_OR_NUL(*end))
|
||||||
|
{
|
||||||
|
semsg(_(e_white_space_required_after_name_str), arg);
|
||||||
|
goto early_ret;
|
||||||
|
}
|
||||||
|
extends = vim_strnsave(arg, end - arg);
|
||||||
|
if (extends == NULL)
|
||||||
|
goto early_ret;
|
||||||
|
|
||||||
|
arg = skipwhite(end + 1);
|
||||||
|
}
|
||||||
|
else if (STRNCMP(arg, "implements", 10) == 0
|
||||||
|
&& IS_WHITE_OR_NUL(arg[10]))
|
||||||
{
|
{
|
||||||
if (ga_impl.ga_len > 0)
|
if (ga_impl.ga_len > 0)
|
||||||
{
|
{
|
||||||
@@ -289,6 +327,7 @@ ex_class(exarg_T *eap)
|
|||||||
{
|
{
|
||||||
semsg(_(e_trailing_characters_str), arg);
|
semsg(_(e_trailing_characters_str), arg);
|
||||||
early_ret:
|
early_ret:
|
||||||
|
vim_free(extends);
|
||||||
ga_clear_strings(&ga_impl);
|
ga_clear_strings(&ga_impl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -496,17 +535,50 @@ early_ret:
|
|||||||
}
|
}
|
||||||
vim_free(theline);
|
vim_free(theline);
|
||||||
|
|
||||||
// Check a few things before defining the class.
|
class_T *extends_cl = NULL; // class from "extends" argument
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check a few things before defining the class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check the "extends" class is valid.
|
||||||
|
if (success && extends != NULL)
|
||||||
|
{
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
|
if (eval_variable(extends, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
|
||||||
|
{
|
||||||
|
semsg(_(e_class_name_not_found_str), extends);
|
||||||
|
success = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tv.v_type != VAR_CLASS
|
||||||
|
|| tv.vval.v_class == NULL
|
||||||
|
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_extend_str), extends);
|
||||||
|
success = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
extends_cl = tv.vval.v_class;
|
||||||
|
++extends_cl->class_refcount;
|
||||||
|
}
|
||||||
|
clear_tv(&tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VIM_CLEAR(extends);
|
||||||
|
|
||||||
|
// Check all "implements" entries are valid.
|
||||||
if (success && ga_impl.ga_len > 0)
|
if (success && ga_impl.ga_len > 0)
|
||||||
{
|
{
|
||||||
// Check all "implements" entries are valid and correct.
|
|
||||||
for (int i = 0; i < ga_impl.ga_len && success; ++i)
|
for (int i = 0; i < ga_impl.ga_len && success; ++i)
|
||||||
{
|
{
|
||||||
char_u *impl = ((char_u **)ga_impl.ga_data)[i];
|
char_u *impl = ((char_u **)ga_impl.ga_data)[i];
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
tv.v_type = VAR_UNKNOWN;
|
tv.v_type = VAR_UNKNOWN;
|
||||||
if (eval_variable(impl, 0, 0, &tv, NULL,
|
if (eval_variable(impl, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
|
||||||
EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL)
|
|
||||||
{
|
{
|
||||||
semsg(_(e_interface_name_not_found_str), impl);
|
semsg(_(e_interface_name_not_found_str), impl);
|
||||||
success = FALSE;
|
success = FALSE;
|
||||||
@@ -620,6 +692,8 @@ early_ret:
|
|||||||
if (cl->class_name == NULL)
|
if (cl->class_name == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
cl->class_extends = extends_cl;
|
||||||
|
|
||||||
if (ga_impl.ga_len > 0)
|
if (ga_impl.ga_len > 0)
|
||||||
{
|
{
|
||||||
// Move the "implements" names into the class.
|
// Move the "implements" names into the class.
|
||||||
@@ -635,11 +709,19 @@ early_ret:
|
|||||||
|
|
||||||
// Add class and object members to "cl".
|
// Add class and object members to "cl".
|
||||||
if (add_members_to_class(&classmembers,
|
if (add_members_to_class(&classmembers,
|
||||||
&cl->class_class_members,
|
extends_cl == NULL ? NULL
|
||||||
&cl->class_class_member_count) == FAIL
|
: extends_cl->class_class_members,
|
||||||
|
extends_cl == NULL ? 0
|
||||||
|
: extends_cl->class_class_member_count,
|
||||||
|
&cl->class_class_members,
|
||||||
|
&cl->class_class_member_count) == FAIL
|
||||||
|| add_members_to_class(&objmembers,
|
|| add_members_to_class(&objmembers,
|
||||||
&cl->class_obj_members,
|
extends_cl == NULL ? NULL
|
||||||
&cl->class_obj_member_count) == FAIL)
|
: extends_cl->class_obj_members,
|
||||||
|
extends_cl == NULL ? 0
|
||||||
|
: extends_cl->class_obj_member_count,
|
||||||
|
&cl->class_obj_members,
|
||||||
|
&cl->class_obj_member_count) == FAIL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (is_class && cl->class_class_member_count > 0)
|
if (is_class && cl->class_class_member_count > 0)
|
||||||
@@ -735,20 +817,47 @@ early_ret:
|
|||||||
ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
|
ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
|
||||||
: &cl->class_obj_methods;
|
: &cl->class_obj_methods;
|
||||||
|
|
||||||
*fcount = gap->ga_len;
|
int parent_count = 0;
|
||||||
if (gap->ga_len == 0)
|
if (extends_cl != NULL)
|
||||||
|
// Include functions from the parent.
|
||||||
|
parent_count = loop == 1
|
||||||
|
? extends_cl->class_class_function_count
|
||||||
|
: extends_cl->class_obj_method_count;
|
||||||
|
|
||||||
|
*fcount = parent_count + gap->ga_len;
|
||||||
|
if (*fcount == 0)
|
||||||
{
|
{
|
||||||
*fup = NULL;
|
*fup = NULL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
*fup = ALLOC_MULT(ufunc_T *, gap->ga_len);
|
*fup = ALLOC_MULT(ufunc_T *, *fcount);
|
||||||
if (*fup == NULL)
|
if (*fup == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
|
|
||||||
vim_free(gap->ga_data);
|
|
||||||
|
|
||||||
// Set the class pointer on all the object methods.
|
int skipped = 0;
|
||||||
for (int i = 0; i < gap->ga_len; ++i)
|
for (int i = 0; i < parent_count; ++i)
|
||||||
|
{
|
||||||
|
// Copy functions from the parent. Can't use the same
|
||||||
|
// function, because "uf_class" is different and compilation
|
||||||
|
// will have a different result.
|
||||||
|
// Skip "new" functions. TODO: not all of them.
|
||||||
|
if (loop == 1 && STRNCMP(
|
||||||
|
extends_cl->class_class_functions[i]->uf_name,
|
||||||
|
"new", 3) == 0)
|
||||||
|
++skipped;
|
||||||
|
else
|
||||||
|
*fup[i - skipped] = copy_function((loop == 1
|
||||||
|
? extends_cl->class_class_functions
|
||||||
|
: extends_cl->class_obj_methods)[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mch_memmove(*fup + parent_count - skipped, gap->ga_data,
|
||||||
|
sizeof(ufunc_T *) * gap->ga_len);
|
||||||
|
vim_free(gap->ga_data);
|
||||||
|
*fcount -= skipped;
|
||||||
|
|
||||||
|
// Set the class pointer on all the functions and object methods.
|
||||||
|
for (int i = 0; i < *fcount; ++i)
|
||||||
{
|
{
|
||||||
ufunc_T *fp = (*fup)[i];
|
ufunc_T *fp = (*fup)[i];
|
||||||
fp->uf_class = cl;
|
fp->uf_class = cl;
|
||||||
@@ -786,6 +895,8 @@ cleanup:
|
|||||||
vim_free(cl);
|
vim_free(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vim_free(extends);
|
||||||
|
class_unref(extends_cl);
|
||||||
ga_clear_strings(&ga_impl);
|
ga_clear_strings(&ga_impl);
|
||||||
|
|
||||||
for (int round = 1; round <= 2; ++round)
|
for (int round = 1; round <= 2; ++round)
|
||||||
@@ -1167,6 +1278,8 @@ class_unref(class_T *cl)
|
|||||||
// be freed.
|
// be freed.
|
||||||
VIM_CLEAR(cl->class_name);
|
VIM_CLEAR(cl->class_name);
|
||||||
|
|
||||||
|
class_unref(cl->class_extends);
|
||||||
|
|
||||||
for (int i = 0; i < cl->class_interface_count; ++i)
|
for (int i = 0; i < cl->class_interface_count; ++i)
|
||||||
vim_free(((char_u **)cl->class_interfaces)[i]);
|
vim_free(((char_u **)cl->class_interfaces)[i]);
|
||||||
vim_free(cl->class_interfaces);
|
vim_free(cl->class_interfaces);
|
||||||
|
Reference in New Issue
Block a user