forked from aniani/vim
patch 9.0.2156: Vim9: can use typealias in assignment
Problem: Vim9: can use typealias in an assignment Solution: Generate errors when class/typealias involved in the rhs of an assignment closes: #13637 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Christian Brabandt <cb@256bit.org> Generate errors when class/typealias involved in assignment.
This commit is contained in:
committed by
Christian Brabandt
parent
fa920da283
commit
9ed53752df
@@ -3557,10 +3557,16 @@ EXTERN char e_using_typealias_as_float[]
|
|||||||
INIT(= N_("E1401: Using type alias \"%s\" as a Float"));
|
INIT(= N_("E1401: Using type alias \"%s\" as a Float"));
|
||||||
EXTERN char e_using_typealias_as_string[]
|
EXTERN char e_using_typealias_as_string[]
|
||||||
INIT(= N_("E1402: Using type alias \"%s\" as a String"));
|
INIT(= N_("E1402: Using type alias \"%s\" as a String"));
|
||||||
EXTERN char e_using_typealias_as_value[]
|
EXTERN char e_using_typealias_as_value_str[]
|
||||||
INIT(= N_("E1403: Type alias \"%s\" cannot be used as a value"));
|
INIT(= N_("E1403: Type alias \"%s\" cannot be used as a value"));
|
||||||
EXTERN char e_abstract_cannot_be_used_in_interface[]
|
EXTERN char e_abstract_cannot_be_used_in_interface[]
|
||||||
INIT(= N_("E1404: Abstract cannot be used in an interface"));
|
INIT(= N_("E1404: Abstract cannot be used in an interface"));
|
||||||
|
EXTERN char e_using_class_as_value_str[]
|
||||||
|
INIT(= N_("E1403: Class \"%s\" cannot be used as a value"));
|
||||||
|
EXTERN char e_using_class_as_var_val[]
|
||||||
|
INIT(= N_("E1405: Cannot use a Class as a variable or value"));
|
||||||
|
EXTERN char e_using_typealias_as_var_val[]
|
||||||
|
INIT(= N_("E1406: Cannot use a Typealias as a variable or value"));
|
||||||
#endif
|
#endif
|
||||||
// E1405 - E1499 unused (reserved for Vim9 class support)
|
// E1405 - E1499 unused (reserved for Vim9 class support)
|
||||||
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
|
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
|
||||||
|
@@ -1836,11 +1836,8 @@ ex_let_one(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tv->v_type == VAR_TYPEALIAS)
|
if (check_typval_is_value(tv) == FAIL)
|
||||||
{
|
|
||||||
semsg(_(e_using_typealias_as_value), tv->vval.v_typealias->ta_name);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
if (*arg == '$')
|
if (*arg == '$')
|
||||||
{
|
{
|
||||||
|
@@ -35,4 +35,7 @@ type_T *get_member_type_from_stack(int count, int skip, cctx_T *cctx);
|
|||||||
char *vartype_name(vartype_T type);
|
char *vartype_name(vartype_T type);
|
||||||
char *type_name(type_T *type, char **tofree);
|
char *type_name(type_T *type, char **tofree);
|
||||||
void f_typename(typval_T *argvars, typval_T *rettv);
|
void f_typename(typval_T *argvars, typval_T *rettv);
|
||||||
|
int check_vartype_is_value(vartype_T typ);
|
||||||
|
int check_typval_is_value(typval_T *tv);
|
||||||
|
int check_type_is_value(type_T *type);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -3057,6 +3057,18 @@ def Test_dict_item_assign()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_class_assign()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class C
|
||||||
|
endclass
|
||||||
|
class D
|
||||||
|
endclass
|
||||||
|
assert_fails('C = D', 'E1403: Class "D" cannot be used as a value')
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
" Test for using various types (dict, list, blob, funcref, class) as variable
|
" Test for using various types (dict, list, blob, funcref, class) as variable
|
||||||
" in assignments with a different type
|
" in assignments with a different type
|
||||||
def Test_type_check()
|
def Test_type_check()
|
||||||
@@ -3093,7 +3105,7 @@ def Test_type_check()
|
|||||||
assert_fails('N = l', 'E1012: Type mismatch; expected number but got list<number>')
|
assert_fails('N = l', 'E1012: Type mismatch; expected number but got list<number>')
|
||||||
assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
|
assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
|
||||||
assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
||||||
assert_fails('N = A', 'E1012: Type mismatch; expected number but got class<A>')
|
assert_fails('N = A', 'E1403: Class "A" cannot be used as a value')
|
||||||
assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>')
|
assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>')
|
||||||
|
|
||||||
# Use a compound operator with different RHS types
|
# Use a compound operator with different RHS types
|
||||||
@@ -3101,7 +3113,7 @@ def Test_type_check()
|
|||||||
assert_fails('N += l', 'E734: Wrong variable type for +=')
|
assert_fails('N += l', 'E734: Wrong variable type for +=')
|
||||||
assert_fails('N += b', 'E974: Using a Blob as a Number')
|
assert_fails('N += b', 'E974: Using a Blob as a Number')
|
||||||
assert_fails('N += Fn', 'E734: Wrong variable type for +=')
|
assert_fails('N += Fn', 'E734: Wrong variable type for +=')
|
||||||
assert_fails('N += A', 'E1319: Using a Class as a Number')
|
assert_fails('N += A', 'E1403: Class "A" cannot be used as a value')
|
||||||
assert_fails('N += o', 'E1320: Using an Object as a Number')
|
assert_fails('N += o', 'E1320: Using an Object as a Number')
|
||||||
|
|
||||||
# Initialize multiple variables using []
|
# Initialize multiple variables using []
|
||||||
@@ -3109,7 +3121,7 @@ def Test_type_check()
|
|||||||
assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list<number>')
|
assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list<number>')
|
||||||
assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob')
|
assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob')
|
||||||
assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
||||||
assert_fails('var [X7: number, Y: number] = [1, A]', 'E1012: Type mismatch; expected number but got class<A>')
|
assert_fails('var [X7: number, Y: number] = [1, A]', 'E1403: Class "A" cannot be used as a value')
|
||||||
assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object<A>')
|
assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object<A>')
|
||||||
|
|
||||||
# String concatenation with various LHS types
|
# String concatenation with various LHS types
|
||||||
@@ -3117,7 +3129,7 @@ def Test_type_check()
|
|||||||
assert_fails('S ..= l', 'E734: Wrong variable type for .=')
|
assert_fails('S ..= l', 'E734: Wrong variable type for .=')
|
||||||
assert_fails('S ..= b', 'E976: Using a Blob as a String')
|
assert_fails('S ..= b', 'E976: Using a Blob as a String')
|
||||||
assert_fails('S ..= Fn', 'E734: Wrong variable type for .=')
|
assert_fails('S ..= Fn', 'E734: Wrong variable type for .=')
|
||||||
assert_fails('S ..= A', 'E1323: Using a Class as a String')
|
assert_fails('S ..= A', 'E1403: Class "A" cannot be used as a value')
|
||||||
assert_fails('S ..= o', 'E1324: Using an Object as a String')
|
assert_fails('S ..= o', 'E1324: Using an Object as a String')
|
||||||
|
|
||||||
# String concatenation with various RHS types
|
# String concatenation with various RHS types
|
||||||
|
@@ -3093,25 +3093,77 @@ def Test_closure_in_class()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_call_constructor_from_legacy()
|
def Test_construct_object_from_legacy()
|
||||||
|
# Cannot directly invoke constructor from legacy
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
var newCalled = 'false'
|
var newCalled = false
|
||||||
|
|
||||||
class A
|
class A
|
||||||
def new()
|
def new(arg: string)
|
||||||
newCalled = 'true'
|
newCalled = true
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
export def F(options = {}): any
|
export def CreateA(...args: list<any>): A
|
||||||
return A
|
return call(A.new, args)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
g:p = F()
|
g:P = CreateA
|
||||||
legacy call p.new()
|
legacy call g:P('some_arg')
|
||||||
assert_equal('true', newCalled)
|
assert_equal(true, newCalled)
|
||||||
|
unlet g:P
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
var newCalled = false
|
||||||
|
|
||||||
|
class A
|
||||||
|
static def CreateA(options = {}): any
|
||||||
|
return A.new()
|
||||||
|
enddef
|
||||||
|
def new()
|
||||||
|
newCalled = true
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
g:P = A.CreateA
|
||||||
|
legacy call g:P()
|
||||||
|
assert_equal(true, newCalled)
|
||||||
|
unlet g:P
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
# This also tests invoking "new()" with "call"
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
var createdObject: any
|
||||||
|
|
||||||
|
class A
|
||||||
|
this.val1: number
|
||||||
|
this.val2: number
|
||||||
|
static def CreateA(...args: list<any>): any
|
||||||
|
createdObject = call(A.new, args)
|
||||||
|
return createdObject
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
g:P = A.CreateA
|
||||||
|
legacy call g:P(3, 5)
|
||||||
|
assert_equal(3, createdObject.val1)
|
||||||
|
assert_equal(5, createdObject.val2)
|
||||||
|
legacy call g:P()
|
||||||
|
assert_equal(0, createdObject.val1)
|
||||||
|
assert_equal(0, createdObject.val2)
|
||||||
|
legacy call g:P(7)
|
||||||
|
assert_equal(7, createdObject.val1)
|
||||||
|
assert_equal(0, createdObject.val2)
|
||||||
|
unlet g:P
|
||||||
END
|
END
|
||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
@@ -212,7 +212,7 @@ def Test_typealias()
|
|||||||
enddef
|
enddef
|
||||||
Foo()
|
Foo()
|
||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 1)
|
v9.CheckSourceFailure(lines, 'E1406: Cannot use a Typealias as a variable or value', 1)
|
||||||
|
|
||||||
# Using type alias in an expression (script level)
|
# Using type alias in an expression (script level)
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2156,
|
||||||
/**/
|
/**/
|
||||||
2155,
|
2155,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -2797,6 +2797,8 @@ compile_assignment(
|
|||||||
|
|
||||||
rhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
|
rhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
|
||||||
: get_type_on_stack(cctx, 0);
|
: get_type_on_stack(cctx, 0);
|
||||||
|
if (check_type_is_value(rhs_type) == FAIL)
|
||||||
|
goto theend;
|
||||||
if (lhs.lhs_lvar != NULL && (is_decl || !lhs.lhs_has_type))
|
if (lhs.lhs_lvar != NULL && (is_decl || !lhs.lhs_has_type))
|
||||||
{
|
{
|
||||||
if ((rhs_type->tt_type == VAR_FUNC
|
if ((rhs_type->tt_type == VAR_FUNC
|
||||||
|
@@ -3813,10 +3813,8 @@ exec_instructions(ectx_T *ectx)
|
|||||||
case ISN_STORE:
|
case ISN_STORE:
|
||||||
--ectx->ec_stack.ga_len;
|
--ectx->ec_stack.ga_len;
|
||||||
tv = STACK_TV_VAR(iptr->isn_arg.number);
|
tv = STACK_TV_VAR(iptr->isn_arg.number);
|
||||||
if (STACK_TV_BOT(0)->v_type == VAR_TYPEALIAS)
|
if (check_typval_is_value(STACK_TV_BOT(0)) == FAIL)
|
||||||
{
|
{
|
||||||
semsg(_(e_using_typealias_as_value),
|
|
||||||
STACK_TV_BOT(0)->vval.v_typealias->ta_name);
|
|
||||||
clear_tv(STACK_TV_BOT(0));
|
clear_tv(STACK_TV_BOT(0));
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
@@ -1846,4 +1846,66 @@ f_typename(typval_T *argvars, typval_T *rettv)
|
|||||||
clear_type_list(&type_list);
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the typval_T is a value type; report an error if it is not.
|
||||||
|
* Note: a type, user defined or typealias, is not a value type.
|
||||||
|
*
|
||||||
|
* Return OK if it's a value type, else FAIL
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_typval_is_value(typval_T *tv)
|
||||||
|
{
|
||||||
|
if (tv->v_type == VAR_CLASS)
|
||||||
|
{
|
||||||
|
semsg(_(e_using_class_as_value_str), tv->vval.v_class->class_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (tv->v_type == VAR_TYPEALIAS)
|
||||||
|
{
|
||||||
|
semsg(_(e_using_typealias_as_value_str), tv->vval.v_typealias->ta_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as above, except check type_T.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_type_is_value(type_T *type)
|
||||||
|
{
|
||||||
|
if (type->tt_type == VAR_CLASS)
|
||||||
|
{
|
||||||
|
semsg(_(e_using_class_as_value_str), type->tt_class->class_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (type->tt_type == VAR_TYPEALIAS)
|
||||||
|
{
|
||||||
|
// Not sure what could be done here to get a name
|
||||||
|
// TODO: MAYBE AN OPTIONAL ARGUMENT
|
||||||
|
emsg(_(e_using_typealias_as_var_val));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as above, except check vartype_T.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_vartype_is_value(vartype_T typ)
|
||||||
|
{
|
||||||
|
if (typ == VAR_CLASS)
|
||||||
|
{
|
||||||
|
emsg(_(e_using_class_as_var_val));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (typ == VAR_TYPEALIAS)
|
||||||
|
{
|
||||||
|
emsg(_(e_using_typealias_as_var_val));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // FEAT_EVAL
|
#endif // FEAT_EVAL
|
||||||
|
Reference in New Issue
Block a user