Compare commits

..

12 Commits

Author SHA1 Message Date
1d634542cf patch 8.2.1479: Vim9: error for list index uses wrong line number
Problem:    Vim9: error for list index uses wrong line number.
Solution:   Set source line number. (closes #6724)  Add a way to assert the
            line number of the error with assert_fails().
2020-08-18 13:41:50 +02:00
558813314d patch 8.2.1478: Vim9: cannot use "true" for some popup options
Problem:    Vim9: cannot use "true" for some popup options.
Solution:   Add dict_get_bool(). (closes #6725)
2020-08-18 13:04:15 +02:00
f39397e515 patch 8.2.1477: Vim9: error when using bufnr('%')
Problem:    Vim9: error when using bufnr('%').
Solution:   Don't give an error for using a string argument. (closes #6723)
2020-08-17 22:21:36 +02:00
aa9675a61d patch 8.2.1476: filetype test fails on MS-Windows
Problem:    Filetype test fails on MS-Windows.
Solution:   Remove "^" from pattern.
2020-08-17 21:57:09 +02:00
36967b32fd patch 8.2.1475: Vim9: can't use v:true for option flags
Problem:    Vim9: can't use v:true for option flags.
Solution:   Add tv_get_bool_chk(). (closes #6725)
2020-08-17 21:41:02 +02:00
624b6eaf20 patch 8.2.1474: /usr/lib/udef/rules.d not recognized as udevrules
Problem:    /usr/lib/udef/rules.d not recognized as udevrules.
Solution:   Adjust match pattern. (Haochen Tong, closes 36722)
2020-08-17 21:17:25 +02:00
021bda5671 patch 8.2.1473: items in a list given to :const can still be modified
Problem:    Items in a list given to :const can still be modified.
Solution:   Work like ":lockvar! name" but don't lock referenced items.
            Make locking a blob work.
2020-08-17 21:07:22 +02:00
7b22117c4e patch 8.2.1472: ":argdel" does not work like ":.argdel" as documented
Problem:    ":argdel" does not work like ":.argdel" as documented. (Alexey
            Demin)
Solution:   Make ":argdel" work like ":.argdel". (closes #6727)
            Also fix giving the error "0 more files to edit".
2020-08-17 19:34:10 +02:00
241572794f patch 8.2.1471: :const only locks the variable, not the value
Problem:    :const only locks the variable, not the value.
Solution:   Lock the value as ":lockvar 1 var" would do. (closes #6719)
2020-08-16 22:50:01 +02:00
c0f8823ee4 patch 8.2.1470: errors in spell file not tested
Problem:    Errors in spell file not tested.
Solution:   Add test for spell file errors. (Yegappan Lakshmanan,
            closes #6721)
2020-08-16 21:51:49 +02:00
0aae4809fd patch 8.2.1469: Vim9: cannot assign string to string option
Problem:    Vim9: cannot assign string to string option.
Solution:   Change checks for option value. (closes #6720)
2020-08-16 21:29:05 +02:00
f923571ec1 patch 8.2.1468: Vim9: invalid error for missing white space
Problem:    Vim9: invalid error for missing white space.
Solution:   Don't skip over white space after index. (closes #6718)
2020-08-16 18:42:53 +02:00
28 changed files with 473 additions and 123 deletions

View File

@ -575,7 +575,7 @@ endfunc
let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
func dist#ft#FTRules()
let path = expand('<amatch>:p')
if path =~ '^/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
setf udevrules
return
endif

View File

@ -12262,10 +12262,18 @@ text...
:const x = 1
< is equivalent to: >
:let x = 1
:lockvar 1 x
:lockvar! x
< This is useful if you want to make sure the variable
is not modified.
*E995*
is not modified. If the value is a List or Dictionary
literal then the items also cannot be changed: >
const ll = [1, 2, 3]
let ll[1] = 5 " Error!
< Nested references are not locked: >
let lvar = ['a']
const lconst = [0, lvar]
let lconst[0] = 2 " Error!
let lconst[1][0] = 'b' " OK
< *E995*
|:const| does not allow to for changing a variable: >
:let x = 1
:const x = 2 " Error!

View File

@ -291,8 +291,9 @@ assert_exception({error} [, {msg}]) *assert_exception()*
catch
call assert_exception('E492:')
endtry
assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
<
*assert_fails()*
assert_fails({cmd} [, {error} [, {msg} [, {lnum}]]])
Run {cmd} and add an error message to |v:errors| if it does
NOT produce an error or when {error} is not found in the
error message. Also see |assert-return|.
@ -311,13 +312,21 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
string for the first error: >
assert_fails('cmd', ['', 'E987:'])
<
If {msg} is empty then it is not used. Do this to get the
default message when passing the {lnum} argument.
When {lnum} is present and not negative, and the {error}
argument is present and matches, then this is compared with
the line number at which the error was reported. That can be
the line number in a function or in a script.
Note that beeping is not considered an error, and some failing
commands only beep. Use |assert_beeps()| for those.
Can also be used as a |method|: >
GetCmd()->assert_fails('E99:')
assert_false({actual} [, {msg}]) *assert_false()*
assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|.
Also see |assert-return|.

View File

@ -776,10 +776,20 @@ ex_argdelete(exarg_T *eap)
int i;
int n;
if (eap->addr_count > 0)
if (eap->addr_count > 0 || *eap->arg == NUL)
{
// ":1,4argdel": Delete all arguments in the range.
if (eap->line2 > ARGCOUNT)
// ":argdel" works like ":argdel"
if (eap->addr_count == 0)
{
if (curwin->w_arg_idx >= ARGCOUNT)
{
emsg(_("E610: No argument to delete"));
return;
}
eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
}
else if (eap->line2 > ARGCOUNT)
// ":1,4argdel": Delete all arguments in the range.
eap->line2 = ARGCOUNT;
n = eap->line2 - eap->line1 + 1;
if (*eap->arg != NUL)
@ -808,8 +818,6 @@ ex_argdelete(exarg_T *eap)
curwin->w_arg_idx = ARGCOUNT - 1;
}
}
else if (*eap->arg == NUL)
emsg(_(e_argreq));
else
do_arglist(eap->arg, AL_DEL, 0, FALSE);
#ifdef FEAT_TITLE

View File

@ -4922,7 +4922,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
{
if (!(supported & JO_MODE))
break;
opt->jo_noblock = tv_get_number(item);
opt->jo_noblock = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "in_io") == 0
|| STRCMP(hi->hi_key, "out_io") == 0
@ -4949,7 +4949,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
{
if (!(supported & JO_MODE))
break;
opt->jo_pty = tv_get_number(item);
opt->jo_pty = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "in_buf") == 0
|| STRCMP(hi->hi_key, "out_buf") == 0
@ -4980,7 +4980,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
if (!(supported & JO_OUT_IO))
break;
opt->jo_set |= JO_OUT_MODIFIABLE << (part - PART_OUT);
opt->jo_modifiable[part] = tv_get_number(item);
opt->jo_modifiable[part] = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "out_msg") == 0
|| STRCMP(hi->hi_key, "err_msg") == 0)
@ -4990,7 +4990,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
if (!(supported & JO_OUT_IO))
break;
opt->jo_set2 |= JO2_OUT_MSG << (part - PART_OUT);
opt->jo_message[part] = tv_get_number(item);
opt->jo_message[part] = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "in_top") == 0
|| STRCMP(hi->hi_key, "in_bot") == 0)
@ -5184,7 +5184,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
if (!(supported2 & JO2_VERTICAL))
break;
opt->jo_set2 |= JO2_VERTICAL;
opt->jo_vertical = tv_get_number(item);
opt->jo_vertical = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "curwin") == 0)
{
@ -5224,14 +5224,14 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
if (!(supported2 & JO2_HIDDEN))
break;
opt->jo_set2 |= JO2_HIDDEN;
opt->jo_hidden = tv_get_number(item);
opt->jo_hidden = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "norestore") == 0)
{
if (!(supported2 & JO2_NORESTORE))
break;
opt->jo_set2 |= JO2_NORESTORE;
opt->jo_term_norestore = tv_get_number(item);
opt->jo_term_norestore = tv_get_bool(item);
}
else if (STRCMP(hi->hi_key, "term_kill") == 0)
{

View File

@ -702,6 +702,21 @@ dict_get_number_check(dict_T *d, char_u *key)
return tv_get_number(&di->di_tv);
}
/*
* Get a bool item (number or true/false) from a dictionary.
* Returns "def" if the entry doesn't exist.
*/
varnumber_T
dict_get_bool(dict_T *d, char_u *key, int def)
{
dictitem_T *di;
di = dict_find(d, key, -1);
if (di == NULL)
return def;
return tv_get_bool(&di->di_tv);
}
/*
* Return an allocated string with the string representation of a Dictionary.
* May return NULL.

View File

@ -1218,6 +1218,8 @@ set_var_lval(
semsg(_(e_letwrong), op);
return;
}
if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE))
return;
if (lp->ll_range && rettv->v_type == VAR_BLOB)
{
@ -3624,7 +3626,7 @@ eval_index(
clear_tv(&var2);
return FAIL;
}
*arg = skipwhite(*arg + 1); // skip the ']'
*arg = *arg + 1; // skip over the ']'
}
if (evaluate)

View File

@ -390,7 +390,8 @@ f_bufnr(typval_T *argvars, typval_T *rettv)
buf = curbuf;
else
{
(void)tv_get_number(&argvars[0]); // issue errmsg if type error
if (argvars[0].v_type != VAR_STRING)
(void)tv_get_number(&argvars[0]); // issue errmsg if type error
++emsg_off;
buf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;

View File

@ -494,7 +494,7 @@ static funcentry_T global_functions[] =
{"assert_equal", 2, 3, FEARG_2, ret_number, f_assert_equal},
{"assert_equalfile", 2, 3, FEARG_1, ret_number, f_assert_equalfile},
{"assert_exception", 1, 2, 0, ret_number, f_assert_exception},
{"assert_fails", 1, 3, FEARG_1, ret_number, f_assert_fails},
{"assert_fails", 1, 4, FEARG_1, ret_number, f_assert_fails},
{"assert_false", 1, 2, FEARG_1, ret_number, f_assert_false},
{"assert_inrange", 3, 4, FEARG_3, ret_number, f_assert_inrange},
{"assert_match", 2, 3, FEARG_2, ret_number, f_assert_match},

View File

@ -173,7 +173,7 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
static void item_lock(typval_T *tv, int deep, int lock);
static void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
static void delete_var(hashtab_T *ht, hashitem_T *hi);
static void list_one_var(dictitem_T *v, char *prefix, int *first);
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);
@ -1294,28 +1294,36 @@ ex_let_one(
emsg(_(e_letunexp));
else
{
long n;
long n = 0;
int opt_type;
long numval;
char_u *stringval = NULL;
char_u *s = NULL;
int failed = FALSE;
c1 = *p;
*p = NUL;
n = (long)tv_get_number(tv);
// avoid setting a string option to the text "v:false" or similar.
if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL)
s = tv_get_string_chk(tv); // != NULL if number or string
if (s != NULL && op != NULL && *op != '=')
opt_type = get_option_value(arg, &numval, &stringval, opt_flags);
if ((opt_type == 1 || opt_type == -1)
&& (tv->v_type != VAR_STRING || !in_vim9script()))
// number, possibly hidden
n = (long)tv_get_number(tv);
// Avoid setting a string option to the text "v:false" or similar.
// In Vim9 script also don't convert a number to string.
if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL
&& (!in_vim9script() || tv->v_type != VAR_NUMBER))
s = tv_get_string_chk(tv);
if (op != NULL && *op != '=')
{
opt_type = get_option_value(arg, &numval,
&stringval, opt_flags);
if ((opt_type == 1 && *op == '.')
|| (opt_type == 0 && *op != '.'))
{
semsg(_(e_letwrong), op);
s = NULL; // don't set the value
failed = TRUE; // don't set the value
}
else
{
@ -1330,19 +1338,25 @@ ex_let_one(
case '%': n = (long)num_modulus(numval, n); break;
}
}
else if (opt_type == 0 && stringval != NULL) // string
else if (opt_type == 0 && stringval != NULL && s != NULL)
{
// string
s = concat_str(stringval, s);
vim_free(stringval);
stringval = s;
}
}
}
if (s != NULL || tv->v_type == VAR_BOOL
|| tv->v_type == VAR_SPECIAL)
if (!failed)
{
set_option_value(arg, n, s, opt_flags);
arg_end = p;
if (opt_type != 0 || s != NULL)
{
set_option_value(arg, n, s, opt_flags);
arg_end = p;
}
else
emsg(_(e_stringreq));
}
*p = c1;
vim_free(stringval);
@ -1689,7 +1703,7 @@ do_lock_var(
di->di_flags |= DI_FLAGS_LOCK;
else
di->di_flags &= ~DI_FLAGS_LOCK;
item_lock(&di->di_tv, deep, lock);
item_lock(&di->di_tv, deep, lock, FALSE);
}
}
*name_end = cc;
@ -1701,26 +1715,28 @@ do_lock_var(
// (un)lock a range of List items.
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
{
item_lock(&li->li_tv, deep, lock);
item_lock(&li->li_tv, deep, lock, FALSE);
li = li->li_next;
++lp->ll_n1;
}
}
else if (lp->ll_list != NULL)
// (un)lock a List item.
item_lock(&lp->ll_li->li_tv, deep, lock);
item_lock(&lp->ll_li->li_tv, deep, lock, FALSE);
else
// (un)lock a Dictionary item.
item_lock(&lp->ll_di->di_tv, deep, lock);
item_lock(&lp->ll_di->di_tv, deep, lock, FALSE);
return ret;
}
/*
* Lock or unlock an item. "deep" is nr of levels to go.
* When "check_refcount" is TRUE do not lock a list or dict with a reference
* count larger than 1.
*/
static void
item_lock(typval_T *tv, int deep, int lock)
item_lock(typval_T *tv, int deep, int lock, int check_refcount)
{
static int recurse = 0;
list_T *l;
@ -1762,7 +1778,8 @@ item_lock(typval_T *tv, int deep, int lock)
break;
case VAR_BLOB:
if ((b = tv->vval.v_blob) != NULL)
if ((b = tv->vval.v_blob) != NULL
&& !(check_refcount && b->bv_refcount > 1))
{
if (lock)
b->bv_lock |= VAR_LOCKED;
@ -1771,7 +1788,8 @@ item_lock(typval_T *tv, int deep, int lock)
}
break;
case VAR_LIST:
if ((l = tv->vval.v_list) != NULL)
if ((l = tv->vval.v_list) != NULL
&& !(check_refcount && l->lv_refcount > 1))
{
if (lock)
l->lv_lock |= VAR_LOCKED;
@ -1780,11 +1798,12 @@ item_lock(typval_T *tv, int deep, int lock)
if ((deep < 0 || deep > 1) && l->lv_first != &range_list_item)
// recursive: lock/unlock the items the List contains
FOR_ALL_LIST_ITEMS(l, li)
item_lock(&li->li_tv, deep - 1, lock);
item_lock(&li->li_tv, deep - 1, lock, check_refcount);
}
break;
case VAR_DICT:
if ((d = tv->vval.v_dict) != NULL)
if ((d = tv->vval.v_dict) != NULL
&& !(check_refcount && d->dv_refcount > 1))
{
if (lock)
d->dv_lock |= VAR_LOCKED;
@ -1799,7 +1818,8 @@ item_lock(typval_T *tv, int deep, int lock)
if (!HASHITEM_EMPTY(hi))
{
--todo;
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock,
check_refcount);
}
}
}
@ -3073,7 +3093,10 @@ set_var_const(
}
if (flags & LET_IS_CONST)
di->di_tv.v_lock |= VAR_LOCKED;
// Like :lockvar! name: lock the value and what it contains, but only
// if the reference count is up to one. That locks only literal
// values.
item_lock(&di->di_tv, DICT_MAXNEST, TRUE, TRUE);
}
/*

View File

@ -5022,7 +5022,7 @@ check_more(
int n = ARGCOUNT - curwin->w_arg_idx - 1;
if (!forceit && only_one_window()
&& ARGCOUNT > 1 && !arg_had_last && n >= 0 && quitmore == 0)
&& ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0)
{
if (message)
{

View File

@ -223,6 +223,7 @@ EXTERN int emsg_severe INIT(= FALSE); // use message of next of several
// used by assert_fails()
EXTERN int emsg_assert_fails_used INIT(= FALSE);
EXTERN char_u *emsg_assert_fails_msg INIT(= NULL);
EXTERN long emsg_assert_fails_lnum INIT(= 0);
EXTERN int did_endif INIT(= FALSE); // just had ":endif"
#endif

View File

@ -655,7 +655,10 @@ emsg_core(char_u *s)
}
if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL)
{
emsg_assert_fails_msg = vim_strsave(s);
emsg_assert_fails_lnum = SOURCING_LNUM;
}
// set "v:errmsg", also when using ":silent! cmd"
set_vim_var_string(VV_ERRMSG, s, -1);

View File

@ -438,9 +438,10 @@ apply_move_options(win_T *wp, dict_T *d)
if (nr != MAXCOL)
wp->w_wantcol = nr;
di = dict_find(d, (char_u *)"fixed", -1);
if (di != NULL)
wp->w_popup_fixed = dict_get_number(d, (char_u *)"fixed") != 0;
nr = dict_get_bool(d, (char_u *)"fixed", -1);
if (nr != -1)
wp->w_popup_fixed = nr != 0;
{
poppos_T ppt = get_pos_entry(d, TRUE);
@ -674,37 +675,31 @@ apply_general_options(win_T *wp, dict_T *dict)
wp->w_popup_title = vim_strsave(str);
}
di = dict_find(dict, (char_u *)"wrap", -1);
if (di != NULL)
{
nr = dict_get_number(dict, (char_u *)"wrap");
nr = dict_get_bool(dict, (char_u *)"wrap", -1);
if (nr != -1)
wp->w_p_wrap = nr != 0;
}
di = dict_find(dict, (char_u *)"drag", -1);
if (di != NULL)
nr = dict_get_bool(dict, (char_u *)"drag", -1);
if (nr != -1)
{
nr = dict_get_number(dict, (char_u *)"drag");
if (nr)
wp->w_popup_flags |= POPF_DRAG;
else
wp->w_popup_flags &= ~POPF_DRAG;
}
di = dict_find(dict, (char_u *)"posinvert", -1);
if (di != NULL)
nr = dict_get_bool(dict, (char_u *)"posinvert", -1);
if (nr != -1)
{
nr = dict_get_number(dict, (char_u *)"posinvert");
if (nr)
wp->w_popup_flags |= POPF_POSINVERT;
else
wp->w_popup_flags &= ~POPF_POSINVERT;
}
di = dict_find(dict, (char_u *)"resize", -1);
if (di != NULL)
nr = dict_get_bool(dict, (char_u *)"resize", -1);
if (nr != -1)
{
nr = dict_get_number(dict, (char_u *)"resize");
if (nr)
wp->w_popup_flags |= POPF_RESIZE;
else
@ -902,10 +897,9 @@ apply_general_options(win_T *wp, dict_T *dict)
set_callback(&wp->w_filter_cb, &callback);
}
}
di = dict_find(dict, (char_u *)"mapping", -1);
if (di != NULL)
nr = dict_get_bool(dict, (char_u *)"mapping", -1);
if (nr != -1)
{
nr = dict_get_number(dict, (char_u *)"mapping");
if (nr)
wp->w_popup_flags |= POPF_MAPPING;
else
@ -950,7 +944,7 @@ apply_options(win_T *wp, dict_T *dict)
apply_general_options(wp, dict);
nr = dict_get_number(dict, (char_u *)"hidden");
nr = dict_get_bool(dict, (char_u *)"hidden", FALSE);
if (nr > 0)
wp->w_popup_flags |= POPF_HIDDEN;

View File

@ -31,6 +31,7 @@ char_u *dict_get_string(dict_T *d, char_u *key, int save);
varnumber_T dict_get_number(dict_T *d, char_u *key);
varnumber_T dict_get_number_def(dict_T *d, char_u *key, int def);
varnumber_T dict_get_number_check(dict_T *d, char_u *key);
varnumber_T dict_get_bool(dict_T *d, char_u *key, int def);
char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action);

View File

@ -6,6 +6,7 @@ void clear_tv(typval_T *varp);
void init_tv(typval_T *varp);
varnumber_T tv_get_number(typval_T *varp);
varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
varnumber_T tv_get_bool(typval_T *varp);
float_T tv_get_float(typval_T *varp);
char_u *tv_get_string(typval_T *varp);
char_u *tv_get_string_buf(typval_T *varp, char_u *buf);

View File

@ -423,9 +423,15 @@ func Test_argdelete()
last
argdelete %
call assert_equal(['b'], argv())
call assert_fails('argdelete', 'E471:')
call assert_fails('argdelete', 'E610:')
call assert_fails('1,100argdelete', 'E16:')
%argd
call Reset_arglist()
args a b c d
next
argdel
call Assert_argc(['a', 'c', 'd'])
%argdel
endfunc
func Test_argdelete_completion()

View File

@ -41,6 +41,7 @@ func Test_define_var_with_lock()
call assert_fails('let s = "vim"', 'E741:')
call assert_fails('let F = funcref("s:noop")', 'E741:')
call assert_fails('let l = [1, 2, 3]', 'E741:')
call assert_fails('call filter(l, "v:val % 2 == 0")', 'E741:')
call assert_fails('let d = {"foo": 10}', 'E741:')
if has('channel')
call assert_fails('let j = test_null_job()', 'E741:')
@ -272,17 +273,35 @@ func Test_const_with_eval_name()
call assert_fails('const {s2} = "bar"', 'E995:')
endfunc
func Test_lock_depth_is_1()
const l = [1, 2, 3]
func Test_lock_depth_is_2()
" Modify list - error when changing item or adding/removing items
const l = [1, 2, [3, 4]]
call assert_fails('let l[0] = 42', 'E741:')
call assert_fails('let l[2][0] = 42', 'E741:')
call assert_fails('call add(l, 4)', 'E741:')
call assert_fails('unlet l[1]', 'E741:')
" Modify blob - error when changing
const b = 0z001122
call assert_fails('let b[0] = 42', 'E741:')
" Modify dict - error when changing item or adding/removing items
const d = {'foo': 10}
call assert_fails("let d['foo'] = 'hello'", 'E741:')
call assert_fails("let d.foo = 'hello'", 'E741:')
call assert_fails("let d['bar'] = 'hello'", 'E741:')
call assert_fails("unlet d['foo']", 'E741:')
" Modify list
call add(l, 4)
let l[0] = 42
" Modifying list or dict item contents is OK.
let lvar = ['a', 'b']
let bvar = 0z1122
const l2 = [0, lvar, bvar]
let l2[1][0] = 'c'
let l2[2][1] = 0x33
call assert_equal([0, ['c', 'b'], 0z1133], l2)
" Modify dict
let d['bar'] = 'hello'
let d.foo = 44
const d2 = #{a: 0, b: lvar, c: 4}
let d2.b[1] = 'd'
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -472,6 +472,7 @@ let s:filename_checks = {
\ 'uc': ['file.uc'],
\ 'udevconf': ['/etc/udev/udev.conf'],
\ 'udevperm': ['/etc/udev/permissions.d/file.permissions'],
\ 'udevrules': ['/etc/udev/rules.d/file.rules', '/usr/lib/udev/rules.d/file.rules', '/lib/udev/rules.d/file.rules'],
\ 'uil': ['file.uit', 'file.uil'],
\ 'updatedb': ['/etc/updatedb.conf'],
\ 'upstart': ['/usr/share/upstart/file.conf', '/usr/share/upstart/file.override', '/etc/init/file.conf', '/etc/init/file.override', '/.init/file.conf', '/.init/file.override', '/.config/upstart/file.conf', '/.config/upstart/file.override'],

View File

@ -167,8 +167,173 @@ func Test_spell_normal()
call assert_equal([], glob('Xspellfile.add',0,1))
call assert_equal([], glob('Xspellfile2.add',0,1))
set spellfile=
set spellfile= spell& spelllang&
bw!
endfunc
" Test for spell file format errors
func Test_spellfile_format_error()
let save_rtp = &rtp
call mkdir('Xtest/spell', 'p')
" empty spell file
call writefile([], './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E757:')
set nospell spelllang&
" invalid file ID
call writefile(['vim'], './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E757:')
set nospell spelllang&
" missing version number
call writefile(['VIMspell'], './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E771:')
set nospell spelllang&
" invalid version number
call writefile(['VIMspellz'], './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E772:')
set nospell spelllang&
" no sections
call writefile(0z56494D7370656C6C32, './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" missing section length
call writefile(['VIMspell200'], './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" unsupported required section
call writefile(['VIMspell2z' .. nr2char(1) .. ' ' .. nr2char(4)],
\ './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E770:')
set nospell spelllang&
" unsupported not-required section
call writefile(['VIMspell2z' .. nr2char(0) .. ' ' .. nr2char(4)],
\ './Xtest/spell/Xtest.utf-8.spl')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" SN_REGION: invalid number of region names
call writefile(0z56494D7370656C6C320000000000FF,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
" SN_CHARFLAGS: missing <charflagslen> length
call writefile(0z56494D7370656C6C32010000000004,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" SN_CHARFLAGS: invalid <charflagslen> length
call writefile(0z56494D7370656C6C320100000000010201,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
set spell
" FIXME: There are no error messages. How to check for the test result?
set nospell spelllang&
" SN_CHARFLAGS: charflagslen == 0 and folcharslen != 0
call writefile(0z56494D7370656C6C3201000000000400000101,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
" SN_CHARFLAGS: missing <folcharslen> length
call writefile(0z56494D7370656C6C3201000000000100,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" SN_PREFCOND: invalid prefcondcnt
call writefile(0z56494D7370656C6C3203000000000100,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
" SN_PREFCOND: invalid condlen
call writefile(0z56494D7370656C6C320300000000020001,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
" SN_REP: invalid repcount
call writefile(0z56494D7370656C6C3204000000000100,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" SN_REP: missing rep
call writefile(0z56494D7370656C6C320400000000020004,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E758:')
set nospell spelllang&
" SN_REP: zero repfromlen
call writefile(0z56494D7370656C6C32040000000003000100,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
" SN_REP: invalid reptolen
call writefile(0z56494D7370656C6C320400000000050001014101,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
" FIXME: There are no error messages. How to check for the test result?
set spell
set nospell spelllang&
" SN_REP: zero reptolen
call writefile(0z56494D7370656C6C320400000000050001014100,
\ './Xtest/spell/Xtest.utf-8.spl', 'b')
set runtimepath=./Xtest
set spelllang=Xtest
call assert_fails('set spell', 'E759:')
set nospell spelllang&
let &rtp = save_rtp
call delete('Xtest', 'rf')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -827,6 +827,13 @@ def Test_expr4_vim9script()
echo len('xxx') == 3
END
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
let line = 'abc'
echo line[1] =~ '\w'
END
CheckScriptSuccess(lines)
enddef
func Test_expr4_fails()
@ -1457,16 +1464,18 @@ def Test_expr7_list()
4]
call CheckDefFailure(["let x = 1234[3]"], 'E1107:')
call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:')
call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:', 1)
call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:')
call CheckDefFailure(["let x = [1,2,3]"], 'E1069:')
call CheckDefFailure(["let x = [1 ,2, 3]"], 'E1068:')
call CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:')
call CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2)
call CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1)
call CheckDefFailure(["let x = g:list_mixed["], 'E1097:')
call CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:')
call CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 1)
call CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:')
call CheckDefFailure(["let l: list<number> = [234, 'x']"], 'E1012:')
call CheckDefFailure(["let l: list<number> = ['x', 234]"], 'E1012:')

View File

@ -1315,6 +1315,11 @@ def Test_filter_return_type()
assert_equal(6, res)
enddef
def Test_bufnr()
let buf = bufnr()
assert_equal(buf, bufnr('%'))
enddef
def Test_getreg_return_type()
let s1: string = getreg('"')
let s2: string = getreg('"', 1)

View File

@ -96,22 +96,36 @@ def Test_assignment()
&ts += 3
assert_equal(9, &ts)
END
call CheckScriptSuccess(lines)
CheckScriptSuccess(lines)
call CheckDefFailure(['&notex += 3'], 'E113:')
call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
call CheckDefFailure(['&ts = [7]'], 'E1012:')
call CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list')
call CheckDefFailure(['&ts = "xx"'], 'E1012:')
call CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string')
call CheckDefFailure(['&path += 3'], 'E1012:')
call CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
CheckDefFailure(['&notex += 3'], 'E113:')
CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
CheckDefFailure(['&ts = [7]'], 'E1012:')
CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list')
CheckDefFailure(['&ts = "xx"'], 'E1012:')
CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string')
CheckDefFailure(['&path += 3'], 'E1012:')
CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
# test freeing ISN_STOREOPT
call CheckDefFailure(['&ts = 3', 'let asdf'], 'E1022:')
CheckDefFailure(['&ts = 3', 'let asdf'], 'E1022:')
&ts = 8
call CheckDefFailure(['let s:var = 123'], 'E1101:')
call CheckDefFailure(['let s:var: number'], 'E1101:')
lines =<< trim END
let save_TI = &t_TI
&t_TI = ''
assert_equal('', &t_TI)
&t_TI = 'xxx'
assert_equal('xxx', &t_TI)
&t_TI = save_TI
END
CheckDefSuccess(lines)
CheckScriptSuccess(['vim9script'] + lines)
CheckDefFailure(['&t_TI = 123'], 'E1012:')
CheckScriptFailure(['vim9script', '&t_TI = 123'], 'E928:')
CheckDefFailure(['let s:var = 123'], 'E1101:')
CheckDefFailure(['let s:var: number'], 'E1101:')
lines =<< trim END
vim9script

View File

@ -9,17 +9,19 @@ func CheckDefSuccess(lines)
endfunc
" Check that "lines" inside ":def" results in an "error" message.
func CheckDefFailure(lines, error)
" If "lnum" is given check that the error is reported for this line.
func CheckDefFailure(lines, error, lnum = -1)
call writefile(['def Func()'] + a:lines + ['enddef', 'defcompile'], 'Xdef')
call assert_fails('so Xdef', a:error, a:lines)
call assert_fails('so Xdef', a:error, a:lines, a:lnum)
call delete('Xdef')
endfunc
" Check that "lines" inside ":def" results in an "error" message when executed.
func CheckDefExecFailure(lines, error)
" If "lnum" is given check that the error is reported for this line.
func CheckDefExecFailure(lines, error, lnum = -1)
call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef')
so Xdef
call assert_fails('call Func()', a:error, a:lines)
call assert_fails('call Func()', a:error, a:lines, a:lnum)
call delete('Xdef')
endfunc

View File

@ -142,7 +142,10 @@ fill_assert_error(
int did_copy = FALSE;
int omitted = 0;
if (opt_msg_tv->v_type != VAR_UNKNOWN)
if (opt_msg_tv->v_type != VAR_UNKNOWN
&& !(opt_msg_tv->v_type == VAR_STRING
&& (opt_msg_tv->vval.v_string == NULL
|| *opt_msg_tv->vval.v_string == NUL)))
{
ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0));
vim_free(tofree);
@ -570,6 +573,7 @@ f_assert_fails(typval_T *argvars, typval_T *rettv)
char_u buf[NUMBUFLEN];
char_u *expected;
int error_found = FALSE;
int lnum_error_found = FALSE;
char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
: emsg_assert_fails_msg;
@ -611,14 +615,31 @@ f_assert_fails(typval_T *argvars, typval_T *rettv)
goto theend;
}
if (!error_found && argvars[3].v_type == VAR_NUMBER
&& argvars[3].vval.v_number >= 0
&& argvars[3].vval.v_number != emsg_assert_fails_lnum)
{
error_found = TRUE;
lnum_error_found = TRUE;
}
if (error_found)
{
typval_T actual_tv;
prepare_assert_error(&ga);
actual_tv.v_type = VAR_STRING;
actual_tv.vval.v_string = actual;
fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
if (lnum_error_found)
{
actual_tv.v_type = VAR_NUMBER;
actual_tv.vval.v_number = emsg_assert_fails_lnum;
}
else
{
actual_tv.v_type = VAR_STRING;
actual_tv.vval.v_string = actual;
}
fill_assert_error(&ga, &argvars[2], NULL,
&argvars[lnum_error_found ? 3 : 1],
&actual_tv, ASSERT_OTHER);
ga_concat(&ga, (char_u *)": ");
assert_append_cmd_or_arg(&ga, argvars, cmd);

View File

@ -169,24 +169,8 @@ init_tv(typval_T *varp)
CLEAR_POINTER(varp);
}
/*
* Get the number value of a variable.
* If it is a String variable, uses vim_str2nr().
* For incompatible types, return 0.
* tv_get_number_chk() is similar to tv_get_number(), but informs the
* caller of incompatible types: it sets *denote to TRUE if "denote"
* is not NULL or returns -1 otherwise.
*/
varnumber_T
tv_get_number(typval_T *varp)
{
int error = FALSE;
return tv_get_number_chk(varp, &error); // return 0L on error
}
varnumber_T
tv_get_number_chk(typval_T *varp, int *denote)
static varnumber_T
tv_get_bool_or_number_chk(typval_T *varp, int *denote, int want_bool)
{
varnumber_T n = 0L;
@ -221,7 +205,7 @@ tv_get_number_chk(typval_T *varp, int *denote)
break;
case VAR_BOOL:
case VAR_SPECIAL:
if (in_vim9script())
if (!want_bool && in_vim9script())
{
emsg(_("E611: Using a Special as a Number"));
break;
@ -253,6 +237,39 @@ tv_get_number_chk(typval_T *varp, int *denote)
return n;
}
/*
* Get the number value of a variable.
* If it is a String variable, uses vim_str2nr().
* For incompatible types, return 0.
* tv_get_number_chk() is similar to tv_get_number(), but informs the
* caller of incompatible types: it sets *denote to TRUE if "denote"
* is not NULL or returns -1 otherwise.
*/
varnumber_T
tv_get_number(typval_T *varp)
{
int error = FALSE;
return tv_get_number_chk(varp, &error); // return 0L on error
}
varnumber_T
tv_get_number_chk(typval_T *varp, int *denote)
{
return tv_get_bool_or_number_chk(varp, denote, FALSE);
}
/*
* Get the boolean value of "varp". This is like tv_get_number_chk(),
* but in Vim9 script accepts Number and Bool.
*/
varnumber_T
tv_get_bool(typval_T *varp)
{
return tv_get_bool_or_number_chk(varp, NULL, TRUE);
}
#ifdef FEAT_FLOAT
float_T
tv_get_float(typval_T *varp)

View File

@ -754,6 +754,30 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1479,
/**/
1478,
/**/
1477,
/**/
1476,
/**/
1475,
/**/
1474,
/**/
1473,
/**/
1472,
/**/
1471,
/**/
1470,
/**/
1469,
/**/
1468,
/**/
1467,
/**/

View File

@ -2299,6 +2299,7 @@ call_def_function(
ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
tv = STACK_TV_BOT(-1);
SOURCING_LNUM = iptr->isn_lnum;
if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE)
== FAIL)
goto on_error;