Compare commits

..

13 Commits

Author SHA1 Message Date
2c79e9d14d patch 8.2.1344: Vim9: No test for trying to redefine global function
Problem:    Vim9: No test for trying to redefine global function.
Solution:   Add a test.
2020-08-01 18:57:52 +02:00
333894b195 patch 8.2.1343: Vim9: cannot find global function when using g:
Problem:    Vim9: cannot find global function when using g: when local
            function with the same name exists.
Solution:   Find global function when using g:.
2020-08-01 18:53:07 +02:00
f5a48010ef patch 8.2.1342: Vim9: accidentally using "t" gives a confusing error
Problem:    Vim9: accidentally using "x" gives a confusing error.
Solution:   Disallow using ":t" in Vim9 script. (issue #6399)
2020-08-01 17:00:03 +02:00
2ec208172c patch 8.2.1341: build failures
Problem:    Build failures.
Solution:   Add missing error message.
2020-08-01 16:35:08 +02:00
b86abadf87 patch 8.2.1340: some tests fail on Cirrus CI and/or with FreeBSD
Problem:    Some tests fail on Cirrus CI and/or with FreeBSD.
Solution:   Make 'backupskip' empty. Do not run tests as root. Check for
            directory when using viminfo. (Ozaki Kiichi, closes #6596)
2020-08-01 16:08:19 +02:00
2caa1594e7 patch 8.2.1339: Vim9: assigning to global dict variable doesn't work
Problem:    Vim9: assigning to global dict variable doesn't work.
Solution:   Guess variable type based in index type. (issue #6591)
2020-08-01 15:53:19 +02:00
8e4c8c853e patch 8.2.1338: Vim9: assigning to script-local variable doesn't check type
Problem:    Vim9: assigning to script-local variable doesn't check type.
Solution:   Use the type. (issue #6591)
2020-08-01 15:38:38 +02:00
586268721d patch 8.2.1337: Vim9: cannot use empty key in dict assignment
Problem:    Vim9: cannot use empty key in dict assignment.
Solution:   Allow empty key. (closes #6591)
2020-08-01 14:06:38 +02:00
af50e899e7 patch 8.2.1336: build failure on non-Unix systems
Problem:    Build failure on non-Unix systems.
Solution:   Add #ifdef.
2020-08-01 13:22:10 +02:00
4e1d8bd79b patch 8.2.1335: CTRL-C in the GUI doesn't interrupt
Problem:    CTRL-C in the GUI doesn't interrupt. (Sergey Vlasov)
Solution:   Recognize "C" with CTRL modifier as CTRL-C. (issue #6565)
2020-08-01 13:10:14 +02:00
b53da7918c patch 8.2.1334: Github workflow timeout needs tuning
Problem:    Github workflow timeout needs tuning
Solution:   Use a 10 minute timeout. Fail when timing out. (Ken Takata,
            closes #6590)
2020-08-01 12:26:04 +02:00
af8edbb8dc patch 8.2.1333: Vim9: memory leak when using nested global function
Problem:    Vim9: memory leak when using nested global function.
Solution:   Swap from and to when copying the lines.
2020-08-01 00:03:09 +02:00
ce6583568f patch 8.2.1332: Vim9: memory leak when using nested global function
Problem:    Vim9: memory leak when using nested global function.
Solution:   Delete the function when deleting the instruction.  Disable test
            that still causes a leak.
2020-07-31 23:47:12 +02:00
23 changed files with 349 additions and 143 deletions

View File

@ -11,6 +11,9 @@ freebsd_12_task:
- NPROC=$(getconf _NPROCESSORS_ONLN)
- ./configure --with-features=${FEATURES}
- make -j${NPROC}
- src/vim --version
test_script:
- make test
- src/vim --version
# run tests as user "cirrus" instead of root
- pw useradd cirrus -m
- chown -R cirrus:cirrus .
- sudo -u cirrus make test

View File

@ -210,14 +210,18 @@ jobs:
echo %COL_GREEN%Wait for vim tests to finish.%COL_RESET%
cd ..\src2\testdir
:: Wait about 5 minutes.
for /L %%i in (1,1,300) do (
:: Wait about 10 minutes.
for /L %%i in (1,1,600) do (
if exist done.txt goto exitloop
ping -n 2 localhost > nul
)
echo %COL_RED%Timed out.%COL_RESET%
set timeout=1
:exitloop
echo %COL_GREEN%Test results of vim:%COL_RESET%
if exist messages type messages
nmake -nologo -f Make_dos.mak report VIMPROG=..\..\src\vim || exit 1
if "%timeout%"=="1" (
echo %COL_RED%Timed out.%COL_RESET%
exit 1
)

View File

@ -190,8 +190,8 @@ To intentionally avoid a variable being available later, a block can be used:
An existing variable cannot be assigned to with `:let`, since that implies a
declaration. Global, window, tab, buffer and Vim variables can only be used
without `:let`, because they are are not really declared, they can also be
deleted with `:unlet`.
without `:let`, because they are not really declared, they can also be deleted
with `:unlet`.
Variables cannot shadow previously defined variables.
Variables may shadow Ex commands, rename the variable if needed.
@ -352,10 +352,11 @@ No curly braces expansion ~
|curly-braces-names| cannot be used.
No :xit, :append, :change or :insert ~
No :xit, :t, :append, :change or :insert ~
These commands are too easily confused with local variable names. Instead of
`:x` or `:xit` you can use `:exit`.
These commands are too easily confused with local variable names.
Instead of `:x` or `:xit` you can use `:exit`.
Instead of `:t` you can use `:copy`.
Comparators ~

View File

@ -7276,6 +7276,9 @@ ex_copymove(exarg_T *eap)
{
long n;
if (not_in_vim9(eap) == FAIL)
return;
n = get_address(eap, &eap->arg, eap->addr_type, FALSE, FALSE, FALSE, 1);
if (eap->arg == NULL) // error detected
{

View File

@ -1684,6 +1684,7 @@ EXTERN char e_readonlysbx[] INIT(= N_("E794: Cannot set variable in the sandbox:
EXTERN char e_stringreq[] INIT(= N_("E928: String required"));
EXTERN char e_emptykey[] INIT(= N_("E713: Cannot use empty key for Dictionary"));
EXTERN char e_dictreq[] INIT(= N_("E715: Dictionary required"));
EXTERN char e_dictnull[] INIT(= N_("E1103: Dictionary not set"));
EXTERN char e_listidx[] INIT(= N_("E684: list index out of range: %ld"));
EXTERN char e_blobidx[] INIT(= N_("E979: Blob index out of range: %ld"));
EXTERN char e_invalblob[] INIT(= N_("E978: Invalid operation for Blob"));

View File

@ -5575,3 +5575,27 @@ gui_handle_drop(
entered = FALSE;
}
#endif
/*
* Check if "key" is to interrupt us. Handles a key that has not had modifiers
* applied yet.
* Return the key with modifiers applied if so, NUL if not.
*/
int
check_for_interrupt(int key, int modifiers_arg)
{
int modifiers = modifiers_arg;
int c = merge_modifyOtherKeys(key, &modifiers);
if ((c == Ctrl_C && ctrl_c_interrupts)
#ifdef UNIX
|| (intr_char != Ctrl_C && c == intr_char)
#endif
)
{
got_int = TRUE;
return c;
}
return NUL;
}

View File

@ -1254,11 +1254,16 @@ key_press_event(GtkWidget *widget UNUSED,
add_to_input_buf(string2, 3);
}
if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
|| (string[0] == intr_char && intr_char != Ctrl_C)))
// Check if the key interrupts.
{
trash_input_buf();
got_int = TRUE;
int int_ch = check_for_interrupt(key, modifiers);
if (int_ch != NUL)
{
trash_input_buf();
string[0] = int_ch;
len = 1;
}
}
add_to_input_buf(string, len);

View File

@ -596,11 +596,17 @@ gui_ph_handle_keyboard(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
string[ len++ ] = ch;
}
if (len == 1 && ((ch == Ctrl_C && ctrl_c_interrupts)
|| ch == intr_char))
// Check if the key interrupts.
{
trash_input_buf();
got_int = TRUE;
int int_ch = check_for_interrupt(ch, modifiers);
if (int_ch != NUL)
{
ch = int_ch;
string[0] = ch;
len = 1;
trash_input_buf();
}
}
if (len == 1 && string[0] == CSI)

View File

@ -970,14 +970,16 @@ gui_x11_key_hit_cb(
add_to_input_buf(string2, 3);
}
if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
#ifdef UNIX
|| (intr_char != 0 && string[0] == intr_char)
#endif
))
// Check if the key interrupts.
{
trash_input_buf();
got_int = TRUE;
int int_ch = check_for_interrupt(key, modifiers);
if (int_ch != NUL)
{
trash_input_buf();
string[0] = int_ch;
len = 1;
}
}
add_to_input_buf(string, len);

View File

@ -65,4 +65,5 @@ void gui_update_screen(void);
char_u *get_find_dialog_text(char_u *arg, int *wwordp, int *mcasep);
int gui_do_findrepl(int flags, char_u *find_text, char_u *repl_text, int down);
void gui_handle_drop(int x, int y, int_u modifiers, char_u **fnames, int count);
int check_for_interrupt(int key, int modifiers_arg);
/* vim: set ft=c : */

View File

@ -5,12 +5,13 @@ int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T
char_u *get_lambda_name(void);
char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
void copy_func(char_u *lambda, char_u *global);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
void copy_func(char_u *lambda, char_u *global);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
void restore_funccal(void);

View File

@ -19,6 +19,22 @@ func Test_backup()
call delete('Xbackup.txt~')
endfunc
func Test_backup_backupskip()
set backup backupdir=. backupskip=*.txt
new
call setline(1, ['line1', 'line2'])
:f Xbackup.txt
:w! Xbackup.txt
" backup file is only created after
" writing a second time (before overwriting)
:w! Xbackup.txt
call assert_false(filereadable('Xbackup.txt~'))
bw!
set backup&vim backupdir&vim backupskip&vim
call delete('Xbackup.txt')
call delete('Xbackup.txt~')
endfunc
func Test_backup2()
set backup backupdir=.// backupskip=
new
@ -30,7 +46,7 @@ func Test_backup2()
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
let f=expand('%')
let f = expand('%')
call assert_match('%testdir%Xbackup.txt\~', f)
bw!
bw!
@ -50,7 +66,7 @@ func Test_backup2_backupcopy()
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
let f=expand('%')
let f = expand('%')
call assert_match('%testdir%Xbackup.txt\~', f)
bw!
bw!
@ -61,14 +77,11 @@ endfunc
" Test for using a non-existing directory as a backup directory
func Test_non_existing_backupdir()
CheckNotBSD
let save_backup = &backupdir
set backupdir=./non_existing_dir
set backupdir=./non_existing_dir backupskip=
call writefile(['line1'], 'Xfile')
new Xfile
" TODO: write doesn't fail in Cirrus FreeBSD CI test
call assert_fails('write', 'E510:')
let &backupdir = save_backup
set backupdir&vim backupskip&vim
call delete('Xfile')
endfunc

View File

@ -1682,7 +1682,6 @@ endfunc
" Test for editing a file without read permission
func Test_edit_file_no_read_perm()
CheckUnix
CheckNotBSD
call writefile(['one', 'two'], 'Xfile')
call setfperm('Xfile', '-w-------')
new

View File

@ -133,6 +133,12 @@ def Test_nested_function()
CheckDefFailure(['func Nested()', 'endfunc'], 'E1086:')
enddef
func Test_call_default_args_from_func()
call assert_equal('string', MyDefaultArgs())
call assert_equal('one', MyDefaultArgs('one'))
call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
endfunc
def Test_nested_global_function()
let lines =<< trim END
vim9script
@ -141,7 +147,7 @@ def Test_nested_global_function()
return 'inner'
enddef
enddef
disass Outer
defcompile
Outer()
assert_equal('inner', g:Inner())
delfunc g:Inner
@ -153,13 +159,35 @@ def Test_nested_global_function()
delfunc g:Inner
END
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
def Outer()
def g:Inner(): string
return 'inner'
enddef
enddef
defcompile
Outer()
Outer()
END
CheckScriptFailure(lines, "E122:")
enddef
func Test_call_default_args_from_func()
call assert_equal('string', MyDefaultArgs())
call assert_equal('one', MyDefaultArgs('one'))
call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
endfunc
def Test_global_local_function()
let lines =<< trim END
vim9script
def g:Func(): string
return 'global'
enddef
def Func(): string
return 'local'
enddef
assert_equal('global', g:Func())
assert_equal('local', Func())
END
CheckScriptSuccess(lines)
enddef
func TakesOneArg(arg)
echo a:arg

View File

@ -244,10 +244,60 @@ def Test_assignment_dict()
# overwrite
dict3['key'] = 'another'
call CheckDefExecFailure(['let dd = {}', 'dd[""] = 6'], 'E713:')
# empty key can be used
let dd = {}
dd[""] = 6
assert_equal({'': 6}, dd)
# type becomes dict<any>
let somedict = rand() > 0 ? #{a: 1, b: 2} : #{a: 'a', b: 'b'}
# assignment to script-local dict
let lines =<< trim END
vim9script
let test: dict<any> = {}
def FillDict(): dict<any>
test['a'] = 43
return test
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
lines =<< trim END
vim9script
let test: dict<any>
def FillDict(): dict<any>
test['a'] = 43
return test
enddef
FillDict()
END
call CheckScriptFailure(lines, 'E1103:')
# assignment to global dict
lines =<< trim END
vim9script
g:test = {}
def FillDict(): dict<any>
g:test['a'] = 43
return g:test
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
# assignment to buffer dict
lines =<< trim END
vim9script
b:test = {}
def FillDict(): dict<any>
b:test['a'] = 43
return b:test
enddef
assert_equal(#{a: 43}, FillDict())
END
call CheckScriptSuccess(lines)
enddef
def Test_assignment_local()
@ -783,13 +833,6 @@ def Test_try_catch()
endtry
assert_equal(300, n)
try
d[''] = 3
catch /E713:/
n = 311
endtry
assert_equal(311, n)
try
unlet g:does_not_exist
catch /E108:/
@ -1585,18 +1628,21 @@ def Test_fixed_size_list()
enddef
def Test_no_insert_xit()
call CheckDefExecFailure(['x = 1'], 'E1100:')
call CheckDefExecFailure(['a = 1'], 'E1100:')
call CheckDefExecFailure(['i = 1'], 'E1100:')
call CheckDefExecFailure(['c = 1'], 'E1100:')
call CheckDefExecFailure(['i = 1'], 'E1100:')
call CheckDefExecFailure(['t = 1'], 'E1100:')
call CheckDefExecFailure(['x = 1'], 'E1100:')
CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:')
CheckScriptFailure(['vim9script', 'a = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'a'], 'E1100:')
CheckScriptFailure(['vim9script', 'i = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'i'], 'E1100:')
CheckScriptFailure(['vim9script', 'c = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'c'], 'E1100:')
CheckScriptFailure(['vim9script', 'i = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'i'], 'E1100:')
CheckScriptFailure(['vim9script', 't'], 'E1100:')
CheckScriptFailure(['vim9script', 't = 1'], 'E1100:')
CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:')
enddef
def IfElse(what: number): string

View File

@ -807,7 +807,7 @@ func Test_viminfo_perm()
" Try to write the viminfo to a directory
call mkdir('Xdir')
call assert_fails('wviminfo Xdir', 'E886:')
call assert_fails('wviminfo Xdir', 'E137:')
call delete('Xdir', 'rf')
endfunc

View File

@ -136,9 +136,7 @@ func Test_writefile_sync_arg()
endfunc
func Test_writefile_sync_dev_stdout()
if !has('unix')
return
endif
CheckUnix
if filewritable('/dev/stdout')
" Just check that this doesn't cause an error.
call writefile(['one'], '/dev/stdout')
@ -371,13 +369,10 @@ endfunc
" Test for writing to a readonly file
func Test_write_readonly()
" In Cirrus-CI, the freebsd tests are run under a root account. So this test
" doesn't fail.
CheckNotBSD
call writefile([], 'Xfile')
call setfperm('Xfile', "r--------")
edit Xfile
set noreadonly
set noreadonly backupskip=
call assert_fails('write', 'E505:')
let save_cpo = &cpo
set cpo+=W
@ -386,37 +381,32 @@ func Test_write_readonly()
call setline(1, ['line1'])
write!
call assert_equal(['line1'], readfile('Xfile'))
set backupskip&
call delete('Xfile')
endfunc
" Test for 'patchmode'
func Test_patchmode()
CheckNotBSD
call writefile(['one'], 'Xfile')
set patchmode=.orig nobackup writebackup
set patchmode=.orig nobackup backupskip= writebackup
new Xfile
call setline(1, 'two')
" first write should create the .orig file
write
" TODO: Xfile.orig is not created in Cirrus FreeBSD CI test
call assert_equal(['one'], readfile('Xfile.orig'))
call setline(1, 'three')
" subsequent writes should not create/modify the .orig file
write
call assert_equal(['one'], readfile('Xfile.orig'))
set patchmode& backup& writebackup&
set patchmode& backup& backupskip& writebackup&
call delete('Xfile')
call delete('Xfile.orig')
endfunc
" Test for writing to a file in a readonly directory
func Test_write_readonly_dir()
if !has('unix') || has('bsd')
" On MS-Windows, modifying files in a read-only directory is allowed.
" In Cirrus-CI for Freebsd, tests are run under a root account where
" modifying files in a read-only directory are allowed.
return
endif
" On MS-Windows, modifying files in a read-only directory is allowed.
CheckUnix
call mkdir('Xdir')
call writefile(['one'], 'Xdir/Xfile1')
call setfperm('Xdir', 'r-xr--r--')
@ -426,12 +416,12 @@ func Test_write_readonly_dir()
call assert_fails('write', 'E212:')
" try to create a backup file in the directory
edit! Xdir/Xfile1
set backupdir=./Xdir
set backupdir=./Xdir backupskip=
set patchmode=.orig
call assert_fails('write', 'E509:')
call setfperm('Xdir', 'rwxr--r--')
call delete('Xdir', 'rf')
set backupdir& patchmode&
set backupdir& backupskip& patchmode&
endfunc
" Test for writing a file using invalid file encoding

View File

@ -780,7 +780,7 @@ find_func_with_sid(char_u *name, int sid)
* When "is_global" is true don't find script-local or imported functions.
* Return NULL for unknown function.
*/
static ufunc_T *
ufunc_T *
find_func_even_dead(char_u *name, int is_global, cctx_T *cctx)
{
hashitem_T *hi;
@ -789,9 +789,10 @@ find_func_even_dead(char_u *name, int is_global, cctx_T *cctx)
if (!is_global)
{
char_u *after_script = NULL;
int vim9script = in_vim9script();
char_u *after_script = NULL;
if (in_vim9script())
if (vim9script)
{
// Find script-local function before global one.
func = find_func_with_sid(name, current_sctx.sc_sid);
@ -799,7 +800,7 @@ find_func_even_dead(char_u *name, int is_global, cctx_T *cctx)
return func;
}
if (!in_vim9script()
if (!vim9script
&& name[0] == K_SPECIAL
&& name[1] == KS_EXTRA
&& name[2] == KE_SNR)
@ -815,7 +816,7 @@ find_func_even_dead(char_u *name, int is_global, cctx_T *cctx)
else
after_script = NULL;
}
if (in_vim9script() || after_script != NULL)
if (vim9script || after_script != NULL)
{
// Find imported function before global one.
imported = find_imported(
@ -1188,10 +1189,10 @@ copy_func(char_u *lambda, char_u *global)
fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY;
fp->uf_def_status = ufunc->uf_def_status;
fp->uf_dfunc_idx = ufunc->uf_dfunc_idx;
if (ga_copy_strings(&fp->uf_args, &ufunc->uf_args) == FAIL
|| ga_copy_strings(&fp->uf_def_args, &ufunc->uf_def_args)
if (ga_copy_strings(&ufunc->uf_args, &fp->uf_args) == FAIL
|| ga_copy_strings(&ufunc->uf_def_args, &fp->uf_def_args)
== FAIL
|| ga_copy_strings(&fp->uf_lines, &ufunc->uf_lines) == FAIL)
|| ga_copy_strings(&ufunc->uf_lines, &fp->uf_lines) == FAIL)
goto failed;
fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL
@ -1759,7 +1760,7 @@ delete_script_functions(int sid)
{
hashitem_T *hi;
ufunc_T *fp;
long_u todo;
long_u todo = 1;
char_u buf[30];
size_t len;
@ -1769,18 +1770,27 @@ delete_script_functions(int sid)
sprintf((char *)buf + 3, "%d_", sid);
len = STRLEN(buf);
todo = func_hashtab.ht_used;
for (hi = func_hashtab.ht_array; todo > 0; ++hi)
if (!HASHITEM_EMPTY(hi))
{
fp = HI2UF(hi);
if (STRNCMP(fp->uf_name, buf, len) == 0)
while (todo > 0)
{
todo = func_hashtab.ht_used;
for (hi = func_hashtab.ht_array; todo > 0; ++hi)
if (!HASHITEM_EMPTY(hi))
{
fp->uf_flags |= FC_DEAD;
func_clear(fp, TRUE);
fp = HI2UF(hi);
if (STRNCMP(fp->uf_name, buf, len) == 0)
{
int changed = func_hashtab.ht_changed;
fp->uf_flags |= FC_DEAD;
func_clear(fp, TRUE);
// When clearing a function another function can be cleared
// as a side effect. When that happens start over.
if (changed != func_hashtab.ht_changed)
break;
}
--todo;
}
--todo;
}
}
}
#if defined(EXITFREE) || defined(PROTO)
@ -2077,10 +2087,14 @@ call_func(
if (error == FCERR_NONE && funcexe->evaluate)
{
char_u *rfname = fname;
int is_global = FALSE;
// Ignore "g:" before a function name.
// Skip "g:" before a function name.
if (fp == NULL && fname[0] == 'g' && fname[1] == ':')
{
is_global = TRUE;
rfname = fname + 2;
}
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
@ -2092,7 +2106,7 @@ call_func(
* User defined function.
*/
if (fp == NULL)
fp = find_func(rfname, FALSE, NULL);
fp = find_func(rfname, is_global, NULL);
// Trigger FuncUndefined event, may load the function.
if (fp == NULL
@ -2101,13 +2115,13 @@ call_func(
&& !aborting())
{
// executed an autocommand, search for the function again
fp = find_func(rfname, FALSE, NULL);
fp = find_func(rfname, is_global, NULL);
}
// Try loading a package.
if (fp == NULL && script_autoload(rfname, TRUE) && !aborting())
{
// loaded a package, search for the function again
fp = find_func(rfname, FALSE, NULL);
fp = find_func(rfname, is_global, NULL);
}
if (fp == NULL)
{
@ -2116,7 +2130,7 @@ call_func(
// If using Vim9 script try not local to the script.
// TODO: should not do this if the name started with "s:".
if (p != NULL)
fp = find_func(p, FALSE, NULL);
fp = find_func(p, is_global, NULL);
}
if (fp != NULL && (fp->uf_flags & FC_DELETED))
@ -2166,6 +2180,7 @@ call_func(
*/
error = call_internal_func(fname, argcount, argvars, rettv);
}
/*
* The function call (or "FuncUndefined" autocommand sequence) might
* have been aborted by an error, an interrupt, or an explicitly thrown

View File

@ -754,6 +754,32 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1344,
/**/
1343,
/**/
1342,
/**/
1341,
/**/
1340,
/**/
1339,
/**/
1338,
/**/
1337,
/**/
1336,
/**/
1335,
/**/
1334,
/**/
1333,
/**/
1332,
/**/
1331,
/**/

View File

@ -5070,6 +5070,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
char_u *ret = NULL;
int var_count = 0;
int var_idx;
int scriptvar_sid = 0;
int scriptvar_idx = -1;
int semicolon = 0;
garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
@ -5333,7 +5335,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
}
else
{
int idx;
int idx;
imported_T *import = NULL;
for (idx = 0; reserved[idx] != NULL; ++idx)
if (STRCMP(reserved[idx], name) == 0)
@ -5374,9 +5377,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
}
else if ((varlen > 1 && STRNCMP(var_start, "s:", 2) == 0)
|| lookup_script(var_start, varlen) == OK
|| find_imported(var_start, varlen, cctx) != NULL)
|| (import = find_imported(var_start, varlen, cctx))
!= NULL)
{
dest = dest_script;
char_u *rawname = name + (name[1] == ':' ? 2 : 0);
if (is_decl)
{
if ((varlen > 1 && STRNCMP(var_start, "s:", 2) == 0))
@ -5387,6 +5392,21 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
name);
goto theend;
}
dest = dest_script;
// existing script-local variables should have a type
scriptvar_sid = current_sctx.sc_sid;
if (import != NULL)
scriptvar_sid = import->imp_sid;
scriptvar_idx = get_script_item_idx(scriptvar_sid,
rawname, TRUE);
if (scriptvar_idx >= 0)
{
scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid);
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ scriptvar_idx;
type = sv->sv_type;
}
}
else if (name[1] == ':' && name[2] != NUL)
{
@ -5466,11 +5486,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
{
has_index = TRUE;
if (type->tt_member == NULL)
{
semsg(_("E1088: cannot use an index on %s"), name);
goto theend;
}
member_type = type->tt_member;
member_type = &t_any;
else
member_type = type->tt_member;
}
else
{
@ -5699,6 +5717,18 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
emsg(_(e_missbrac));
goto theend;
}
if (type == &t_any)
{
type_T *idx_type = ((type_T **)stack->ga_data)[
stack->ga_len - 1];
// Index on variable of unknown type: guess the type from the
// index type: number is dict, otherwise dict.
// TODO: should do the assignment at runtime
if (idx_type->tt_type == VAR_NUMBER)
type = &t_list_any;
else
type = &t_dict_any;
}
if (type->tt_type == VAR_DICT
&& may_generate_2STRING(-1, cctx) == FAIL)
goto theend;
@ -5766,21 +5796,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
break;
case dest_script:
{
char_u *rawname = name + (name[1] == ':' ? 2 : 0);
imported_T *import = NULL;
int sid = current_sctx.sc_sid;
int idx;
if (name[1] != ':')
{
import = find_imported(name, 0, cctx);
if (import != NULL)
sid = import->imp_sid;
}
idx = get_script_item_idx(sid, rawname, TRUE);
// TODO: specific type
if (idx < 0)
if (scriptvar_idx < 0)
{
char_u *name_s = name;
@ -5796,14 +5812,14 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
vim_snprintf((char *)name_s, len,
"s:%s", name);
}
generate_OLDSCRIPT(cctx, ISN_STORES, name_s, sid,
&t_any);
generate_OLDSCRIPT(cctx, ISN_STORES, name_s,
scriptvar_sid, type);
if (name_s != name)
vim_free(name_s);
}
else
generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
sid, idx, &t_any);
scriptvar_sid, scriptvar_idx, type);
}
break;
case dest_local:
@ -7451,6 +7467,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
case CMD_append:
case CMD_change:
case CMD_insert:
case CMD_t:
case CMD_xit:
not_in_vim9(&ea);
goto erret;
@ -7677,8 +7694,21 @@ delete_instr(isn_T *isn)
break;
case ISN_NEWFUNC:
vim_free(isn->isn_arg.newfunc.nf_lambda);
vim_free(isn->isn_arg.newfunc.nf_global);
{
char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
ufunc_T *ufunc = find_func_even_dead(lambda, TRUE, NULL);
if (ufunc != NULL)
{
// Clear uf_dfunc_idx so that the function is deleted.
clear_def_function(ufunc);
ufunc->uf_dfunc_idx = 0;
func_ptr_unref(ufunc);
}
vim_free(lambda);
vim_free(isn->isn_arg.newfunc.nf_global);
}
break;
case ISN_2BOOL:

View File

@ -1422,11 +1422,13 @@ call_def_function(
dict_T *dict = tv_dict->vval.v_dict;
dictitem_T *di;
if (key == NULL || *key == NUL)
if (dict == NULL)
{
emsg(_(e_emptykey));
emsg(_(e_dictnull));
goto on_error;
}
if (key == NULL)
key = (char_u *)"";
tv = STACK_TV_BOT(-3);
di = dict_find(dict, key, -1);
if (di != NULL)

View File

@ -67,9 +67,10 @@ not_in_vim9(exarg_T *eap)
if (in_vim9script())
switch (eap->cmdidx)
{
case CMD_insert:
case CMD_append:
case CMD_change:
case CMD_insert:
case CMD_t:
case CMD_xit:
semsg(_("E1100: Missing :let: %s"), eap->cmd);
return FAIL;

View File

@ -3007,6 +3007,7 @@ read_viminfo(
{
FILE *fp;
char_u *fname;
stat_T st; // mch_stat() of existing viminfo file
if (no_viminfo())
return FAIL;
@ -3031,6 +3032,11 @@ read_viminfo(
vim_free(fname);
if (fp == NULL)
return FAIL;
if (mch_fstat(fileno(fp), &st) < 0 || S_ISDIR(st.st_mode))
{
fclose(fp);
return FAIL;
}
viminfo_errcnt = 0;
do_viminfo(fp, NULL, flags);
@ -3054,12 +3060,12 @@ write_viminfo(char_u *file, int forceit)
FILE *fp_out = NULL; // output viminfo file
char_u *tempname = NULL; // name of temp viminfo file
stat_T st_new; // mch_stat() of potential new file
stat_T st_old; // mch_stat() of existing viminfo file
#if defined(UNIX) || defined(VMS)
mode_t umask_save;
#endif
#ifdef UNIX
int shortname = FALSE; // use 8.3 file name
stat_T st_old; // mch_stat() of existing viminfo file
#endif
#ifdef MSWIN
int hidden = FALSE;
@ -3097,20 +3103,20 @@ write_viminfo(char_u *file, int forceit)
// write the new viminfo into, in the same directory as the
// existing viminfo file, which will be renamed once all writing is
// successful.
if (mch_fstat(fileno(fp_in), &st_old) < 0
|| S_ISDIR(st_old.st_mode)
#ifdef UNIX
// For Unix we check the owner of the file. It's not very nice to
// overwrite a user's viminfo file after a "su root", with a
// viminfo file that the user can't read.
st_old.st_dev = (dev_t)0;
st_old.st_ino = 0;
st_old.st_mode = 0600;
if (mch_stat((char *)fname, &st_old) == 0
&& getuid() != ROOT_UID
&& !(st_old.st_uid == getuid()
? (st_old.st_mode & 0200)
: (st_old.st_gid == getgid()
? (st_old.st_mode & 0020)
: (st_old.st_mode & 0002))))
// For Unix we check the owner of the file. It's not very nice
// to overwrite a user's viminfo file after a "su root", with a
// viminfo file that the user can't read.
|| (getuid() != ROOT_UID
&& !(st_old.st_uid == getuid()
? (st_old.st_mode & 0200)
: (st_old.st_gid == getgid()
? (st_old.st_mode & 0020)
: (st_old.st_mode & 0002))))
#endif
)
{
int tt = msg_didany;
@ -3120,7 +3126,6 @@ write_viminfo(char_u *file, int forceit)
fclose(fp_in);
goto end;
}
#endif
#ifdef MSWIN
// Get the file attributes of the existing viminfo file.
hidden = mch_ishidden(fname);