patch 9.0.1949: Vim9: allows reserved keywords as members

Problem:  Vim9: allows reserved keywords as members
Solution: Disallow reserved keywords, disallow
          duplicate object and class variables

closes: #13209

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index c272aab..681f614 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -4150,7 +4150,7 @@
     assert_equal(10, C.val)
     assert_equal(20, c.val)
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
 
   # Duplicate object member variable in a derived class
   lines =<< trim END
@@ -5768,4 +5768,91 @@
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Test for duplicate object and class variable
+def Test_duplicate_variable()
+  # Object variable name is same as the class variable name
+  var lines =<< trim END
+    vim9script
+    class A
+      public static sval: number
+      public this.sval: number
+    endclass
+    var a = A.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+
+  # Duplicate variable name and calling a class method
+  lines =<< trim END
+    vim9script
+    class A
+      public static sval: number
+      public this.sval: number
+      def F1()
+        echo this.sval
+      enddef
+      static def F2()
+        echo sval
+      enddef
+    endclass
+    A.F2()
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+
+  # Duplicate variable with an empty constructor
+  lines =<< trim END
+    vim9script
+    class A
+      public static sval: number
+      public this.sval: number
+      def new()
+      enddef
+    endclass
+    var a = A.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+enddef
+
+" Test for using a reserved keyword as a variable name
+def Test_reserved_varname()
+  for kword in ['true', 'false', 'null', 'null_blob', 'null_dict',
+                'null_function', 'null_list', 'null_partial', 'null_string',
+                'null_channel', 'null_job', 'super', 'this']
+
+    var lines =<< trim eval END
+      vim9script
+      class C
+        public this.{kword}: list<number> = [1, 2, 3]
+      endclass
+      var o = C.new()
+    END
+    v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+    lines =<< trim eval END
+      vim9script
+      class C
+        public this.{kword}: list<number> = [1, 2, 3]
+        def new()
+        enddef
+      endclass
+      var o = C.new()
+    END
+    v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+    lines =<< trim eval END
+      vim9script
+      class C
+        public this.{kword}: list<number> = [1, 2, 3]
+        def new()
+        enddef
+        def F()
+          echo this.{kword}
+        enddef
+      endclass
+      var o = C.new()
+      o.F()
+    END
+    v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+  endfor
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 49b4067..dade6d2 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1949,
+/**/
     1948,
 /**/
     1947,
diff --git a/src/vim9class.c b/src/vim9class.c
index 8b07d76..4c70f8d 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -893,24 +893,52 @@
 }
 
 /*
- * Returns TRUE if the member "varname" is already defined.
+ * Returns TRUE if 'varname' is a reserved keyword name
  */
     static int
-is_duplicate_member(garray_T *mgap, char_u *varname, char_u *varname_end)
+is_reserved_varname(char_u *varname, char_u *varname_end)
+{
+    int reserved = FALSE;
+    char_u save_varname_end = *varname_end;
+    *varname_end = NUL;
+
+    reserved = check_reserved_name(varname, FALSE) == FAIL;
+
+    *varname_end = save_varname_end;
+
+    return reserved;
+}
+
+/*
+ * Returns TRUE if the variable "varname" is already defined either as a class
+ * variable or as an object variable.
+ */
+    static int
+is_duplicate_variable(
+    garray_T	*class_members,
+    garray_T	*obj_members,
+    char_u	*varname,
+    char_u	*varname_end)
 {
     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 loop = 1; loop <= 2; loop++)
     {
-	ocmember_T *m = ((ocmember_T *)mgap->ga_data) + i;
-	char_u	*qstr = *m->ocm_name == '_' ? m->ocm_name + 1 : m->ocm_name;
-	if (STRCMP(pstr, qstr) == 0)
+	// loop == 1: class variables, loop == 2: object variables
+	garray_T    *vgap = (loop == 1) ? class_members : obj_members;
+	for (int i = 0; i < vgap->ga_len; ++i)
 	{
-	    semsg(_(e_duplicate_variable_str), name);
-	    dup = TRUE;
-	    break;
+	    ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i;
+	    char_u	*qstr = *m->ocm_name == '_' ? m->ocm_name + 1
+						    : m->ocm_name;
+	    if (STRCMP(pstr, qstr) == 0)
+	    {
+		semsg(_(e_duplicate_variable_str), name);
+		dup = TRUE;
+		break;
+	    }
 	}
     }
 
@@ -1646,7 +1674,13 @@
 			  &varname_end, &type_list, &type,
 			  is_class ? &init_expr: NULL) == FAIL)
 		break;
-	    if (is_duplicate_member(&objmembers, varname, varname_end))
+	    if (is_reserved_varname(varname, varname_end))
+	    {
+		vim_free(init_expr);
+		break;
+	    }
+	    if (is_duplicate_variable(&classmembers, &objmembers, varname,
+								varname_end))
 	    {
 		vim_free(init_expr);
 		break;
@@ -1768,7 +1802,13 @@
 		      &varname_end, &type_list, &type,
 		      is_class ? &init_expr : NULL) == FAIL)
 		break;
-	    if (is_duplicate_member(&classmembers, varname, varname_end))
+	    if (is_reserved_varname(varname, varname_end))
+	    {
+		vim_free(init_expr);
+		break;
+	    }
+	    if (is_duplicate_variable(&classmembers, &objmembers, varname,
+								varname_end))
 	    {
 		vim_free(init_expr);
 		break;