char *f_name; // function name
char f_min_argc; // minimal number of arguments
char f_max_argc; // maximal number of arguments
- char f_argtype; // for method: FEARG_ values
+ char f_argtype; // for method: FEARG_ values; bits FE_
argcheck_T *f_argcheck; // list of functions to check argument types;
// use "arg_any" (not NULL) to accept an
// argument of any type
#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
-#define FEARG_3 3 // base is the third argument
-#define FEARG_4 4 // base is the fourth argument
+#define FEARG_1 0x01 // base is the first argument
+#define FEARG_2 0x02 // base is the second argument
+#define FEARG_3 0x03 // base is the third argument
+#define FEARG_4 0x04 // base is the fourth argument
+#define FEARG_MASK 0x0F // bits in f_argtype used as argument index
+#define FE_X 0x10 // builtin accepts a non-value (class, typealias)
#if defined(HAVE_MATH_H)
# define MATH_FUNC(name) name
ret_string, f_inputsecret},
{"insert", 2, 3, FEARG_1, arg23_insert,
ret_first_arg, f_insert},
- {"instanceof", 2, VARGS, FEARG_1, arg2_instanceof,
+ {"instanceof", 2, VARGS, FEARG_1|FE_X, arg2_instanceof,
ret_bool, f_instanceof},
{"interrupt", 0, 0, 0, NULL,
ret_void, f_interrupt},
ret_number, f_strgetchar},
{"stridx", 2, 3, FEARG_1, arg3_string_string_number,
ret_number, f_stridx},
- {"string", 1, 1, FEARG_1, NULL,
+ {"string", 1, 1, FEARG_1|FE_X, NULL,
ret_string, f_string},
{"strlen", 1, 1, FEARG_1, arg1_string_or_nr,
ret_number, f_strlen},
ret_void, f_test_option_not_set},
{"test_override", 2, 2, FEARG_2, arg2_string_number,
ret_void, f_test_override},
- {"test_refcount", 1, 1, FEARG_1, NULL,
+ {"test_refcount", 1, 1, FEARG_1|FE_X, NULL,
ret_number, f_test_refcount},
{"test_setmouse", 2, 2, 0, arg2_number,
ret_void, f_test_setmouse},
ret_string, f_trim},
{"trunc", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_trunc},
- {"type", 1, 1, FEARG_1, NULL,
+ {"type", 1, 1, FEARG_1|FE_X, NULL,
ret_number, f_type},
- {"typename", 1, 1, FEARG_1, NULL,
+ {"typename", 1, 1, FEARG_1|FE_X, NULL,
ret_string, f_typename},
{"undofile", 1, 1, FEARG_1, arg1_string,
ret_string, f_undofile},
ret_number, f_xor},
};
+/*
+ * Return true if specified function allows a type as an argument.
+ */
+ static int
+func_allows_type(int idx)
+{
+ return (global_functions[idx].f_argtype & FE_X) != 0;
+}
+
/*
* Function given to ExpandGeneric() to obtain the list of internal
* or user defined function names.
int argcount,
cctx_T *cctx)
{
+ // Some internal functions accept types like Class as arguments. For other
+ // functions, check the arguments are not types.
+ if (!(func_allows_type(idx)))
+ {
+ for (int i = 0; i < argcount; ++i)
+ if (check_type_is_value(types[i].type_curr) == FAIL)
+ return FAIL;
+ }
+
argcheck_T *argchecks = global_functions[idx].f_argcheck;
if (argchecks == NULL)
else if (argcount > global_functions[idx].f_max_argc)
res = FCERR_TOOMANY;
else
- return global_functions[idx].f_argtype;
+ return global_functions[idx].f_argtype & FEARG_MASK;
name = internal_func_name(idx);
if (res == FCERR_TOOMANY)
return -1;
}
+/*
+ * Some internal functions accept types like Class as arguments. For other
+ * functions, check the arguments are not types.
+ *
+ * Return OK/FAIL.
+ */
+ static int
+check_args_for_type(int idx, int argcount, typval_T *argvars)
+{
+ if (!func_allows_type(idx))
+ {
+ for (int i = 0; i < argcount; ++i)
+ if (check_typval_is_value(&argvars[i]) == FAIL)
+ return FAIL;
+ }
+ return OK;
+}
+
funcerror_T
call_internal_func(
char_u *name,
return FCERR_TOOFEW;
if (argcount > global_functions[i].f_max_argc)
return FCERR_TOOMANY;
+ if (check_args_for_type(i, argcount, argvars) == FAIL)
+ return FCERR_OTHER;
argvars[argcount].v_type = VAR_UNKNOWN;
global_functions[i].f_func(argvars, rettv);
return FCERR_NONE;
fi = find_internal_func(name);
if (fi < 0)
return FCERR_UNKNOWN;
- if (global_functions[fi].f_argtype == 0)
+ if ((global_functions[fi].f_argtype & FEARG_MASK) == 0)
return FCERR_NOTMETHOD;
if (argcount + 1 < global_functions[fi].f_min_argc)
return FCERR_TOOFEW;
if (argcount + 1 > global_functions[fi].f_max_argc)
return FCERR_TOOMANY;
+ if (check_args_for_type(fi, argcount, argvars) == FAIL)
+ return FCERR_OTHER;
- if (global_functions[fi].f_argtype == FEARG_2)
+ if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_2)
{
if (argcount < 1)
return FCERR_TOOFEW;
for (int i = 1; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
- else if (global_functions[fi].f_argtype == FEARG_3)
+ else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_3)
{
if (argcount < 2)
return FCERR_TOOFEW;
for (int i = 2; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
- else if (global_functions[fi].f_argtype == FEARG_4)
+ else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_4)
{
if (argcount < 3)
return FCERR_TOOFEW;
}
argv[argcount + 1].v_type = VAR_UNKNOWN;
+ if (check_args_for_type(fi, argcount + 1, argv) == FAIL)
+ return FCERR_OTHER;
+
global_functions[fi].f_func(argv, rettv);
return FCERR_NONE;
}
v9.CheckDefExecAndScriptFailure(['writefile(["a"], "")'], 'E482: Can''t create file <empty>')
enddef
+def Test_passing_type_to_builtin()
+ # type, typename, string, instanceof are allowed type argument
+ var lines =<< trim END
+ vim9script
+ class C
+ endclass
+ type T = number
+ type U = C
+ var x: any
+ x = type(C)
+ x = type(T)
+ x = typename(C)
+ x = typename(T)
+ x = string(C)
+ x = string(T)
+ x = instanceof(C.new(), U, C)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # check argument to add at script level
+ # Note: add() is special cased in compile_call in vim9expr
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ add([], C)
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check argument to add in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ add([], C)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to add at script level
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ []->add(C)
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to add in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ []->add(C)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # Try "empty()" builtin
+ # check argument to empty at script level
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ empty(C)
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check argument to empty in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ empty(C)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to empty at script level
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ C->empty()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to empty in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ C->empty()
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # Try "abs()" builtin
+ # check argument to abs at script level
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ abs(C)
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check argument to abs in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ abs(C)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to abs at script level
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ C->abs()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # check member call argument to abs in :def
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def F()
+ C->abs()
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
type A = list<string>
var x = json_encode(A)
END
- v9.CheckSourceFailure(lines, 'E1161: Cannot json encode a typealias', 3)
+ v9.CheckSourceFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 3)
# Comparing type alias with a number (script level)
lines =<< trim END
var lines =<< trim END
vim9script
type A = list<func>
- assert_equal(0, empty(A))
+ var x = empty(A)
END
- v9.CheckScriptSuccess(lines)
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 3)
# Using a type alias with len()
lines =<< trim END
type A = list<func>
var x = len(A)
END
- v9.CheckScriptFailure(lines, 'E701: Invalid type for len()', 3)
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 3)
# Using a type alias with len()
lines =<< trim END
enddef
Foo()
END
- v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected list<any> but got typealias', 1)
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
# Using a type alias with eval()
lines =<< trim END
v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
enddef
+def Test_passing_typealias_to_builtin()
+ # type, typename, string, instanceof are allowed type argument
+ var lines =<< trim END
+ vim9script
+ type T = number
+ var x: any
+ x = type(T)
+ x = typename(T)
+ x = string(T)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # check argument to add at script level
+ # Note: add() is special cased in compile_call in vim9expr
+ lines =<< trim END
+ vim9script
+ type T = number
+ add([], T)
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check argument to add in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ add([], T)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+
+ # check member call argument to add at script level
+ lines =<< trim END
+ vim9script
+ type T = number
+ []->add(T)
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check member call argument to add in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ []->add(T)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+
+ # Try "empty()" builtin
+ # check argument to empty at script level
+ lines =<< trim END
+ vim9script
+ type T = number
+ empty(T)
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check argument to empty in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ empty(T)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+
+ # check member call argument to empty at script level
+ lines =<< trim END
+ vim9script
+ type T = number
+ T->empty()
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check member call argument to empty in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ T->empty()
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+
+ # Try "abs()" builtin
+ # check argument to abs at script level
+ lines =<< trim END
+ vim9script
+ type T = number
+ abs(T)
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check argument to abs in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ abs(T)
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+
+ # check member call argument to abs at script level
+ lines =<< trim END
+ vim9script
+ type T = number
+ T->abs()
+ END
+ v9.CheckScriptFailure(lines, 'E1403: Type alias "T" cannot be used as a value')
+
+ # check member call argument to abs in :def
+ lines =<< trim END
+ vim9script
+ type T = number
+ def F()
+ T->abs()
+ enddef
+ F()
+ END
+ v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker