diff options
author | Ernie Rael <errael@raelity.com> | 2023-12-12 16:58:00 +0100 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2023-12-12 16:58:00 +0100 |
commit | 2025af165ec68d831f0f0f668a3ceac3f39142ef (patch) | |
tree | beb22176d7d2fa61be4054de5a5830cb31b933eb /src | |
parent | c1c3b83816c8e22c9a710fb0c0cebc180ee6ce1e (diff) |
patch 9.0.2160: instanceof() should use varargs as second argv9.0.2160
Problem: instanceof() should use varargs as second arg
Solution: Modify `instanceof()` to use varargs instead of list
Modify `instanceof()` to use varargs instead of list
Valid `instanceof()` arguments are `type`s. A `type` is not a value;
it cannot be added to a list.
This change is non-compatible with the current usage of instanceof;
but instanceof is relatively new and it's a trivial change.
fixes: #13421
closes: #13644
Signed-off-by: Ernie Rael <errael@raelity.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/errors.h | 12 | ||||
-rw-r--r-- | src/evalfunc.c | 44 | ||||
-rw-r--r-- | src/proto/typval.pro | 2 | ||||
-rw-r--r-- | src/testdir/test_vim9_assign.vim | 10 | ||||
-rw-r--r-- | src/testdir/test_vim9_builtin.vim | 8 | ||||
-rw-r--r-- | src/testdir/test_vim9_class.vim | 75 | ||||
-rw-r--r-- | src/testdir/test_vim9_typealias.vim | 16 | ||||
-rw-r--r-- | src/typval.c | 15 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9class.c | 43 |
10 files changed, 155 insertions, 72 deletions
diff --git a/src/errors.h b/src/errors.h index 2788800c00..7192514f0e 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1778,8 +1778,8 @@ EXTERN char e_can_only_compare_list_with_list[] INIT(= N_("E691: Can only compare List with List")); EXTERN char e_invalid_operation_for_list[] INIT(= N_("E692: Invalid operation for List")); -EXTERN char e_list_or_class_required_for_argument_nr[] - INIT(= N_("E693: List or Class required for argument %d")); +EXTERN char e_class_or_typealias_required_for_argument_nr[] + INIT(= N_("E693: Class or class typealias required for argument %d")); EXTERN char e_invalid_operation_for_funcrefs[] INIT(= N_("E694: Invalid operation for Funcrefs")); EXTERN char e_cannot_index_a_funcref[] @@ -3562,13 +3562,13 @@ EXTERN char e_using_typealias_as_value_str[] EXTERN char e_abstract_cannot_be_used_in_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")); + INIT(= N_("E1405: 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")); + INIT(= N_("E1406: 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")); + INIT(= N_("E1407: Cannot use a Typealias as a variable or value")); #endif -// E1405 - E1499 unused (reserved for Vim9 class support) +// E1408 - 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[] diff --git a/src/evalfunc.c b/src/evalfunc.c index b0b975061d..84d0a338ab 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -758,17 +758,23 @@ arg_string_or_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context } /* - * Check "type" is a list of 'any' or a class. + * Check varargs' "type" are class. */ static int -arg_class_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +varargs_class(type_T *type UNUSED, + type_T *decl_type UNUSED, + argcontext_T *context) { - if (type->tt_type == VAR_CLASS - || type->tt_type == VAR_LIST - || type_any_or_unknown(type)) - return OK; - arg_type_mismatch(&t_class, type, context->arg_idx + 1); - return FAIL; + for (int i = context->arg_idx; i < context->arg_count; ++i) + { + type2_T *types = &context->arg_types[i]; + if (types->type_curr->tt_type != VAR_CLASS) + { + semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1); + return FAIL; + } + } + return OK; } /* @@ -1152,7 +1158,7 @@ static argcheck_T arg1_len[] = {arg_len1}; static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr}; static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool}; static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func}; -static argcheck_T arg2_instanceof[] = {arg_object, arg_class_or_list}; +static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL }; static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func}; static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, NULL}; static argcheck_T arg25_matchadd[] = {arg_string, arg_string, arg_number, arg_number, arg_dict_any}; @@ -1621,6 +1627,12 @@ ret_maparg(int argcount, /* * Array with names and number of arguments of all internal functions * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH! + * + * The function may be varargs. In that case + * - f_max_argc == VARGS + * - f_argcheck must be NULL terminated. Last non-null argument + * should check all the remaining args. + * NOTE: if varargs, there can only be one NULL in f_argcheck array. */ typedef struct { @@ -1636,6 +1648,9 @@ typedef struct // implementation of function } funcentry_T; +// Set f_max_argc to VARGS for varargs. +#define VARGS CHAR_MAX + // values for f_argtype; zero means it cannot be used as a method #define FEARG_1 1 // base is the first argument #define FEARG_2 2 // base is the second argument @@ -2152,7 +2167,7 @@ static funcentry_T global_functions[] = ret_string, f_inputsecret}, {"insert", 2, 3, FEARG_1, arg23_insert, ret_first_arg, f_insert}, - {"instanceof", 2, 2, FEARG_1, arg2_instanceof, + {"instanceof", 2, VARGS, FEARG_1, arg2_instanceof, ret_bool, f_instanceof}, {"interrupt", 0, 0, 0, NULL, ret_void, f_interrupt}, @@ -3035,13 +3050,20 @@ internal_func_check_arg_types( if (argchecks == NULL) return OK; + int has_varargs = global_functions[idx].f_max_argc == VARGS; + argcontext_T context; context.arg_count = argcount; context.arg_types = types; context.arg_cctx = cctx; for (int i = 0; i < argcount; ++i) - if (argchecks[i] != NULL) + if (argchecks[i] == NULL) + { + if (has_varargs) + break; + } + else { context.arg_idx = i; if (argchecks[i](types[i].type_curr, types[i].type_decl, diff --git a/src/proto/typval.pro b/src/proto/typval.pro index 3f577adf78..c9845e0d13 100644 --- a/src/proto/typval.pro +++ b/src/proto/typval.pro @@ -53,7 +53,7 @@ int check_for_list_or_dict_or_blob_or_string_arg(typval_T *args, int idx); int check_for_opt_buffer_or_dict_arg(typval_T *args, int idx); int check_for_object_arg(typval_T *args, int idx); int tv_class_alias(typval_T *tv); -int check_for_class_or_list_arg(typval_T *args, int idx); +int check_for_class_or_typealias_args(typval_T *args, int idx); char_u *tv_get_string(typval_T *varp); char_u *tv_get_string_strict(typval_T *varp); char_u *tv_get_string_buf(typval_T *varp, char_u *buf); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 8be0769b72..e288d0306e 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -3064,7 +3064,7 @@ def Test_class_assign() endclass class D endclass - assert_fails('C = D', 'E1403: Class "D" cannot be used as a value') + assert_fails('C = D', 'E1405: Class "D" cannot be used as a value') END v9.CheckSourceSuccess(lines) enddef @@ -3105,7 +3105,7 @@ def Test_type_check() 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 = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number') - assert_fails('N = A', 'E1403: Class "A" cannot be used as a value') + assert_fails('N = A', 'E1405: Class "A" cannot be used as a value') assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>') # Use a compound operator with different RHS types @@ -3113,7 +3113,7 @@ def Test_type_check() assert_fails('N += l', 'E734: Wrong variable type for +=') assert_fails('N += b', 'E974: Using a Blob as a Number') assert_fails('N += Fn', 'E734: Wrong variable type for +=') - assert_fails('N += A', 'E1403: Class "A" cannot be used as a value') + assert_fails('N += A', 'E1405: Class "A" cannot be used as a value') assert_fails('N += o', 'E1320: Using an Object as a Number') # Initialize multiple variables using [] @@ -3121,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 [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 [X7: number, Y: number] = [1, A]', 'E1403: Class "A" cannot be used as a value') + assert_fails('var [X7: number, Y: number] = [1, A]', 'E1405: 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>') # String concatenation with various LHS types @@ -3129,7 +3129,7 @@ def Test_type_check() assert_fails('S ..= l', 'E734: Wrong variable type for .=') assert_fails('S ..= b', 'E976: Using a Blob as a String') assert_fails('S ..= Fn', 'E734: Wrong variable type for .=') - assert_fails('S ..= A', 'E1403: Class "A" cannot be used as a value') + assert_fails('S ..= A', 'E1405: Class "A" cannot be used as a value') assert_fails('S ..= o', 'E1324: Using an Object as a String') # String concatenation with various RHS types diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index f9d2e4181e..216118ec68 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -2316,7 +2316,7 @@ def Test_instanceof() endclass instanceof(Foo.new(), 123) END - v9.CheckScriptFailure(lines, 'E693: List or Class required for argument 2') + v9.CheckScriptFailure(lines, 'E693: Class or class typealias required for argument 2') lines =<< trim END vim9script @@ -2338,7 +2338,7 @@ def Test_instanceof() enddef Bar() END - v9.CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected class<Unknown> but got number') + v9.CheckScriptFailure(lines, 'E693: Class or class typealias required for argument 2') lines =<< trim END vim9script @@ -2346,7 +2346,7 @@ def Test_instanceof() endclass instanceof(Foo.new(), [{}]) END - v9.CheckSourceFailure(lines, 'E614: Class required') + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 2') lines =<< trim END vim9script @@ -2357,7 +2357,7 @@ def Test_instanceof() enddef Bar() END - v9.CheckSourceFailure(lines, 'E614: Class required') + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 2') enddef def Test_invert() diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 84ea1cb357..e0f5db6253 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -3328,6 +3328,11 @@ def Test_instanceof() class Base3 extends Mix1 endclass + type AliasBase1 = Base1 + type AliasBase2 = Base2 + type AliasIntf1 = Intf1 + type AliasMix1 = Mix1 + var b1 = Base1.new() var b2 = Base2.new() var b3 = Base3.new() @@ -3336,8 +3341,13 @@ def Test_instanceof() assert_true(instanceof(b2, Base1)) assert_false(instanceof(b1, Base2)) assert_true(instanceof(b3, Mix1)) - assert_false(instanceof(b3, [])) - assert_true(instanceof(b3, [Base1, Base2, Intf1])) + assert_true(instanceof(b3, Base1, Base2, Intf1)) + + assert_true(instanceof(b1, AliasBase1)) + assert_true(instanceof(b2, AliasBase1)) + assert_false(instanceof(b1, AliasBase2)) + assert_true(instanceof(b3, AliasMix1)) + assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1)) def Foo() var a1 = Base1.new() @@ -3348,8 +3358,13 @@ def Test_instanceof() assert_true(instanceof(a2, Base1)) assert_false(instanceof(a1, Base2)) assert_true(instanceof(a3, Mix1)) - assert_false(instanceof(a3, [])) - assert_true(instanceof(a3, [Base1, Base2, Intf1])) + assert_true(instanceof(a3, Base1, Base2, Intf1)) + + assert_true(instanceof(a1, AliasBase1)) + assert_true(instanceof(a2, AliasBase1)) + assert_false(instanceof(a1, AliasBase2)) + assert_true(instanceof(a3, AliasMix1)) + assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1)) enddef Foo() @@ -3358,6 +3373,58 @@ def Test_instanceof() END v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class Base1 + endclass + instanceof(Base1.new()) + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + + lines =<< trim END + vim9script + + class Base1 + endclass + def F() + instanceof(Base1.new()) + enddef + F() + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + + lines =<< trim END + vim9script + + class Base1 + endclass + + class Base2 + endclass + + var o = Base2.new() + instanceof(o, Base1, Base2, 3) + END + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10) + + lines =<< trim END + vim9script + + class Base1 + endclass + + class Base2 + endclass + + def F() + var o = Base2.new() + instanceof(o, Base1, Base2, 3) + enddef + F() + END + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4') enddef " Test for calling a method in the parent class that is extended partially. diff --git a/src/testdir/test_vim9_typealias.vim b/src/testdir/test_vim9_typealias.vim index 34c9e0ea72..20829340f2 100644 --- a/src/testdir/test_vim9_typealias.vim +++ b/src/testdir/test_vim9_typealias.vim @@ -212,7 +212,7 @@ def Test_typealias() enddef Foo() END - v9.CheckSourceFailure(lines, 'E1406: Cannot use a Typealias as a variable or value', 1) + v9.CheckSourceFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1) # Using type alias in an expression (script level) lines =<< trim END @@ -516,8 +516,18 @@ def Test_typealias_instanceof() var o = C.new() assert_equal(1, instanceof(o, Ctype)) type Ntype = number - assert_fails('instanceof(o, Ntype)', 'E693: List or Class required for argument 2') - assert_equal(1, instanceof(o, [Ctype])) + assert_fails('instanceof(o, Ntype)', 'E693: Class or class typealias required for argument 2') + assert_fails('instanceof(o, Ctype, Ntype)', 'E693: Class or class typealias required for argument 3') + + def F() + var x = instanceof(o, Ntype) + enddef + assert_fails('F()', 'E693: Class or class typealias required for argument 2') + + def G(): bool + return instanceof(o, Ctype) + enddef + assert_equal(1, G()) END v9.CheckScriptSuccess(lines) enddef diff --git a/src/typval.c b/src/typval.c index 078e2eb058..65cd912c32 100644 --- a/src/typval.c +++ b/src/typval.c @@ -1014,16 +1014,19 @@ tv_class_alias(typval_T *tv) } /* - * Give an error and return FAIL unless "args[idx]" is a class or a list. + * Give an error and return FAIL unless "args[idx]" is a class + * or class typealias. */ int -check_for_class_or_list_arg(typval_T *args, int idx) +check_for_class_or_typealias_args(typval_T *args, int idx) { - if (args[idx].v_type != VAR_CLASS && args[idx].v_type != VAR_LIST - && !tv_class_alias(&args[idx])) + for (int i = idx; args[i].v_type != VAR_UNKNOWN; ++i) { - semsg(_(e_list_or_class_required_for_argument_nr), idx + 1); - return FAIL; + if (args[i].v_type != VAR_CLASS && !tv_class_alias(&args[idx])) + { + semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1); + return FAIL; + } } return OK; } diff --git a/src/version.c b/src/version.c index 15ff51dd72..ad5bda644d 100644 --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2160, +/**/ 2159, /**/ 2158, diff --git a/src/vim9class.c b/src/vim9class.c index c0705ff3ca..4b065c8590 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -3161,58 +3161,37 @@ class_instance_of(class_T *cl, class_T *other_cl) } /* - * "instanceof(object, classinfo)" function + * "instanceof(object, classinfo, ...)" function */ void f_instanceof(typval_T *argvars, typval_T *rettv) { typval_T *object_tv = &argvars[0]; typval_T *classinfo_tv = &argvars[1]; - listitem_T *li; class_T *c; rettv->vval.v_number = VVAL_FALSE; if (check_for_object_arg(argvars, 0) == FAIL - || check_for_class_or_list_arg(argvars, 1) == FAIL) + || check_for_class_or_typealias_args(argvars, 1) == FAIL) return; if (object_tv->vval.v_object == NULL) return; - if (classinfo_tv->v_type == VAR_LIST) + for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv) { - FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li) - { - if (li->li_tv.v_type != VAR_CLASS && !tv_class_alias(&li->li_tv)) - { - emsg(_(e_class_required)); - return; - } - - if (li->li_tv.v_type == VAR_TYPEALIAS) - c = li->li_tv.vval.v_typealias->ta_type->tt_class; - else - c = li->li_tv.vval.v_class; + if (classinfo_tv->v_type == VAR_TYPEALIAS) + c = classinfo_tv->vval.v_typealias->ta_type->tt_class; + else + c = classinfo_tv->vval.v_class; - if (class_instance_of(object_tv->vval.v_object->obj_class, c) - == TRUE) - { - rettv->vval.v_number = VVAL_TRUE; - return; - } + if (class_instance_of(object_tv->vval.v_object->obj_class, c)) + { + rettv->vval.v_number = VVAL_TRUE; + return; } - - return; } - - if (classinfo_tv->v_type == VAR_TYPEALIAS) - c = classinfo_tv->vval.v_typealias->ta_type->tt_class; - else - c = classinfo_tv->vval.v_class; - - rettv->vval.v_number = - class_instance_of(object_tv->vval.v_object->obj_class, c); } #endif // FEAT_EVAL |