Compare commits

...

10 Commits

Author SHA1 Message Date
Bram Moolenaar
2995e5cf4e patch 8.2.4590: Vim9: range type check has wrong offset
Problem:    Vim9: range type check has wrong offset.
Solution:   Adjust offset for CHECKTYPE.  Remove other type check.
2022-03-18 21:41:47 +00:00
Bram Moolenaar
2e17fef225 patch 8.2.4589: cannot index the g: dictionary
Problem:    Cannot index the g: dictionary.
Solution:   Recognize using "g:[key]". (closes #9969)
2022-03-18 19:44:48 +00:00
Bram Moolenaar
f35fd8e5d4 patch 8.2.4588: mapping with key after other matching mapping does not work
Problem:    Mapping with key code after other matching mapping does not work.
Solution:   Change ">" to ">=". (closes #9903)
2022-03-18 15:41:17 +00:00
Bram Moolenaar
61efa16932 patch 8.2.4587: Vim9: double free after unpacking a list
Problem:    Vim9: double free after unpacking a list.
Solution:   Make a copy of the value instead of moving it. (closes #9968)
2022-03-18 13:10:48 +00:00
Bram Moolenaar
1d9cef769d patch 8.2.4586: Vim9: no error for using lower case name for "func" argument
Problem:    Vim9: no error for using lower case name for "func" argument.
            (Ernie Rael)
Solution:   Check the name as soon as the type is known.
2022-03-17 16:30:03 +00:00
Yegappan Lakshmanan
155b088208 patch 8.2.4585: cannot use keypad page-up/down for completion menu
Problem:    Cannot use keypad page-up/down for completion menu.
Solution:   Recognize the keypad keys. (Yegappan Lakshmanan, closes #9963)
2022-03-17 13:03:09 +00:00
Bram Moolenaar
da6d42c35a patch 8.2.4584: error for using autoload function in custom completion
Problem:    Error for using autoload function in custom completion.
Solution:   Do not check for errors when using an autoload function.
            (closes #9962)
2022-03-17 11:46:55 +00:00
Bram Moolenaar
4f6e772c9c patch 8.2.4583: screendump test fails
Problem:    Screendump test fails.
Solution:   Check that making a screendump is possible.
2022-03-16 20:26:02 +00:00
Bram Moolenaar
4c8b546da2 patch 8.2.4582: useless code handling a type declaration
Problem:    Useless code handling a type declaration.
Solution:   Remove the code and give an error.
2022-03-16 20:01:39 +00:00
Bram Moolenaar
d597ab00d7 patch 8.2.4581: null types not fully tested
Problem:    Null types not fully tested.
Solution:   Add some more tests using null types.
2022-03-16 17:56:33 +00:00
16 changed files with 223 additions and 121 deletions

View File

@@ -3252,4 +3252,6 @@ EXTERN char e_cannot_use_s_backslash_in_vim9_script[]
#ifdef FEAT_EVAL
EXTERN char e_compiling_closure_without_context_str[]
INIT(= N_("E1271: compiling closure without context: %s"));
EXTERN char e_using_type_not_in_script_context_str[]
INIT(= N_("E1272: Using type not in a script context: %s"));
#endif

View File

@@ -705,10 +705,9 @@ call_vim_function(
// The name might be "import.Func" or "Funcref". We don't know, we need to
// ignore errors for an undefined name. But we do want errors when an
// autoload script has errors. Guess that when there is a dot or '#' in
// the name showing errors is the right choice.
ignore_errors = vim_strchr(func, '.') == NULL
&& vim_strchr(func, AUTOLOAD_CHAR) == NULL;
// autoload script has errors. Guess that when there is a dot in the name
// showing errors is the right choice.
ignore_errors = vim_strchr(func, '.') == NULL;
arg = func;
if (ignore_errors)
++emsg_off;
@@ -930,15 +929,14 @@ get_lval(
if (vim9script)
{
// "a: type" is declaring variable "a" with a type, not "a:".
if (p == name + 2 && p[-1] == ':')
// However, "g:[key]" is indexing a dictionary.
if (p == name + 2 && p[-1] == ':' && *p != '[')
{
--p;
lp->ll_name_end = p;
}
if (*p == ':')
{
garray_T tmp_type_list;
garray_T *type_list;
char_u *tp = skipwhite(p + 1);
if (tp == p + 1 && !quiet)
@@ -947,27 +945,19 @@ get_lval(
return NULL;
}
if (SCRIPT_ID_VALID(current_sctx.sc_sid))
type_list = &SCRIPT_ITEM(current_sctx.sc_sid)->sn_type_list;
else
if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
{
// TODO: should we give an error here?
type_list = &tmp_type_list;
ga_init2(type_list, sizeof(type_T), 10);
semsg(_(e_using_type_not_in_script_context_str), p);
return NULL;
}
// parse the type after the name
lp->ll_type = parse_type(&tp, type_list, !quiet);
lp->ll_type = parse_type(&tp,
&SCRIPT_ITEM(current_sctx.sc_sid)->sn_type_list,
!quiet);
if (lp->ll_type == NULL && !quiet)
return NULL;
lp->ll_name_end = tp;
// drop the type when not in a script
if (type_list == &tmp_type_list)
{
lp->ll_type = NULL;
clear_type_list(type_list);
}
}
}
}

View File

@@ -3523,12 +3523,14 @@ find_ex_command(
return eap->cmd;
}
if (p != eap->cmd && (
if ((p != eap->cmd && (
// "varname[]" is an expression.
*p == '['
// "varname.key" is an expression.
|| (*p == '.' && (ASCII_ISALPHA(p[1])
|| p[1] == '_'))))
|| (*p == '.'
&& (ASCII_ISALPHA(p[1]) || p[1] == '_'))))
// g:[key] is an expression
|| STRNCMP(eap->cmd, "g:[", 3) == 0)
{
char_u *after = eap->cmd;

View File

@@ -1606,6 +1606,7 @@ getcmdline_int(
cmdline_info_T save_ccline;
int did_save_ccline = FALSE;
int cmdline_type;
int wild_type;
if (ccline.cmdbuff != NULL)
{
@@ -1867,10 +1868,7 @@ getcmdline_int(
// text.
if (c == Ctrl_E || c == Ctrl_Y)
{
int wild_type;
wild_type = (c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY;
if (nextwild(&xpc, wild_type, WILD_NO_BEEP,
firstc != '@') == FAIL)
break;
@@ -2304,8 +2302,8 @@ getcmdline_int(
case Ctrl_P: // previous match
if (xpc.xp_numfiles > 0)
{
if (nextwild(&xpc, (c == Ctrl_P) ? WILD_PREV : WILD_NEXT,
0, firstc != '@') == FAIL)
wild_type = (c == Ctrl_P) ? WILD_PREV : WILD_NEXT;
if (nextwild(&xpc, wild_type, 0, firstc != '@') == FAIL)
break;
goto cmdline_not_changed;
}
@@ -2325,9 +2323,10 @@ getcmdline_int(
{
// If the popup menu is displayed, then PageUp and PageDown
// are used to scroll the menu.
if (nextwild(&xpc,
(c == K_PAGEUP) ? WILD_PAGEUP : WILD_PAGEDOWN,
0, firstc != '@') == FAIL)
wild_type = WILD_PAGEUP;
if (c == K_PAGEDOWN || c == K_KPAGEDOWN)
wild_type = WILD_PAGEDOWN;
if (nextwild(&xpc, wild_type, 0, firstc != '@') == FAIL)
break;
goto cmdline_not_changed;
}

View File

@@ -2416,6 +2416,7 @@ handle_mapping(
mapblock_T *mp_match;
int mp_match_len = 0;
int max_mlen = 0;
int want_termcode = 0; // 1 if termcode expected after max_mlen
int tb_c1;
int mlen;
#ifdef FEAT_LANGMAP
@@ -2591,9 +2592,16 @@ handle_mapping(
}
else
// No match; may have to check for termcode at next
// character.
// character. If the first character that didn't match is
// K_SPECIAL then check for a termcode. This isn't perfect
// but should work in most cases.
if (max_mlen < mlen)
{
max_mlen = mlen;
want_termcode = mp->m_keys[mlen] == K_SPECIAL;
}
else if (max_mlen == mlen && mp->m_keys[mlen] == K_SPECIAL)
want_termcode = 1;
}
}
@@ -2646,7 +2654,7 @@ handle_mapping(
// May check for a terminal code when there is no mapping or only a partial
// mapping. Also check if there is a full mapping with <Esc>, unless timed
// out, since that is nearly always a partial match with a terminal code.
if ((mp == NULL || max_mlen > mp_match_len
if ((mp == NULL || max_mlen + want_termcode > mp_match_len
|| (mp_match_len == 1 && *mp->m_keys == ESC && !*timedout))
&& keylen != KEYLEN_PART_MAP)
{

View File

@@ -0,0 +1,6 @@
|~+0#4040ff13#ffffff0| @73
|~| @73
|~| @73
|~| @73
|E+0#ffffff16#e000002|1|2|7|2|:| |U|s|i|n|g| |t|y|p|e| |n|o|t| |i|n| |a| |s|c|r|i|p|t| |c|o|n|t|e|x|t|:| |:| |s|t|r|i|n|g| +0#0000000#ffffff0@23
|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35

View File

@@ -1829,6 +1829,29 @@ func Test_wildmode()
let &encoding = save_encoding
endfunc
func Test_custom_complete_autoload()
call mkdir('Xdir/autoload', 'p')
let save_rtp = &rtp
exe 'set rtp=' .. getcwd() .. '/Xdir'
let lines =<< trim END
func vim8#Complete(a, c, p)
return "oneA\noneB\noneC"
endfunc
END
call writefile(lines, 'Xdir/autoload/vim8.vim')
command -nargs=1 -complete=custom,vim8#Complete MyCmd
set nowildmenu
set wildmode=full,list
call feedkeys(":MyCmd \<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal('"MyCmd oneA oneB oneC', @:)
let &rtp = save_rtp
set wildmode& wildmenu&
delcommand MyCmd
call delete('Xdir', 'rf')
endfunc
" Test for interrupting the command-line completion
func Test_interrupt_compl()
func F(lead, cmdl, p)
@@ -2083,7 +2106,8 @@ func Test_wildmenu_dirstack()
endfunc
" Test for recalling newer or older cmdline from history with <Up>, <Down>,
" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>.
" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <kPageUp>, <kPageDown>, <C-p>, or
" <C-n>.
func Test_recalling_cmdline()
CheckFeature cmdline_hist
@@ -2091,17 +2115,18 @@ func Test_recalling_cmdline()
cnoremap <Plug>(save-cmdline) <Cmd>let g:cmdlines += [getcmdline()]<CR>
let histories = [
\ {'name': 'cmd', 'enter': ':', 'exit': "\<Esc>"},
\ {'name': 'search', 'enter': '/', 'exit': "\<Esc>"},
\ {'name': 'expr', 'enter': ":\<C-r>=", 'exit': "\<Esc>\<Esc>"},
\ {'name': 'input', 'enter': ":call input('')\<CR>", 'exit': "\<CR>"},
\ #{name: 'cmd', enter: ':', exit: "\<Esc>"},
\ #{name: 'search', enter: '/', exit: "\<Esc>"},
\ #{name: 'expr', enter: ":\<C-r>=", exit: "\<Esc>\<Esc>"},
\ #{name: 'input', enter: ":call input('')\<CR>", exit: "\<CR>"},
"\ TODO: {'name': 'debug', ...}
\]
let keypairs = [
\ {'older': "\<Up>", 'newer': "\<Down>", 'prefixmatch': v:true},
\ {'older': "\<S-Up>", 'newer': "\<S-Down>", 'prefixmatch': v:false},
\ {'older': "\<PageUp>", 'newer': "\<PageDown>", 'prefixmatch': v:false},
\ {'older': "\<C-p>", 'newer': "\<C-n>", 'prefixmatch': v:false},
\ #{older: "\<Up>", newer: "\<Down>", prefixmatch: v:true},
\ #{older: "\<S-Up>", newer: "\<S-Down>", prefixmatch: v:false},
\ #{older: "\<PageUp>", newer: "\<PageDown>", prefixmatch: v:false},
\ #{older: "\<kPageUp>", newer: "\<kPageDown>", prefixmatch: v:false},
\ #{older: "\<C-p>", newer: "\<C-n>", prefixmatch: v:false},
\]
let prefix = 'vi'
for h in histories

View File

@@ -2111,6 +2111,15 @@ func Test_modifyOtherKeys_ambiguous_mapping()
unmap <C-J>
unmap <C-J>x
" if a special character is following there should be a check for a termcode
nnoremap s aX<Esc>
nnoremap s<BS> aY<Esc>
set t_kb=
call setline(1, 'x')
call feedkeys("s\x08", 'Lx!')
call assert_equal('xY', getline(1))
set timeoutlen&
bwipe!
endfunc

View File

@@ -1116,6 +1116,14 @@ def Test_assignment_dict()
END
v9.CheckDefAndScriptSuccess(lines)
lines =<< trim END
var key = 'foo'
g:[key] = 'value'
assert_equal('value', g:foo)
unlet g:foo
END
v9.CheckDefAndScriptSuccess(lines)
lines =<< trim END
var dd = {one: 1}
dd.one) = 2
@@ -1594,7 +1602,7 @@ def Test_assign_list()
l[g:idx : 1] = [0]
echo l
END
v9.CheckDefExecAndScriptFailure(lines, 'E1030: Using a String as a Number: "x"')
v9.CheckDefExecAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "x"'])
lines =<< trim END
var l = [1, 2]

View File

@@ -2091,6 +2091,11 @@ def Test_expr8_list()
var llstring: list<list<string>> = [['text'], []]
llstring = [[], ['text']]
llstring = [[], []]
var ls = [null_string]
assert_equal('list<string>', typename(ls))
var lb = [null_blob]
assert_equal('list<blob>', typename(lb))
END
v9.CheckDefAndScriptSuccess(lines)
@@ -2608,6 +2613,11 @@ def Test_expr8_dict()
# comment to start fold is OK
var x1: number #{{ fold
var x2 = 9 #{{ fold
var ds = {k: null_string}
assert_equal('dict<string>', typename(ds))
var dl = {a: null_list}
assert_equal('dict<list<unknown>>', typename(dl))
END
v9.CheckDefAndScriptSuccess(lines)

View File

@@ -1479,12 +1479,12 @@ def Test_pass_legacy_lambda_to_def_func()
lines =<< trim END
vim9script
def g:TestFunc(f: func)
def g:TestFunc(F: func)
enddef
legacy call g:TestFunc({-> 0})
delfunc g:TestFunc
def g:TestFunc(f: func(number))
def g:TestFunc(F: func(number))
enddef
legacy call g:TestFunc({nr -> 0})
delfunc g:TestFunc
@@ -3788,8 +3788,8 @@ def Test_check_func_arg_types()
return x + 1
enddef
def G(g: func): dict<func>
return {f: g}
def G(Fg: func): dict<func>
return {f: Fg}
enddef
def H(d: dict<func>): string
@@ -3799,6 +3799,8 @@ def Test_check_func_arg_types()
v9.CheckScriptSuccess(lines + ['echo H(G(F1))'])
v9.CheckScriptFailure(lines + ['echo H(G(F2))'], 'E1013:')
v9.CheckScriptFailure(lines + ['def SomeFunc(ff: func)', 'enddef'], 'E704:')
enddef
def Test_call_func_with_null()

View File

@@ -2253,6 +2253,13 @@ def Test_for_loop_unpack()
res->add(n)
endfor
assert_equal([2, 5], res)
var text: list<string> = ["hello there", "goodbye now"]
var splitted = ''
for [first; next] in mapnew(text, (i, v) => split(v))
splitted ..= string(first) .. string(next) .. '/'
endfor
assert_equal("'hello'['there']/'goodbye'['now']/", splitted)
END
v9.CheckDefAndScriptSuccess(lines)
@@ -3942,10 +3949,6 @@ def Test_profile_with_lambda()
enddef
def Profile()
profile start Xprofile.log
profile func ProfiledWithLambda
# mark ProfiledNested for profiling to avoid E1271
profile func ProfiledNested
ProfiledWithLambda()
ProfiledNested()
@@ -3957,8 +3960,20 @@ def Test_profile_with_lambda()
profdel func *
profile pause
enddef
Profile()
writefile(['done'], 'Xdidprofile')
var result = 'done'
try
# mark functions for profiling now to avoid E1271
profile start Xprofile.log
profile func ProfiledWithLambda
profile func ProfiledNested
Profile()
catch
result = 'failed: ' .. v:exception
finally
writefile([result], 'Xdidprofile')
endtry
END
writefile(lines, 'Xprofile.vim')
call system(g:GetVimCommand()
@@ -3974,6 +3989,21 @@ def Test_profile_with_lambda()
delete('Xprofile.vim')
enddef
func Test_misplaced_type()
CheckRunVimInTerminal
call Run_Test_misplaced_type()
endfunc
def Run_Test_misplaced_type()
writefile(['let g:somevar = "asdf"'], 'XTest_misplaced_type')
var buf = g:RunVimInTerminal('-S XTest_misplaced_type', {'rows': 6})
term_sendkeys(buf, ":vim9cmd echo islocked('g:somevar: string')\<CR>")
g:VerifyScreenDump(buf, 'Test_misplaced_type', {})
g:StopVimInTerminal(buf)
delete('XTest_misplaced_type')
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new

View File

@@ -429,6 +429,12 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
if (type == NULL)
return FAIL;
fp->uf_arg_types[i] = type;
if (i < fp->uf_args.ga_len
&& (type->tt_type == VAR_FUNC
|| type->tt_type == VAR_PARTIAL)
&& var_wrong_func_name(
((char_u **)fp->uf_args.ga_data)[i], TRUE))
return FAIL;
}
}
}

View File

@@ -750,6 +750,26 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4590,
/**/
4589,
/**/
4588,
/**/
4587,
/**/
4586,
/**/
4585,
/**/
4584,
/**/
4583,
/**/
4582,
/**/
4581,
/**/
4580,
/**/

View File

@@ -1000,7 +1000,12 @@ generate_loadvar(
break;
case dest_global:
if (vim_strchr(name, AUTOLOAD_CHAR) == NULL)
generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
{
if (name[2] == NUL)
generate_instr_type(cctx, ISN_LOADGDICT, &t_dict_any);
else
generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
}
else
generate_LOAD(cctx, ISN_LOADAUTO, 0, name, type);
break;
@@ -1797,7 +1802,7 @@ compile_assign_unlet(
{
type = get_type_on_stack(cctx, 1);
if (need_type(type, &t_number,
-1, 0, cctx, FALSE, FALSE) == FAIL)
-2, 0, cctx, FALSE, FALSE) == FAIL)
return FAIL;
}
type = get_type_on_stack(cctx, 0);
@@ -2413,17 +2418,19 @@ may_compile_assignment(exarg_T *eap, char_u **line, cctx_T *cctx)
// Recognize an assignment if we recognize the variable
// name:
// "g:var = expr"
// "local = expr" where "local" is a local var.
// "script = expr" where "script" is a script-local var.
// "import = expr" where "import" is an imported var
// "&opt = expr"
// "$ENV = expr"
// "@r = expr"
// "g:var = expr"
// "g:[key] = expr"
// "local = expr" where "local" is a local var.
// "script = expr" where "script" is a script-local var.
// "import = expr" where "import" is an imported var
if (*eap->cmd == '&'
|| *eap->cmd == '$'
|| *eap->cmd == '@'
|| ((len) > 2 && eap->cmd[1] == ':')
|| STRNCMP(eap->cmd, "g:[", 3) == 0
|| variable_exists(eap->cmd, len, cctx))
{
*line = compile_assignment(eap->cmd, eap, CMD_SIZE, cctx);

View File

@@ -1896,79 +1896,54 @@ execute_storerange(isn_T *iptr, ectx_T *ectx)
SOURCING_LNUM = iptr->isn_lnum;
if (tv_dest->v_type == VAR_LIST)
{
long n1;
long n2;
int error = FALSE;
long n1;
long n2;
listitem_T *li1;
n1 = (long)tv_get_number_chk(tv_idx1, &error);
if (error)
n1 = (long)tv_get_number_chk(tv_idx1, NULL);
if (tv_idx2->v_type == VAR_SPECIAL
&& tv_idx2->vval.v_number == VVAL_NONE)
n2 = list_len(tv_dest->vval.v_list) - 1;
else
n2 = (long)tv_get_number_chk(tv_idx2, NULL);
li1 = check_range_index_one(tv_dest->vval.v_list, &n1, FALSE);
if (li1 == NULL)
status = FAIL;
else
{
if (tv_idx2->v_type == VAR_SPECIAL
&& tv_idx2->vval.v_number == VVAL_NONE)
n2 = list_len(tv_dest->vval.v_list) - 1;
else
n2 = (long)tv_get_number_chk(tv_idx2, &error);
if (error)
status = FAIL; // cannot happen?
else
{
listitem_T *li1 = check_range_index_one(
tv_dest->vval.v_list, &n1, FALSE);
if (li1 == NULL)
status = FAIL;
else
{
status = check_range_index_two(
tv_dest->vval.v_list,
&n1, li1, &n2, FALSE);
if (status != FAIL)
status = list_assign_range(
tv_dest->vval.v_list,
tv->vval.v_list,
n1,
n2,
tv_idx2->v_type == VAR_SPECIAL,
(char_u *)"=",
(char_u *)"[unknown]");
}
}
status = check_range_index_two(tv_dest->vval.v_list,
&n1, li1, &n2, FALSE);
if (status != FAIL)
status = list_assign_range(
tv_dest->vval.v_list,
tv->vval.v_list,
n1,
n2,
tv_idx2->v_type == VAR_SPECIAL,
(char_u *)"=",
(char_u *)"[unknown]");
}
}
else if (tv_dest->v_type == VAR_BLOB)
{
varnumber_T n1;
varnumber_T n2;
int error = FALSE;
long bloblen;
n1 = tv_get_number_chk(tv_idx1, &error);
if (error)
n1 = tv_get_number_chk(tv_idx1, NULL);
if (tv_idx2->v_type == VAR_SPECIAL
&& tv_idx2->vval.v_number == VVAL_NONE)
n2 = blob_len(tv_dest->vval.v_blob) - 1;
else
n2 = tv_get_number_chk(tv_idx2, NULL);
bloblen = blob_len(tv_dest->vval.v_blob);
if (check_blob_index(bloblen, n1, FALSE) == FAIL
|| check_blob_range(bloblen, n1, n2, FALSE) == FAIL)
status = FAIL;
else
{
if (tv_idx2->v_type == VAR_SPECIAL
&& tv_idx2->vval.v_number == VVAL_NONE)
n2 = blob_len(tv_dest->vval.v_blob) - 1;
else
n2 = tv_get_number_chk(tv_idx2, &error);
if (error)
status = FAIL;
else
{
long bloblen = blob_len(tv_dest->vval.v_blob);
if (check_blob_index(bloblen,
n1, FALSE) == FAIL
|| check_blob_range(bloblen,
n1, n2, FALSE) == FAIL)
status = FAIL;
else
status = blob_set_range(
tv_dest->vval.v_blob, n1, n2, tv);
}
}
status = blob_set_range(tv_dest->vval.v_blob, n1, n2, tv);
}
else
{
@@ -4773,7 +4748,10 @@ exec_instructions(ectx_T *ectx)
li = li->li_next;
for (i = 0; li != NULL; ++i)
{
list_set_item(rem_list, i, &li->li_tv);
typval_T tvcopy;
copy_tv(&li->li_tv, &tvcopy);
list_set_item(rem_list, i, &tvcopy);
li = li->li_next;
}
--count;