# HG changeset patch # User Christian Brabandt # Date 1693251010 -7200 # Node ID 9ef43d02dd8fe434ba2192b5bb121fab897c8f9f # Parent 29b9f832b7d340f376182906e781b1d7f333df25 patch 9.0.1814: Vim9 no error on duplicate object member var Commit: https://github.com/vim/vim/commit/2ba9d2e14e3633c92a32abba2a12533f93fefbf5 Author: Yegappan Lakshmanan Date: Mon Aug 28 21:26:23 2023 +0200 patch 9.0.1814: Vim9 no error on duplicate object member var Problem: Vim9 no error on duplicate object member var Solution: detect duplicate members and error out closes: #12938 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3489,6 +3489,10 @@ EXTERN char e_cannot_access_private_meth EXTERN char e_interface_str_and_class_str_function_access_not_same[] INIT(= N_("E1367: Access type of class method %s differs from interface method %s")); +EXTERN char e_static_cannot_be_followed_by_this[] + INIT(= N_("E1368: Static cannot be followed by \"this\" in a member name")); +EXTERN char e_duplicate_member_str[] + INIT(= N_("E1369: Duplicate member: %s")); EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -894,6 +894,15 @@ def Test_class_object_member_access() endclass END v9.CheckScriptFailure(lines, 'E1065:') + + # Test for "static" cannot be followed by "this". + lines =<< trim END + vim9script + class Something + static this.val = 1 + endclass + END + v9.CheckScriptFailure(lines, 'E1368: Static cannot be followed by "this" in a member name') enddef def Test_class_object_compare() @@ -3438,4 +3447,92 @@ def Test_objmethod_funcarg() v9.CheckScriptSuccess(lines) enddef +" Test for declaring duplicate object and class members +def Test_dup_member_variable() + # Duplicate member variable + var lines =<< trim END + vim9script + class C + this.val = 10 + this.val = 20 + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + + # Duplicate private member variable + lines =<< trim END + vim9script + class C + this._val = 10 + this._val = 20 + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + + # Duplicate public member variable + lines =<< trim END + vim9script + class C + public this.val = 10 + public this.val = 20 + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + + # Duplicate private member variable + lines =<< trim END + vim9script + class C + this.val = 10 + this._val = 20 + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + + # Duplicate public and private member variable + lines =<< trim END + vim9script + class C + this._val = 20 + public this.val = 10 + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + + # Duplicate class member variable + lines =<< trim END + vim9script + class C + static s: string = "abc" + static _s: string = "def" + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _s') + + # Duplicate public and private class member variable + lines =<< trim END + vim9script + class C + public static s: string = "abc" + static _s: string = "def" + endclass + END + v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _s') + + # Duplicate class and object member variable + lines =<< trim END + vim9script + class C + static val = 10 + this.val = 20 + def new() + enddef + endclass + var c = C.new() + assert_equal(10, C.val) + assert_equal(20, c.val) + END + v9.CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1814, +/**/ 1813, /**/ 1812, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -24,7 +24,7 @@ /* * Parse a member declaration, both object and class member. * Returns OK or FAIL. When OK then "varname_end" is set to just after the - * variable name and "type_ret" is set to the decleared or detected type. + * variable name and "type_ret" is set to the declared or detected type. * "init_expr" is set to the initialisation expression (allocated), if there is * one. For an interface "init_expr" is NULL. */ @@ -490,6 +490,52 @@ check_func_arg_names( } /* + * Returns TRUE if the member "varname" is already defined. + */ + static int +is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end) +{ + char_u *pstr = (*varname == '_') ? varname + 1 : varname; + + 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) + { + char_u *name = vim_strnsave(varname, varname_end - varname); + semsg(_(e_duplicate_member_str), name); + vim_free(name); + return TRUE; + } + } + + return FALSE; +} + +/* + * Returns TRUE if the method "name" is already defined. + */ + static int +is_duplicate_method(garray_T *fgap, char_u *name) +{ + char_u *pstr = (*name == '_') ? name + 1 : name; + + for (int i = 0; i < fgap->ga_len; ++i) + { + char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name; + char_u *qstr = *n == '_' ? n + 1 : n; + if (STRCMP(pstr, qstr) == 0) + { + semsg(_(e_duplicate_function_str), name); + return TRUE; + } + } + + return FALSE; +} + +/* * Update the interface class lookup table for the member index on the * interface to the member index in the class implementing the interface. * And a lookup table for the object method index on the interface @@ -1080,6 +1126,11 @@ early_ret: semsg(_(e_invalid_object_member_declaration_str), p); break; } + if (has_static) + { + emsg(_(e_static_cannot_be_followed_by_this)); + break; + } char_u *varname = p + 5; char_u *varname_end = NULL; type_T *type = NULL; @@ -1088,6 +1139,11 @@ early_ret: &varname_end, &type_list, &type, is_class ? &init_expr: NULL) == FAIL) break; + if (is_duplicate_member(&objmembers, varname, varname_end)) + { + vim_free(init_expr); + break; + } if (add_member(&objmembers, varname, varname_end, has_public, type, init_expr) == FAIL) { @@ -1154,17 +1210,8 @@ early_ret: garray_T *fgap = has_static || is_new ? &classfunctions : &objmethods; // Check the name isn't used already. - for (int i = 0; i < fgap->ga_len; ++i) - { - char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name; - char_u *pstr = *name == '_' ? name + 1 : name; - char_u *qstr = *n == '_' ? n + 1 : n; - if (STRCMP(pstr, qstr) == 0) - { - semsg(_(e_duplicate_function_str), name); - break; - } - } + if (is_duplicate_method(fgap, name)) + break; if (ga_grow(fgap, 1) == OK) { @@ -1197,6 +1244,11 @@ early_ret: &varname_end, &type_list, &type, is_class ? &init_expr : NULL) == FAIL) break; + if (is_duplicate_member(&classmembers, varname, varname_end)) + { + vim_free(init_expr); + break; + } if (add_member(&classmembers, varname, varname_end, has_public, type, init_expr) == FAIL) {