summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/errors.h4
-rw-r--r--src/testdir/test_vim9_class.vim97
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c76
4 files changed, 167 insertions, 12 deletions
diff --git a/src/errors.h b/src/errors.h
index cc0af8ad13..bf994333e8 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3489,6 +3489,10 @@ EXTERN char e_cannot_access_private_method_str[]
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
index f7d6b73f49..319eb0546d 100644
--- 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
index 7b8683e7cb..ce28694860 100644
--- 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
index 36a5d0a358..955bd0cbcd 100644
--- 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)
{