mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.1152: class "implements" argument not implemented
Problem: Class "implements" argument not implemented. Solution: Implement "implements" argument. Add basic checks for when a class implements an interface.
This commit is contained in:
@@ -813,7 +813,7 @@ ga_copy_string(garray_T *gap, char_u *p)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Add string "p" to "gap".
|
* Add string "p" to "gap".
|
||||||
* When out of memory "p" is freed and FAIL is returned.
|
* When out of memory FAIL is returned (caller may want to free "p").
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ga_add_string(garray_T *gap, char_u *p)
|
ga_add_string(garray_T *gap, char_u *p)
|
||||||
|
@@ -3414,4 +3414,12 @@ EXTERN char e_cannot_initialize_member_in_interface[]
|
|||||||
INIT(= N_("E1344: Cannot initialize a member in an interface"));
|
INIT(= N_("E1344: Cannot initialize a member in an interface"));
|
||||||
EXTERN char e_not_valid_command_in_interface_str[]
|
EXTERN char e_not_valid_command_in_interface_str[]
|
||||||
INIT(= N_("E1345: Not a valid command in an interface: %s"));
|
INIT(= N_("E1345: Not a valid command in an interface: %s"));
|
||||||
|
EXTERN char e_interface_name_not_found_str[]
|
||||||
|
INIT(= N_("E1346: Interface name not found: %s"));
|
||||||
|
EXTERN char e_not_valid_interface_str[]
|
||||||
|
INIT(= N_("E1347: Not a valid interface: %s"));
|
||||||
|
EXTERN char e_member_str_of_interface_str_not_implemented[]
|
||||||
|
INIT(= N_("E1348: Member \"%s\" of interface \"%s\" not implemented"));
|
||||||
|
EXTERN char e_function_str_of_interface_str_not_implemented[]
|
||||||
|
INIT(= N_("E1349: Function \"%s\" of interface \"%s\" not implemented"));
|
||||||
#endif
|
#endif
|
||||||
|
@@ -5676,7 +5676,8 @@ set_ref_in_item(
|
|||||||
case VAR_CLASS:
|
case VAR_CLASS:
|
||||||
{
|
{
|
||||||
class_T *cl = tv->vval.v_class;
|
class_T *cl = tv->vval.v_class;
|
||||||
if (cl != NULL && cl->class_copyID != copyID)
|
if (cl != NULL && cl->class_copyID != copyID
|
||||||
|
&& (cl->class_flags && CLASS_INTERFACE) == 0)
|
||||||
{
|
{
|
||||||
cl->class_copyID = copyID;
|
cl->class_copyID = copyID;
|
||||||
for (int i = 0; !abort
|
for (int i = 0; !abort
|
||||||
|
@@ -2913,7 +2913,7 @@ set_cmdarg(exarg_T *eap, char_u *oldarg)
|
|||||||
int
|
int
|
||||||
eval_variable(
|
eval_variable(
|
||||||
char_u *name,
|
char_u *name,
|
||||||
int len, // length of "name"
|
int len, // length of "name" or zero
|
||||||
scid_T sid, // script ID for imported item or zero
|
scid_T sid, // script ID for imported item or zero
|
||||||
typval_T *rettv, // NULL when only checking existence
|
typval_T *rettv, // NULL when only checking existence
|
||||||
dictitem_T **dip, // non-NULL when typval's dict item is needed
|
dictitem_T **dip, // non-NULL when typval's dict item is needed
|
||||||
@@ -2923,12 +2923,15 @@ eval_variable(
|
|||||||
typval_T *tv = NULL;
|
typval_T *tv = NULL;
|
||||||
int found = FALSE;
|
int found = FALSE;
|
||||||
hashtab_T *ht = NULL;
|
hashtab_T *ht = NULL;
|
||||||
int cc;
|
int cc = 0;
|
||||||
type_T *type = NULL;
|
type_T *type = NULL;
|
||||||
|
|
||||||
// truncate the name, so that we can use strcmp()
|
if (len > 0)
|
||||||
cc = name[len];
|
{
|
||||||
name[len] = NUL;
|
// truncate the name, so that we can use strcmp()
|
||||||
|
cc = name[len];
|
||||||
|
name[len] = NUL;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for local variable when debugging.
|
// Check for local variable when debugging.
|
||||||
if ((tv = lookup_debug_var(name)) == NULL)
|
if ((tv = lookup_debug_var(name)) == NULL)
|
||||||
@@ -3095,7 +3098,8 @@ eval_variable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name[len] = cc;
|
if (len > 0)
|
||||||
|
name[len] = cc;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -1494,6 +1494,10 @@ struct class_S
|
|||||||
int class_refcount;
|
int class_refcount;
|
||||||
int class_copyID; // used by garbage collection
|
int class_copyID; // used by garbage collection
|
||||||
|
|
||||||
|
// interfaces declared for the class
|
||||||
|
int class_interface_count;
|
||||||
|
char_u **class_interfaces; // allocated array of names
|
||||||
|
|
||||||
// class members: "static varname"
|
// class members: "static varname"
|
||||||
int class_class_member_count;
|
int class_class_member_count;
|
||||||
ocmember_T *class_class_members; // allocated
|
ocmember_T *class_class_members; // allocated
|
||||||
|
@@ -612,5 +612,58 @@ def Test_interface_basics()
|
|||||||
v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5')
|
v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_class_implements_interface()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
interface Some
|
||||||
|
static count: number
|
||||||
|
def Method(nr: number)
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class SomeImpl implements Some
|
||||||
|
static count: number
|
||||||
|
def Method(nr: number)
|
||||||
|
echo nr
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
interface Some
|
||||||
|
static counter: number
|
||||||
|
def Method(nr: number)
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class SomeImpl implements Some
|
||||||
|
static count: number
|
||||||
|
def Method(nr: number)
|
||||||
|
echo nr
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
interface Some
|
||||||
|
static count: number
|
||||||
|
def Methods(nr: number)
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class SomeImpl implements Some
|
||||||
|
static count: number
|
||||||
|
def Method(nr: number)
|
||||||
|
echo nr
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented')
|
||||||
|
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
|
||||||
|
@@ -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 */
|
||||||
|
/**/
|
||||||
|
1152,
|
||||||
/**/
|
/**/
|
||||||
1151,
|
1151,
|
||||||
/**/
|
/**/
|
||||||
|
175
src/vim9class.c
175
src/vim9class.c
@@ -227,15 +227,50 @@ ex_class(exarg_T *eap)
|
|||||||
semsg(_(e_white_space_required_after_name_str), arg);
|
semsg(_(e_white_space_required_after_name_str), arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
char_u *name_start = arg;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// generics: <Tkey, Tentry>
|
// generics: <Tkey, Tentry>
|
||||||
// extends SomeClass
|
|
||||||
// implements SomeInterface
|
|
||||||
// specifies SomeInterface
|
|
||||||
// check that nothing follows
|
|
||||||
// handle "is_export" if it is set
|
// handle "is_export" if it is set
|
||||||
|
|
||||||
|
// Names for "implements SomeInterface"
|
||||||
|
garray_T ga_impl;
|
||||||
|
ga_init2(&ga_impl, sizeof(char_u *), 5);
|
||||||
|
|
||||||
|
arg = skipwhite(name_end);
|
||||||
|
while (*arg != NUL && *arg != '#' && *arg != '\n')
|
||||||
|
{
|
||||||
|
// TODO:
|
||||||
|
// extends SomeClass
|
||||||
|
// specifies SomeInterface
|
||||||
|
if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10]))
|
||||||
|
{
|
||||||
|
arg = skipwhite(arg + 10);
|
||||||
|
char_u *impl_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
|
||||||
|
if (!IS_WHITE_OR_NUL(*impl_end))
|
||||||
|
{
|
||||||
|
semsg(_(e_white_space_required_after_name_str), arg);
|
||||||
|
goto early_ret;
|
||||||
|
}
|
||||||
|
char_u *iname = vim_strnsave(arg, impl_end - arg);
|
||||||
|
if (iname == NULL)
|
||||||
|
goto early_ret;
|
||||||
|
if (ga_add_string(&ga_impl, iname) == FAIL)
|
||||||
|
{
|
||||||
|
vim_free(iname);
|
||||||
|
goto early_ret;
|
||||||
|
}
|
||||||
|
arg = skipwhite(impl_end);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
semsg(_(e_trailing_characters_str), arg);
|
||||||
|
early_ret:
|
||||||
|
ga_clear_strings(&ga_impl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
garray_T type_list; // list of pointers to allocated types
|
garray_T type_list; // list of pointers to allocated types
|
||||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||||
|
|
||||||
@@ -438,6 +473,114 @@ ex_class(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
vim_free(theline);
|
vim_free(theline);
|
||||||
|
|
||||||
|
// Check a few things before defining the class.
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
char_u *impl = ((char_u **)ga_impl.ga_data)[i];
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
|
if (eval_variable(impl, 0, 0, &tv, NULL,
|
||||||
|
EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL)
|
||||||
|
{
|
||||||
|
semsg(_(e_interface_name_not_found_str), impl);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tv.v_type != VAR_CLASS
|
||||||
|
|| tv.vval.v_class == NULL
|
||||||
|
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
|
||||||
|
{
|
||||||
|
semsg(_(e_not_valid_interface_str), impl);
|
||||||
|
success = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the members of the interface match the members of the class
|
||||||
|
class_T *ifcl = tv.vval.v_class;
|
||||||
|
for (int loop = 1; loop <= 2 && success; ++loop)
|
||||||
|
{
|
||||||
|
// loop == 1: check class members
|
||||||
|
// loop == 2: check object members
|
||||||
|
int if_count = loop == 1 ? ifcl->class_class_member_count
|
||||||
|
: ifcl->class_obj_member_count;
|
||||||
|
if (if_count == 0)
|
||||||
|
continue;
|
||||||
|
ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
|
||||||
|
: ifcl->class_obj_members;
|
||||||
|
ocmember_T *cl_ms = (ocmember_T *)(loop == 1
|
||||||
|
? classmembers.ga_data
|
||||||
|
: objmembers.ga_data);
|
||||||
|
int cl_count = loop == 1 ? classmembers.ga_len
|
||||||
|
: objmembers.ga_len;
|
||||||
|
for (int if_i = 0; if_i < if_count; ++if_i)
|
||||||
|
{
|
||||||
|
int cl_i;
|
||||||
|
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
||||||
|
{
|
||||||
|
ocmember_T *m = &cl_ms[cl_i];
|
||||||
|
if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0)
|
||||||
|
{
|
||||||
|
// TODO: check type
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl_i == cl_count)
|
||||||
|
{
|
||||||
|
semsg(_(e_member_str_of_interface_str_not_implemented),
|
||||||
|
if_ms[if_i].ocm_name, impl);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the functions/methods of the interface match the
|
||||||
|
// functions/methods of the class
|
||||||
|
for (int loop = 1; loop <= 2 && success; ++loop)
|
||||||
|
{
|
||||||
|
// loop == 1: check class functions
|
||||||
|
// loop == 2: check object methods
|
||||||
|
int if_count = loop == 1 ? ifcl->class_class_function_count
|
||||||
|
: ifcl->class_obj_method_count;
|
||||||
|
if (if_count == 0)
|
||||||
|
continue;
|
||||||
|
ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
|
||||||
|
: ifcl->class_obj_methods;
|
||||||
|
ufunc_T **cl_fp = (ufunc_T **)(loop == 1
|
||||||
|
? classfunctions.ga_data
|
||||||
|
: objmethods.ga_data);
|
||||||
|
int cl_count = loop == 1 ? classfunctions.ga_len
|
||||||
|
: objmethods.ga_len;
|
||||||
|
for (int if_i = 0; if_i < if_count; ++if_i)
|
||||||
|
{
|
||||||
|
char_u *if_name = if_fp[if_i]->uf_name;
|
||||||
|
int cl_i;
|
||||||
|
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
||||||
|
{
|
||||||
|
char_u *cl_name = cl_fp[cl_i]->uf_name;
|
||||||
|
if (STRCMP(if_name, cl_name) == 0)
|
||||||
|
{
|
||||||
|
// TODO: check return and argument types
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl_i == cl_count)
|
||||||
|
{
|
||||||
|
semsg(_(e_function_str_of_interface_str_not_implemented),
|
||||||
|
if_name, impl);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_tv(&tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class_T *cl = NULL;
|
class_T *cl = NULL;
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
@@ -450,10 +593,23 @@ ex_class(exarg_T *eap)
|
|||||||
cl->class_flags = CLASS_INTERFACE;
|
cl->class_flags = CLASS_INTERFACE;
|
||||||
|
|
||||||
cl->class_refcount = 1;
|
cl->class_refcount = 1;
|
||||||
cl->class_name = vim_strnsave(arg, name_end - arg);
|
cl->class_name = vim_strnsave(name_start, name_end - name_start);
|
||||||
if (cl->class_name == NULL)
|
if (cl->class_name == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
if (ga_impl.ga_len > 0)
|
||||||
|
{
|
||||||
|
// Move the "implements" names into the class.
|
||||||
|
cl->class_interface_count = ga_impl.ga_len;
|
||||||
|
cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
|
||||||
|
if (cl->class_interfaces == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
for (int i = 0; i < ga_impl.ga_len; ++i)
|
||||||
|
cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
|
||||||
|
CLEAR_POINTER(ga_impl.ga_data);
|
||||||
|
ga_impl.ga_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 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,
|
&cl->class_class_members,
|
||||||
@@ -499,7 +655,7 @@ ex_class(exarg_T *eap)
|
|||||||
have_new = TRUE;
|
have_new = TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!have_new)
|
if (is_class && !have_new)
|
||||||
{
|
{
|
||||||
// No new() method was defined, add the default constructor.
|
// No new() method was defined, add the default constructor.
|
||||||
garray_T fga;
|
garray_T fga;
|
||||||
@@ -589,6 +745,7 @@ ex_class(exarg_T *eap)
|
|||||||
// - Fill hashtab with object members and methods ?
|
// - Fill hashtab with object members and methods ?
|
||||||
|
|
||||||
// Add the class to the script-local variables.
|
// Add the class to the script-local variables.
|
||||||
|
// TODO: handle other context, e.g. in a function
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
tv.v_type = VAR_CLASS;
|
tv.v_type = VAR_CLASS;
|
||||||
tv.vval.v_class = cl;
|
tv.vval.v_class = cl;
|
||||||
@@ -607,6 +764,8 @@ cleanup:
|
|||||||
vim_free(cl);
|
vim_free(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ga_clear_strings(&ga_impl);
|
||||||
|
|
||||||
for (int round = 1; round <= 2; ++round)
|
for (int round = 1; round <= 2; ++round)
|
||||||
{
|
{
|
||||||
garray_T *gap = round == 1 ? &classmembers : &objmembers;
|
garray_T *gap = round == 1 ? &classmembers : &objmembers;
|
||||||
@@ -986,6 +1145,10 @@ class_unref(class_T *cl)
|
|||||||
// be freed.
|
// be freed.
|
||||||
VIM_CLEAR(cl->class_name);
|
VIM_CLEAR(cl->class_name);
|
||||||
|
|
||||||
|
for (int i = 0; i < cl->class_interface_count; ++i)
|
||||||
|
vim_free(((char_u **)cl->class_interfaces)[i]);
|
||||||
|
vim_free(cl->class_interfaces);
|
||||||
|
|
||||||
for (int i = 0; i < cl->class_class_member_count; ++i)
|
for (int i = 0; i < cl->class_class_member_count; ++i)
|
||||||
{
|
{
|
||||||
ocmember_T *m = &cl->class_class_members[i];
|
ocmember_T *m = &cl->class_class_members[i];
|
||||||
|
Reference in New Issue
Block a user