0
0
mirror of https://github.com/vim/vim.git synced 2025-07-04 23:07: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:
Yegappan Lakshmanan 2023-09-06 20:23:23 +02:00 committed by Christian Brabandt
parent 1690ec64ff
commit 1689e847ff
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
3 changed files with 147 additions and 62 deletions

View File

@ -3790,6 +3790,16 @@ def Test_dup_member_variable()
endclass
END
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
def Test_interface_static_member_access()
@ -4084,4 +4094,39 @@ def Test_modify_class_member_from_def_function()
v9.CheckScriptSuccess(lines)
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

View File

@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1879,
/**/
1878,
/**/

View File

@ -584,8 +584,8 @@ check_func_arg_names(
// Check all the class member names
for (int mi = 0; mi < mgap->ga_len; ++mi)
{
char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
->ocm_name;
char_u *mname =
((ocmember_T *)mgap->ga_data + mi)->ocm_name;
if (STRCMP(aname, mname) == 0)
{
if (uf->uf_script_ctx.sc_sid > 0)
@ -610,22 +610,24 @@ check_func_arg_names(
static int
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)
{
ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
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);
vim_free(name);
return TRUE;
dup = TRUE;
break;
}
}
return FALSE;
vim_free(name);
return dup;
}
/*
@ -735,8 +737,7 @@ update_member_method_lookup_table(
for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
{
if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
== 0)
((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name) == 0)
{
int *table = (int *)(if2cl + 1);
table[if_i] = cl_i;
@ -1030,9 +1031,9 @@ ex_class(exarg_T *eap)
{
int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
long start_lnum = SOURCING_LNUM;
char_u *arg = eap->arg;
int is_abstract = eap->cmdidx == CMD_abstract;
if (is_abstract)
{
if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
@ -1670,6 +1671,57 @@ ex_type(exarg_T *eap UNUSED)
// 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:
* - class member: SomeClass.varname
@ -1791,28 +1843,14 @@ class_object_index(
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];
if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == 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;
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);
}