0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 9.1.0988: Vim9: no error when using uninitialized var in new()

Problem:  Vim9: no error when using uninitialized var in new()
          (lifepillar, Aliaksei Budavei)
Solution: Give an error if an uninitialized object variable is referenced
          in new() (Yegappan Lakshmanan)

fixes: #14411
fixes: #16344
closes: #16374

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2025-01-03 10:50:08 +01:00
committed by Christian Brabandt
parent 27f2e473e1
commit b04af4cc96
4 changed files with 146 additions and 1 deletions

View File

@@ -3616,8 +3616,10 @@ EXTERN char e_duplicate_enum_str[]
INIT(= N_("E1428: Duplicate enum value: %s"));
EXTERN char e_class_can_only_be_used_in_script[]
INIT(= N_("E1429: Class can only be used in a script"));
EXTERN char e_uninitialized_object_var_reference[]
INIT(= N_("E1430: Uninitialized object variable '%s' referenced"));
#endif
// E1429 - E1499 unused (reserved for Vim9 class support)
// E1431 - E1499 unused (reserved for Vim9 class support)
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
EXTERN char e_fmt_arg_nr_unused_str[]

View File

@@ -11723,4 +11723,120 @@ def Test_use_object_method_in_a_method_call()
v9.CheckSourceFailure(lines, 'E1326: Variable "NewCost" not found in object "Foo"')
enddef
" Test for referencing an object variable which is not yet initialized
def Test_uninitialized_object_var()
var lines =<< trim END
vim9script
class Foo
const two: number = Foo.Two(this)
const one: number = 1
static def Two(that: Foo): number
return that.one + 2
enddef
endclass
echo Foo.Two(Foo.new())
END
v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'one' referenced")
lines =<< trim END
vim9script
class Foo
const one: number = Foo.One(this)
static def One(that: Foo): number
return 1
enddef
endclass
assert_equal(1, Foo.One(Foo.new()))
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Foo
const one: number = 1
const two: number = Foo.Two(this)
static def Two(that: Foo): number
return that.one + 1
enddef
endclass
assert_equal(2, Foo.Two(Foo.new()))
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Foo
const Id: func(any): any = ((_) => (v) => v)(this)
static def Id(that: Foo): func(any): any
return that.Id
enddef
endclass
assert_equal(5, Foo.Id(Foo.new())(5))
assert_equal(7, Foo.new().Id(7))
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Foo
const Id: func(any): any = ((that) => (_) => that)(this)
static def Id(that: Foo): func(any): any
return that.Id
enddef
endclass
const Id0: func(any): any = Foo.Id(Foo.new())
const Id1: func(any): any = Foo.new().Id
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Foo
const Id: any = Foo.Id(this)
static def Id(that: Foo): any
return that.Id
enddef
endclass
const Id2: any = Foo.Id(Foo.new())
const Id3: any = Foo.new().Id
END
v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'Id' referenced")
lines =<< trim END
vim9script
class Foo
var x: string = ''
var Y: func(): string = () => this.x
endclass
var foo = Foo.new('ok')
assert_equal('ok', foo.Y())
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Foo
var x: string = this.x
endclass
var foo = Foo.new('ok')
END
v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'x' referenced")
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
988,
/**/
987,
/**/

View File

@@ -4844,6 +4844,20 @@ exec_instructions(ectx_T *ectx)
int arg_set = tv->v_type != VAR_UNKNOWN
&& !(tv->v_type == VAR_SPECIAL
&& tv->vval.v_number == VVAL_NONE);
if (iptr->isn_type == ISN_JUMP_IF_ARG_NOT_SET && !arg_set)
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
ufunc_T *ufunc = df->df_ufunc;
// jump_arg_off is negative for arguments
size_t argidx = ufunc->uf_def_args.ga_len
+ iptr->isn_arg.jumparg.jump_arg_off
+ STACK_FRAME_SIZE;
type_T *t = ufunc->uf_arg_types[argidx];
tv->v_type = t->tt_type;
}
if (iptr->isn_type == ISN_JUMP_IF_ARG_SET ? arg_set : !arg_set)
ectx->ec_iidx = iptr->isn_arg.jumparg.jump_where;
break;
@@ -5718,6 +5732,17 @@ exec_instructions(ectx_T *ectx)
// The members are located right after the object struct.
typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
if (mtv->v_type == VAR_UNKNOWN)
{
// Referencing an object variable (without a type)
// which is not yet initialized. So the type is not
// yet known.
ocmember_T *m = &obj->obj_class->class_obj_members[idx];
SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_uninitialized_object_var_reference),
m->ocm_name);
goto on_error;
}
copy_tv(mtv, tv);
// Unreference the object after getting the member, it may