summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-12-13 21:14:28 +0000
committerBram Moolenaar <Bram@vim.org>2022-12-13 21:14:28 +0000
commit74e1274edf632b83d2948a2a2d519589eff52997 (patch)
tree3513a53175d76a294e5d189525480f276591a8e3
parent65b0d1676814ee08fb58ef8d64dd342d1d883192 (diff)
patch 9.0.1054: object member can't get type from initializerv9.0.1054
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.
-rw-r--r--src/errors.h4
-rw-r--r--src/proto/vim9type.pro1
-rw-r--r--src/testdir/test_vim9_class.vim51
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c87
-rw-r--r--src/vim9type.c30
6 files changed, 147 insertions, 28 deletions
diff --git a/src/errors.h b/src/errors.h
index 9c8d59347b..2cf29cc7f7 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -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
diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro
index 98c6091e98..d5e4d4816a 100644
--- a/src/proto/vim9type.pro
+++ b/src/proto/vim9type.pro
@@ -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);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 3f66f3f605..7eecddaad2 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index fbc5956e5a..6ab6ddc2fb 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1054,
+/**/
1053,
/**/
1052,
diff --git a/src/vim9class.c b/src/vim9class.c
index 74a9bf3702..1713496529 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -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;
diff --git a/src/vim9type.c b/src/vim9type.c
index 2d55cf2242..0709ce0437 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -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)
@@ -620,6 +631,25 @@ typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags)
}
/*
+ * 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.
*/