]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1054: object member can't get type from initializer v9.0.1054
authorBram Moolenaar <Bram@vim.org>
Tue, 13 Dec 2022 21:14:28 +0000 (21:14 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 13 Dec 2022 21:14:28 +0000 (21:14 +0000)
Problem:    Object member can't get type from initializer.
Solution:   If there is no type specified try to use the type of the
            initializer.  Check for a valid type.

src/errors.h
src/proto/vim9type.pro
src/testdir/test_vim9_class.vim
src/version.c
src/vim9class.c
src/vim9type.c

index 9c8d59347b80593b29f975df8e49974661e88f6a..2cf29cc7f7e4637b5229121be425089b05ca745c 100644 (file)
@@ -3374,4 +3374,8 @@ EXTERN char e_object_required_found_str[]
        INIT(= N_("E1327: Object required, found %s"));
 EXTERN char e_constructor_default_value_must_be_vnone_str[]
        INIT(= N_("E1328: Constructor default value must be v:none: %s"));
+EXTERN char e_cannot_get_object_member_type_from_initializer_str[]
+       INIT(= N_("E1329: Cannot get object member type from initializer: %s"));
+EXTERN char e_invalid_type_for_object_member_str[]
+       INIT(= N_("E1330: Invalid type for object member: %s"));
 #endif
index 98c6091e98d0b74ad62ea475bcebe22a0f560e9a..d5e4d4816a2cbcaa8b4072a4b8962bd8d19cfb4f 100644 (file)
@@ -13,6 +13,7 @@ int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap);
 int type_any_or_unknown(type_T *type);
 int need_convert_to_bool(type_T *type, typval_T *tv);
 type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags);
+int valid_declaration_type(type_T *type);
 type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap);
 int check_typval_arg_type(type_T *expected, typval_T *actual_tv, char *func_name, int arg_idx);
 int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where);
index 3f66f3f60536db663eb0448929946c988443977f..7eecddaad22acedcb305e31d2f522a2b748c7b80 100644 (file)
@@ -231,7 +231,58 @@ def Test_class_default_new()
       assert_equal("none", chris.education)
   END
   v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+      class Person
+        this.name: string
+        this.age: number = 42
+        this.education: string = "unknown"
+
+        def new(this.name, this.age = v:none, this.education = v:none)
+        enddef
+      endclass
+
+      var missing = Person.new()
+  END
+  v9.CheckScriptFailure(lines, 'E119:')
 enddef
 
+def Test_class_object_member_inits()
+  var lines =<< trim END
+      vim9script
+      class TextPosition
+        this.lnum: number
+        this.col = 1
+        this.addcol: number = 2
+      endclass
+
+      var pos = TextPosition.new()
+      assert_equal(0, pos.lnum)
+      assert_equal(1, pos.col)
+      assert_equal(2, pos.addcol)
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+      class TextPosition
+        this.lnum
+        this.col = 1
+      endclass
+  END
+  v9.CheckScriptFailure(lines, 'E1022:')
+
+  lines =<< trim END
+      vim9script
+      class TextPosition
+        this.lnum = v:none
+        this.col = 1
+      endclass
+  END
+  v9.CheckScriptFailure(lines, 'E1330:')
+enddef
+
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index fbc5956e5a7e47bb71766c31afcc4d2b4ccb034f..6ab6ddc2fbf7b6e2270d7f8ba4b119f2a1f29a33 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1054,
 /**/
     1053,
 /**/
index 74a9bf370203418ac5770bcc5bb512969247bf0c..1713496529d91baf1689873fb82ec289617fe577 100644 (file)
@@ -125,43 +125,74 @@ ex_class(exarg_T *eap)
            char_u *varname_end = to_name_end(varname, FALSE);
 
            char_u *colon = skipwhite(varname_end);
-           // TODO: accept initialization and figure out type from it
-           if (*colon != ':')
+           char_u *type_arg = colon;
+           type_T *type = NULL;
+           if (*colon == ':')
            {
-               emsg(_(e_type_or_initialization_required));
-               break;
-           }
-           if (VIM_ISWHITE(*varname_end))
-           {
-               semsg(_(e_no_white_space_allowed_before_colon_str), varname);
-               break;
+               if (VIM_ISWHITE(*varname_end))
+               {
+                   semsg(_(e_no_white_space_allowed_before_colon_str),
+                                                                     varname);
+                   break;
+               }
+               if (!VIM_ISWHITE(colon[1]))
+               {
+                   semsg(_(e_white_space_required_after_str_str), ":",
+                                                                     varname);
+                   break;
+               }
+               type_arg = skipwhite(colon + 1);
+               type = parse_type(&type_arg, &type_list, TRUE);
+               if (type == NULL)
+                   break;
            }
-           if (!VIM_ISWHITE(colon[1]))
+
+           char_u *expr_start = skipwhite(type_arg);
+           char_u *expr_end = expr_start;
+           if (type == NULL && *expr_start != '=')
            {
-               semsg(_(e_white_space_required_after_str_str), ":", varname);
+               emsg(_(e_type_or_initialization_required));
                break;
            }
 
-           char_u *type_arg = skipwhite(colon + 1);
-           type_T *type = parse_type(&type_arg, &type_list, TRUE);
-           if (type == NULL)
-               break;
-
-           char_u *expr_start = skipwhite(type_arg);
-           if (*expr_start == '=' && (!VIM_ISWHITE(expr_start[-1])
-                                              || !VIM_ISWHITE(expr_start[1])))
+           if (*expr_start == '=')
            {
-               semsg(_(e_white_space_required_before_and_after_str_at_str),
+               if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
+               {
+                   semsg(_(e_white_space_required_before_and_after_str_at_str),
                                                                "=", type_arg);
-               break;
-           }
-           expr_start = skipwhite(expr_start + 1);
+                   break;
+               }
+               expr_start = skipwhite(expr_start + 1);
 
-           char_u *expr_end = expr_start;
-           evalarg_T   evalarg;
-           init_evalarg(&evalarg);
-           skip_expr(&expr_end, &evalarg);
-           clear_evalarg(&evalarg, NULL);
+               expr_end = expr_start;
+               evalarg_T evalarg;
+               fill_evalarg_from_eap(&evalarg, eap, FALSE);
+               skip_expr(&expr_end, &evalarg);
+
+               if (type == NULL)
+               {
+                   // No type specified, use the type of the initializer.
+                   typval_T tv;
+                   tv.v_type = VAR_UNKNOWN;
+                   char_u *expr = expr_start;
+                   int res = eval0(expr, &tv, eap, &evalarg);
+
+                   if (res == OK)
+                       type = typval2type(&tv, get_copyID(), &type_list,
+                                                              TVTT_DO_MEMBER);
+                   if (type == NULL)
+                   {
+                       semsg(_(e_cannot_get_object_member_type_from_initializer_str),
+                               expr_start);
+                       clear_evalarg(&evalarg, NULL);
+                       break;
+                   }
+               }
+               clear_evalarg(&evalarg, NULL);
+           }
+           if (!valid_declaration_type(type))
+               break;
 
            if (ga_grow(&objmembers, 1) == FAIL)
                break;
index 2d55cf224219ae74a10d1f178c89b7c6536a1e75..0709ce0437fb53c8364f9f7ba3f909220eef849c 100644 (file)
@@ -425,6 +425,17 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
        return &t_number;
     if (tv->v_type == VAR_BOOL)
        return &t_bool;
+    if (tv->v_type == VAR_SPECIAL)
+    {
+       if (tv->vval.v_number == VVAL_NULL)
+           return &t_null;
+       if (tv->vval.v_number == VVAL_NONE)
+           return &t_none;
+       if (tv->vval.v_number == VVAL_TRUE
+               || tv->vval.v_number == VVAL_TRUE)
+           return &t_bool;
+       return &t_unknown;
+    }
     if (tv->v_type == VAR_STRING)
        return &t_string;
     if (tv->v_type == VAR_BLOB)
@@ -619,6 +630,25 @@ typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags)
     return type;
 }
 
+/*
+ * Return TRUE if "type" can be used for a variable declaration.
+ * Give an error and return FALSE if not.
+ */
+    int
+valid_declaration_type(type_T *type)
+{
+    if (type->tt_type == VAR_SPECIAL  // null, none
+           || type->tt_type == VAR_VOID)
+    {
+       char *tofree = NULL;
+       char *name = type_name(type, &tofree);
+       semsg(_(e_invalid_type_for_object_member_str), name);
+       vim_free(tofree);
+       return FALSE;
+    }
+    return TRUE;
+}
+
 /*
  * Get a type_T for a typval_T, used for v: variables.
  * "type_list" is used to temporarily create types in.