mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.0.1879: Vim9: incorrect duplicate class member detection
Problem: Vim9: incorrect duplicate class member detection Solution: Incorrect duplicate class member detection when variable names have the same prefix. Not able to access class member variables using an object. Fix coding style issues closes: #13042 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
parent
1690ec64ff
commit
1689e847ff
@ -3790,6 +3790,16 @@ def Test_dup_member_variable()
|
|||||||
endclass
|
endclass
|
||||||
END
|
END
|
||||||
v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val')
|
v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val')
|
||||||
|
|
||||||
|
# Two member variables with a common prefix
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
public static svar2: number
|
||||||
|
public static svar: number
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_interface_static_member_access()
|
def Test_interface_static_member_access()
|
||||||
@ -4084,4 +4094,39 @@ def Test_modify_class_member_from_def_function()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for accessing a class member variable using an object
|
||||||
|
def Test_class_member_access_using_object()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
public static svar1: list<number> = [1]
|
||||||
|
public static svar2: list<number> = [2]
|
||||||
|
endclass
|
||||||
|
|
||||||
|
A.svar1->add(3)
|
||||||
|
A.svar2->add(4)
|
||||||
|
assert_equal([1, 3], A.svar1)
|
||||||
|
assert_equal([2, 4], A.svar2)
|
||||||
|
var a1 = A.new()
|
||||||
|
a1.svar1->add(5)
|
||||||
|
a1.svar2->add(6)
|
||||||
|
assert_equal([1, 3, 5], a1.svar1)
|
||||||
|
assert_equal([2, 4, 6], a1.svar2)
|
||||||
|
|
||||||
|
def Foo()
|
||||||
|
A.svar1->add(7)
|
||||||
|
A.svar2->add(8)
|
||||||
|
assert_equal([1, 3, 5, 7], A.svar1)
|
||||||
|
assert_equal([2, 4, 6, 8], A.svar2)
|
||||||
|
var a2 = A.new()
|
||||||
|
a2.svar1->add(9)
|
||||||
|
a2.svar2->add(10)
|
||||||
|
assert_equal([1, 3, 5, 7, 9], a2.svar1)
|
||||||
|
assert_equal([2, 4, 6, 8, 10], a2.svar2)
|
||||||
|
enddef
|
||||||
|
Foo()
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
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
|
||||||
|
@ -699,6 +699,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 */
|
||||||
|
/**/
|
||||||
|
1879,
|
||||||
/**/
|
/**/
|
||||||
1878,
|
1878,
|
||||||
/**/
|
/**/
|
||||||
|
162
src/vim9class.c
162
src/vim9class.c
@ -467,8 +467,8 @@ validate_interface_methods(
|
|||||||
: objmethods_gap->ga_len;
|
: objmethods_gap->ga_len;
|
||||||
for (int if_i = 0; if_i < if_count; ++if_i)
|
for (int if_i = 0; if_i < if_count; ++if_i)
|
||||||
{
|
{
|
||||||
char_u *if_name = if_fp[if_i]->uf_name;
|
char_u *if_name = if_fp[if_i]->uf_name;
|
||||||
int cl_i;
|
int cl_i;
|
||||||
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
||||||
{
|
{
|
||||||
char_u *cl_name = cl_fp[cl_i]->uf_name;
|
char_u *cl_name = cl_fp[cl_i]->uf_name;
|
||||||
@ -584,8 +584,8 @@ check_func_arg_names(
|
|||||||
// Check all the class member names
|
// Check all the class member names
|
||||||
for (int mi = 0; mi < mgap->ga_len; ++mi)
|
for (int mi = 0; mi < mgap->ga_len; ++mi)
|
||||||
{
|
{
|
||||||
char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
|
char_u *mname =
|
||||||
->ocm_name;
|
((ocmember_T *)mgap->ga_data + mi)->ocm_name;
|
||||||
if (STRCMP(aname, mname) == 0)
|
if (STRCMP(aname, mname) == 0)
|
||||||
{
|
{
|
||||||
if (uf->uf_script_ctx.sc_sid > 0)
|
if (uf->uf_script_ctx.sc_sid > 0)
|
||||||
@ -610,22 +610,24 @@ check_func_arg_names(
|
|||||||
static int
|
static int
|
||||||
is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
|
is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
|
||||||
{
|
{
|
||||||
char_u *pstr = (*varname == '_') ? varname + 1 : varname;
|
char_u *name = vim_strnsave(varname, varname_end - varname);
|
||||||
|
char_u *pstr = (*name == '_') ? name + 1 : name;
|
||||||
|
int dup = FALSE;
|
||||||
|
|
||||||
for (int i = 0; i < mgap->ga_len; ++i)
|
for (int i = 0; i < mgap->ga_len; ++i)
|
||||||
{
|
{
|
||||||
ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
|
ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
|
||||||
char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
|
char_u *qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
|
||||||
if (STRNCMP(pstr, qstr, varname_end - pstr) == 0)
|
if (STRCMP(pstr, qstr) == 0)
|
||||||
{
|
{
|
||||||
char_u *name = vim_strnsave(varname, varname_end - varname);
|
|
||||||
semsg(_(e_duplicate_member_str), name);
|
semsg(_(e_duplicate_member_str), name);
|
||||||
vim_free(name);
|
dup = TRUE;
|
||||||
return TRUE;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
vim_free(name);
|
||||||
|
return dup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -638,8 +640,8 @@ is_duplicate_method(garray_T *fgap, char_u *name)
|
|||||||
|
|
||||||
for (int i = 0; i < fgap->ga_len; ++i)
|
for (int i = 0; i < fgap->ga_len; ++i)
|
||||||
{
|
{
|
||||||
char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
|
char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
|
||||||
char_u *qstr = *n == '_' ? n + 1 : n;
|
char_u *qstr = *n == '_' ? n + 1 : n;
|
||||||
if (STRCMP(pstr, qstr) == 0)
|
if (STRCMP(pstr, qstr) == 0)
|
||||||
{
|
{
|
||||||
semsg(_(e_duplicate_function_str), name);
|
semsg(_(e_duplicate_function_str), name);
|
||||||
@ -699,7 +701,7 @@ update_member_method_lookup_table(
|
|||||||
|
|
||||||
// Table for members.
|
// Table for members.
|
||||||
itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
|
itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
|
||||||
+ ifcl->class_obj_member_count * sizeof(int));
|
+ ifcl->class_obj_member_count * sizeof(int));
|
||||||
if (if2cl == NULL)
|
if (if2cl == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if2cl->i2c_next = ifcl->class_itf2class;
|
if2cl->i2c_next = ifcl->class_itf2class;
|
||||||
@ -711,7 +713,7 @@ update_member_method_lookup_table(
|
|||||||
for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
|
for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
|
||||||
{
|
{
|
||||||
if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
|
if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
|
||||||
cl->class_obj_members[cl_i].ocm_name) == 0)
|
cl->class_obj_members[cl_i].ocm_name) == 0)
|
||||||
{
|
{
|
||||||
int *table = (int *)(if2cl + 1);
|
int *table = (int *)(if2cl + 1);
|
||||||
table[if_i] = cl_i;
|
table[if_i] = cl_i;
|
||||||
@ -721,7 +723,7 @@ update_member_method_lookup_table(
|
|||||||
|
|
||||||
// Table for methods.
|
// Table for methods.
|
||||||
if2cl = alloc_clear(sizeof(itf2class_T)
|
if2cl = alloc_clear(sizeof(itf2class_T)
|
||||||
+ ifcl->class_obj_method_count * sizeof(int));
|
+ ifcl->class_obj_method_count * sizeof(int));
|
||||||
if (if2cl == NULL)
|
if (if2cl == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if2cl->i2c_next = ifcl->class_itf2class;
|
if2cl->i2c_next = ifcl->class_itf2class;
|
||||||
@ -735,8 +737,7 @@ update_member_method_lookup_table(
|
|||||||
for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
|
for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
|
||||||
{
|
{
|
||||||
if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
|
if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
|
||||||
((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
|
((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
|
||||||
== 0)
|
|
||||||
{
|
{
|
||||||
int *table = (int *)(if2cl + 1);
|
int *table = (int *)(if2cl + 1);
|
||||||
table[if_i] = cl_i;
|
table[if_i] = cl_i;
|
||||||
@ -754,8 +755,8 @@ update_member_method_lookup_table(
|
|||||||
// the intermediate parent classes.
|
// the intermediate parent classes.
|
||||||
if (cl->class_extends != ifcl)
|
if (cl->class_extends != ifcl)
|
||||||
{
|
{
|
||||||
class_T *parent = cl->class_extends;
|
class_T *parent = cl->class_extends;
|
||||||
int method_offset = objmethods->ga_len;
|
int method_offset = objmethods->ga_len;
|
||||||
|
|
||||||
while (!done && parent != NULL && parent != ifcl)
|
while (!done && parent != NULL && parent != ifcl)
|
||||||
{
|
{
|
||||||
@ -844,8 +845,8 @@ add_class_members(class_T *cl, exarg_T *eap)
|
|||||||
|
|
||||||
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];
|
||||||
typval_T *tv = &cl->class_members_tv[i];
|
typval_T *tv = &cl->class_members_tv[i];
|
||||||
if (m->ocm_init != NULL)
|
if (m->ocm_init != NULL)
|
||||||
{
|
{
|
||||||
typval_T *etv = eval_expr(m->ocm_init, eap);
|
typval_T *etv = eval_expr(m->ocm_init, eap);
|
||||||
@ -905,7 +906,7 @@ add_default_constructor(
|
|||||||
if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
|
if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
|
||||||
{
|
{
|
||||||
((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
|
((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
|
||||||
= nf;
|
= nf;
|
||||||
++classfunctions_gap->ga_len;
|
++classfunctions_gap->ga_len;
|
||||||
|
|
||||||
nf->uf_flags |= FC_NEW;
|
nf->uf_flags |= FC_NEW;
|
||||||
@ -935,10 +936,10 @@ add_classfuncs_objmethods(
|
|||||||
// loop 1: class functions, loop 2: object methods
|
// loop 1: class functions, loop 2: object methods
|
||||||
for (int loop = 1; loop <= 2; ++loop)
|
for (int loop = 1; loop <= 2; ++loop)
|
||||||
{
|
{
|
||||||
garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
|
garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
|
||||||
int *fcount = loop == 1 ? &cl->class_class_function_count
|
int *fcount = loop == 1 ? &cl->class_class_function_count
|
||||||
: &cl->class_obj_method_count;
|
: &cl->class_obj_method_count;
|
||||||
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;
|
||||||
|
|
||||||
int parent_count = 0;
|
int parent_count = 0;
|
||||||
@ -1028,11 +1029,11 @@ add_classfuncs_objmethods(
|
|||||||
void
|
void
|
||||||
ex_class(exarg_T *eap)
|
ex_class(exarg_T *eap)
|
||||||
{
|
{
|
||||||
int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
|
int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
|
||||||
long start_lnum = SOURCING_LNUM;
|
long start_lnum = SOURCING_LNUM;
|
||||||
|
char_u *arg = eap->arg;
|
||||||
|
int is_abstract = eap->cmdidx == CMD_abstract;
|
||||||
|
|
||||||
char_u *arg = eap->arg;
|
|
||||||
int is_abstract = eap->cmdidx == CMD_abstract;
|
|
||||||
if (is_abstract)
|
if (is_abstract)
|
||||||
{
|
{
|
||||||
if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
|
if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
|
||||||
@ -1329,8 +1330,8 @@ early_ret:
|
|||||||
|
|
||||||
if (uf != NULL)
|
if (uf != NULL)
|
||||||
{
|
{
|
||||||
char_u *name = uf->uf_name;
|
char_u *name = uf->uf_name;
|
||||||
int is_new = STRNCMP(name, "new", 3) == 0;
|
int is_new = STRNCMP(name, "new", 3) == 0;
|
||||||
|
|
||||||
if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
|
if (is_new && !is_valid_constructor(uf, is_abstract, has_static))
|
||||||
{
|
{
|
||||||
@ -1630,11 +1631,11 @@ class_member_type(
|
|||||||
ocmember_T **p_m)
|
ocmember_T **p_m)
|
||||||
{
|
{
|
||||||
*member_idx = -1; // not found (yet)
|
*member_idx = -1; // not found (yet)
|
||||||
size_t len = name_end - name;
|
size_t len = name_end - name;
|
||||||
int member_count = is_object ? cl->class_obj_member_count
|
int member_count = is_object ? cl->class_obj_member_count
|
||||||
: cl->class_class_member_count;
|
: cl->class_class_member_count;
|
||||||
ocmember_T *members = is_object ? cl->class_obj_members
|
ocmember_T *members = is_object ? cl->class_obj_members
|
||||||
: cl->class_class_members;
|
: cl->class_class_members;
|
||||||
|
|
||||||
for (int i = 0; i < member_count; ++i)
|
for (int i = 0; i < member_count; ++i)
|
||||||
{
|
{
|
||||||
@ -1670,6 +1671,57 @@ ex_type(exarg_T *eap UNUSED)
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns OK if a member variable named "name" is present in the class "cl".
|
||||||
|
* Otherwise returns FAIL. If found, the member variable typval is set in
|
||||||
|
* "rettv". If "is_object" is TRUE, then the object member variable table is
|
||||||
|
* searched. Otherwise the class member variable table is searched.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_member_tv(
|
||||||
|
class_T *cl,
|
||||||
|
int is_object,
|
||||||
|
char_u *name,
|
||||||
|
size_t namelen,
|
||||||
|
typval_T *rettv)
|
||||||
|
{
|
||||||
|
int member_count = is_object ? cl->class_obj_member_count
|
||||||
|
: cl->class_class_member_count;
|
||||||
|
ocmember_T *members = is_object ? cl->class_obj_members
|
||||||
|
: cl->class_class_members;
|
||||||
|
|
||||||
|
for (int i = 0; i < member_count; ++i)
|
||||||
|
{
|
||||||
|
ocmember_T *m = &members[i];
|
||||||
|
if (STRNCMP(name, m->ocm_name, namelen) == 0
|
||||||
|
&& m->ocm_name[namelen] == NUL)
|
||||||
|
{
|
||||||
|
if (*name == '_')
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object only contains a pointer to the class, the member
|
||||||
|
// values array follows right after that.
|
||||||
|
object_T *obj = rettv->vval.v_object;
|
||||||
|
if (is_object)
|
||||||
|
{
|
||||||
|
typval_T *tv = (typval_T *)(obj + 1) + i;
|
||||||
|
copy_tv(tv, rettv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
copy_tv(&cl->class_members_tv[i], rettv);
|
||||||
|
|
||||||
|
object_unref(obj);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate what comes after a class:
|
* Evaluate what comes after a class:
|
||||||
* - class member: SomeClass.varname
|
* - class member: SomeClass.varname
|
||||||
@ -1736,8 +1788,8 @@ class_object_index(
|
|||||||
char_u *ufname = (char_u *)fp->uf_name;
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
|
if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
|
||||||
{
|
{
|
||||||
typval_T argvars[MAX_FUNC_ARGS + 1];
|
typval_T argvars[MAX_FUNC_ARGS + 1];
|
||||||
int argcount = 0;
|
int argcount = 0;
|
||||||
|
|
||||||
if (*ufname == '_')
|
if (*ufname == '_')
|
||||||
{
|
{
|
||||||
@ -1752,7 +1804,7 @@ class_object_index(
|
|||||||
if (ret == FAIL)
|
if (ret == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
funcexe_T funcexe;
|
funcexe_T funcexe;
|
||||||
CLEAR_FIELD(funcexe);
|
CLEAR_FIELD(funcexe);
|
||||||
funcexe.fe_evaluate = TRUE;
|
funcexe.fe_evaluate = TRUE;
|
||||||
if (rettv->v_type == VAR_OBJECT)
|
if (rettv->v_type == VAR_OBJECT)
|
||||||
@ -1791,27 +1843,13 @@ class_object_index(
|
|||||||
|
|
||||||
else if (rettv->v_type == VAR_OBJECT)
|
else if (rettv->v_type == VAR_OBJECT)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
// Search in the object member variable table and the class member
|
||||||
|
// variable table.
|
||||||
|
if (get_member_tv(cl, TRUE, name, len, rettv) == OK
|
||||||
|
|| get_member_tv(cl, FALSE, name, len, rettv) == OK)
|
||||||
{
|
{
|
||||||
ocmember_T *m = &cl->class_obj_members[i];
|
*arg = name_end;
|
||||||
if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
|
return OK;
|
||||||
{
|
|
||||||
if (*name == '_')
|
|
||||||
{
|
|
||||||
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The object only contains a pointer to the class, the member
|
|
||||||
// values array follows right after that.
|
|
||||||
object_T *obj = rettv->vval.v_object;
|
|
||||||
typval_T *tv = (typval_T *)(obj + 1) + i;
|
|
||||||
copy_tv(tv, rettv);
|
|
||||||
object_unref(obj);
|
|
||||||
|
|
||||||
*arg = name_end;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
||||||
@ -1864,8 +1902,8 @@ find_class_func(char_u **arg)
|
|||||||
if (name_end == name || *name_end != '.')
|
if (name_end == name || *name_end != '.')
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
size_t len = name_end - name;
|
size_t len = name_end - name;
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
tv.v_type = VAR_UNKNOWN;
|
tv.v_type = VAR_UNKNOWN;
|
||||||
if (eval_variable(name, (int)len,
|
if (eval_variable(name, (int)len,
|
||||||
0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
|
0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user