0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 9.0.2160: instanceof() should use varargs as second arg

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>
This commit is contained in:
Ernie Rael 2023-12-12 16:58:00 +01:00 committed by Christian Brabandt
parent c1c3b83816
commit 2025af165e
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
11 changed files with 159 additions and 76 deletions

View File

@ -5058,12 +5058,12 @@ insert({object}, {item} [, {idx}]) *insert()*
*instanceof()* *E614* *E616* *E693*
instanceof({object}, {class})
The result is a Number, which is |TRUE| when the {object}
argument is a direct or indirect instance of a |Class|
specified by {class}.
When {class} is a |List| the function returns |TRUE| when
argument is a direct or indirect instance of a |Class|,
|Interface|, or class |:type| alias specified by {class}.
If {class} is varargs, the function returns |TRUE| when
{object} is an instance of any of the specified classes.
Example: >
instanceof(animal, [Dog, Cat])
instanceof(animal, Dog, Cat)
< Can also be used as a |method|: >
myobj->instanceof(mytype)

View File

@ -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[]

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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()

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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 (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))
{
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 (class_instance_of(object_tv->vval.v_object->obj_class, c)
== TRUE)
{
rettv->vval.v_number = VVAL_TRUE;
return;
}
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