Compare commits

...

11 Commits

Author SHA1 Message Date
Bram Moolenaar
5d905c2b96 patch 8.2.0513: reading past allocate memory when using varargs
Problem:    Reading past allocate memory when using varargs.
Solution:   Fix copying function argument types.
2020-04-05 18:20:45 +02:00
Bram Moolenaar
5deeb3f1f9 patch 8.2.0512: Vim9: no optional arguments in func type
Problem:    Vim9: no optional arguments in func type.
Solution:   Check for question mark after type.  Find function reference
            without function().
2020-04-05 17:08:17 +02:00
Bram Moolenaar
d7ffc0ba8c patch 8.2.0511: Cscope code not fully tested
Problem:    Cscope code not fully tested.
Solution:   Add more test cases. (Dominique Pelle, closes #5886)
2020-04-05 15:36:16 +02:00
Bram Moolenaar
8d4ed11da6 patch 8.2.0510: Coverity complains about using uninitialized variable
Problem:    Coverity complains about using uninitialized variable.
Solution:   Assign a value to "scol".  Move code inside NULL check.
2020-04-04 14:50:32 +02:00
Bram Moolenaar
cde0ff39da patch 8.2.0509: various code is not properly tested.
Problem:    various code is not properly tested.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #5871)
2020-04-04 14:00:39 +02:00
Bram Moolenaar
d77a8525d5 patch 8.2.0508: Vim9: func and partial types not done yet
Problem:    Vim9: func and partial types not done yet
Solution:   Fill in details about func declaration, drop a separate partial
            declaration.
2020-04-03 21:59:57 +02:00
Bram Moolenaar
5259275347 patch 8.2.0507: getbufvar() may get the wrong dictionary
Problem:    Getbufvar() may get the wrong dictionary. (David le Blanc)
Solution:   Check for empty name. (closes #5878)
2020-04-03 18:43:35 +02:00
Bram Moolenaar
d1e9dc2723 patch 8.2.0506: Coverity complains about ignoring return value
Problem:    Coverity complains about ignoring return value.
Solution:   Add (void).
2020-04-03 18:13:57 +02:00
Bram Moolenaar
01603a9970 patch 8.2.0505: term_getty() not sufficiently tested
Problem:    term_getty() not sufficiently tested.
Solution:   Add more asserts. (Dominique Pelle, closes #5877)
2020-04-03 12:56:17 +02:00
Bram Moolenaar
3cca299520 patch 8.2.0504: Vim9: leaking scope memory when compilation fails
Problem:    Vim9: leaking scope memory when compilation fails.
Solution:   Cleanup the scope list.
2020-04-02 22:57:36 +02:00
Bram Moolenaar
585fea7b98 patch 8.2.0503: Vim9: some code is not tested
Problem:    Vim9: some code is not tested.
Solution:   Add tests.  Fix uncovered problems.
2020-04-02 22:33:21 +02:00
33 changed files with 1587 additions and 581 deletions

View File

@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2020 Mar 01
*vim9.txt* For Vim version 8.2. Last change: 2020 Apr 03
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -249,8 +249,8 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
the function follows in the next lines, until the
matching `:enddef`.
When {return-type} is omitted the function is not
expected to return anything.
When {return-type} is omitted or is "void" the
function is not expected to return anything.
{arguments} is a sequence of zero or more argument
declarations. There are three forms:
@@ -296,29 +296,40 @@ The following builtin types are supported:
float
string
blob
list<type>
dict<type>
(a: type, b: type): type
list<{type}>
dict<{type}>
job
channel
func
partial
func({type}, ...)
func({type}, ...): {type}
Not supported yet:
tuple<a: type, b: type, ...>
tuple<a: {type}, b: {type}, ...>
These types can be used in declarations, but no variable will have this type:
type|type
These types can be used in declarations, but no value will have this type:
{type}|{type}
void
any
There is no array type, use list<type> instead. For a list constant an
There is no array type, use list<{type}> instead. For a list constant an
efficient implementation is used that avoids allocating lot of small pieces of
memory.
A function defined with `:def` must declare the return type. If there is no
type then the function doesn't return anything. "void" is used in type
declarations.
A partial and function can be declared in more or less specific ways:
func any kind of function reference, no type
checking
func: {type} any number and type of arguments with specific
return type
func({type} ...) function with argument types, does not return
a value
func({type} ...): {type} function with argument types and return type
If the return type is "void" the function does not return a value.
The reference can also be a |Partial|, in which case it stores extra arguments
and/or a dictionary, which are not visible to the caller. Since they are
called in the same way the declaration is the same.
Custom types can be defined with `:type`: >
:type MyList list<string>

View File

@@ -110,6 +110,8 @@ find_word_under_cursor(
lbuf = vim_strnsave(lbuf, len);
}
}
else
scol = col;
if (winp != NULL)
*winp = wp;

View File

@@ -336,11 +336,6 @@ ret_func_any(int argcount UNUSED, type_T **argtypes UNUSED)
return &t_func_any;
}
static type_T *
ret_partial_any(int argcount UNUSED, type_T **argtypes UNUSED)
{
return &t_partial_any;
}
static type_T *
ret_channel(int argcount UNUSED, type_T **argtypes UNUSED)
{
return &t_channel;
@@ -564,7 +559,7 @@ static funcentry_T global_functions[] =
{"foldtext", 0, 0, 0, ret_string, f_foldtext},
{"foldtextresult", 1, 1, FEARG_1, ret_string, f_foldtextresult},
{"foreground", 0, 0, 0, ret_void, f_foreground},
{"funcref", 1, 3, FEARG_1, ret_partial_any, f_funcref},
{"funcref", 1, 3, FEARG_1, ret_func_any, f_funcref},
{"function", 1, 3, FEARG_1, ret_f_function, f_function},
{"garbagecollect", 0, 1, 0, ret_void, f_garbagecollect},
{"get", 2, 3, FEARG_1, ret_any, f_get},
@@ -961,7 +956,7 @@ static funcentry_T global_functions[] =
{"test_null_function", 0, 0, 0, ret_func_any, f_test_null_function},
{"test_null_job", 0, 0, 0, ret_job, JOB_FUNC(f_test_null_job)},
{"test_null_list", 0, 0, 0, ret_list_any, f_test_null_list},
{"test_null_partial", 0, 0, 0, ret_partial_any, f_test_null_partial},
{"test_null_partial", 0, 0, 0, ret_func_any, f_test_null_partial},
{"test_null_string", 0, 0, 0, ret_string, f_test_null_string},
{"test_option_not_set", 1, 1, FEARG_1,ret_void, f_test_option_not_set},
{"test_override", 2, 2, FEARG_2, ret_void, f_test_override},
@@ -2902,7 +2897,7 @@ ret_f_function(int argcount, type_T **argtypes UNUSED)
{
if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING)
return &t_func_any;
return &t_partial_void;
return &t_func_void;
}
/*

View File

@@ -2382,6 +2382,7 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
/*
* Find variable "varname" in hashtab "ht" with name "htname".
* When "varname" is empty returns curwin/curtab/etc vars dictionary.
* Returns NULL if not found.
*/
dictitem_T *
@@ -3503,8 +3504,12 @@ f_getbufvar(typval_T *argvars, typval_T *rettv)
else
{
// Look up the variable.
// Let getbufvar({nr}, "") return the "b:" dictionary.
v = find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', varname, FALSE);
if (*varname == NUL)
// Let getbufvar({nr}, "") return the "b:" dictionary.
v = &buf->b_bufvar;
else
v = find_var_in_ht(&buf->b_vars->dv_hashtab, 'b',
varname, FALSE);
if (v != NULL)
{
copy_tv(&v->di_tv, rettv);

View File

@@ -379,36 +379,39 @@ EXTERN sctx_T current_sctx INIT4(0, 0, 0, 0);
// Commonly used types.
EXTERN type_T t_any INIT4(VAR_UNKNOWN, 0, NULL, NULL);
EXTERN type_T t_void INIT4(VAR_VOID, 0, NULL, NULL);
EXTERN type_T t_bool INIT4(VAR_BOOL, 0, NULL, NULL);
EXTERN type_T t_special INIT4(VAR_SPECIAL, 0, NULL, NULL);
EXTERN type_T t_number INIT4(VAR_NUMBER, 0, NULL, NULL);
EXTERN type_T t_float INIT4(VAR_FLOAT, 0, NULL, NULL);
EXTERN type_T t_string INIT4(VAR_STRING, 0, NULL, NULL);
EXTERN type_T t_blob INIT4(VAR_BLOB, 0, NULL, NULL);
EXTERN type_T t_job INIT4(VAR_JOB, 0, NULL, NULL);
EXTERN type_T t_channel INIT4(VAR_CHANNEL, 0, NULL, NULL);
EXTERN type_T t_any INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL);
EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL);
EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL);
EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL);
EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL);
EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL);
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_func_void INIT4(VAR_FUNC, -1, &t_void, NULL);
EXTERN type_T t_func_any INIT4(VAR_FUNC, -1, &t_any, NULL);
EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL);
EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL);
EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL);
EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL);
EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL);
EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL);
EXTERN type_T t_partial_void INIT4(VAR_PARTIAL, -1, &t_void, NULL);
EXTERN type_T t_partial_any INIT4(VAR_PARTIAL, -1, &t_any, NULL);
EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_void, NULL);
EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_void, NULL);
EXTERN type_T t_list_any INIT4(VAR_LIST, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT4(VAR_DICT, 0, &t_any, NULL);
EXTERN type_T t_list_empty INIT4(VAR_LIST, 0, &t_void, NULL);
EXTERN type_T t_dict_empty INIT4(VAR_DICT, 0, &t_void, NULL);
EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL);
EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL);
EXTERN type_T t_list_bool INIT4(VAR_LIST, 0, &t_bool, NULL);
EXTERN type_T t_list_number INIT4(VAR_LIST, 0, &t_number, NULL);
EXTERN type_T t_list_string INIT4(VAR_LIST, 0, &t_string, NULL);
EXTERN type_T t_list_dict_any INIT4(VAR_LIST, 0, &t_dict_any, NULL);
EXTERN type_T t_dict_bool INIT4(VAR_DICT, 0, &t_bool, NULL);
EXTERN type_T t_dict_number INIT4(VAR_DICT, 0, &t_number, NULL);
EXTERN type_T t_dict_string INIT4(VAR_DICT, 0, &t_string, NULL);
EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL);
EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL);
#endif

View File

@@ -1966,7 +1966,7 @@ parse_command_name(mparm_T *parmp)
#endif
}
// Checking for "ex" here may catch some weir names, such as "vimex" or
// Checking for "ex" here may catch some weird names, such as "vimex" or
// "viewex", we assume the user knows that.
if (STRNICMP(initstr, "ex", 2) == 0)
{

View File

@@ -798,16 +798,16 @@ apply_general_options(win_T *wp, dict_T *dict)
if (*str != NUL)
wp->w_border_char[i] = mb_ptr2char(str);
}
}
if (list->lv_len == 1)
for (i = 1; i < 8; ++i)
wp->w_border_char[i] = wp->w_border_char[0];
if (list->lv_len == 2)
{
for (i = 4; i < 8; ++i)
wp->w_border_char[i] = wp->w_border_char[1];
for (i = 1; i < 4; ++i)
wp->w_border_char[i] = wp->w_border_char[0];
if (list->lv_len == 1)
for (i = 1; i < 8; ++i)
wp->w_border_char[i] = wp->w_border_char[0];
if (list->lv_len == 2)
{
for (i = 4; i < 8; ++i)
wp->w_border_char[i] = wp->w_border_char[1];
for (i = 1; i < 4; ++i)
wp->w_border_char[i] = wp->w_border_char[0];
}
}
}
}

View File

@@ -1341,11 +1341,16 @@ typedef enum
typedef struct type_S type_T;
struct type_S {
vartype_T tt_type;
short tt_argcount; // for func, partial, -1 for unknown
char tt_argcount; // for func, -1 for unknown
char tt_min_argcount; // number of non-optional arguments
char tt_flags; // TTFLAG_ values
type_T *tt_member; // for list, dict, func return type
type_T *tt_args; // func arguments
type_T **tt_args; // func argument types, allocated
};
#define TTFLAG_VARARGS 1 // func args ends with "..."
#define TTFLAG_OPTARG 2 // func arg type with "?"
/*
* Structure to hold an internal variable without a name.
*/
@@ -1516,7 +1521,7 @@ typedef struct
int uf_calls; // nr of active calls
int uf_cleared; // func_clear() was already called
int uf_dfunc_idx; // >= 0 for :def function only
garray_T uf_args; // arguments
garray_T uf_args; // arguments, including optional arguments
garray_T uf_def_args; // default argument expressions
// for :def (for :function uf_ret_type is NULL)
@@ -1527,6 +1532,7 @@ typedef struct
// uf_def_args; length: uf_def_args.ga_len + 1
char_u *uf_va_name; // name from "...name" or NULL
type_T *uf_va_type; // type from "...name: type" or NULL
type_T *uf_func_type; // type of the function, &t_func_any if unknown
garray_T uf_lines; // function lines
# ifdef FEAT_PROFILE

View File

@@ -274,6 +274,7 @@ NEW_TESTS = \
test_vartabs \
test_vim9_disassemble \
test_vim9_expr \
test_vim9_func \
test_vim9_script \
test_viminfo \
test_vimscript \
@@ -483,6 +484,7 @@ NEW_TESTS_RES = \
test_vartabs.res \
test_vim9_disassemble.res \
test_vim9_expr.res \
test_vim9_func.res \
test_vim9_script.res \
test_viminfo.res \
test_vimscript.res \

View File

@@ -133,3 +133,13 @@ func CheckNotRoot()
throw 'Skipped: cannot run test as root'
endif
endfunc
" Command to check that the current language is English
command CheckEnglish call CheckEnglish()
func CheckEnglish()
if v:lang != "C" && v:lang !~ '^[Ee]n'
throw 'Skipped: only works in English language environment'
endif
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -337,3 +337,5 @@ func IsRoot()
endif
return v:false
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -82,6 +82,8 @@ func RunVimInTerminal(arguments, options)
let cols = term_getsize(buf)[1]
endif
call term_wait(buf)
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
" the status line of the last window. This can be quite slow (e.g. when
" using valgrind).
@@ -113,3 +115,5 @@ func StopVimInTerminal(buf)
call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))})
only!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -6,11 +6,7 @@ CheckFeature clientserver
source shared.vim
func Test_client_server()
let cmd = GetVimCommand()
if cmd == ''
return
endif
func Check_X11_Connection()
if has('x11')
if empty($DISPLAY)
throw 'Skipped: $DISPLAY is not set'
@@ -19,11 +15,19 @@ func Test_client_server()
call remote_send('xxx', '')
catch
if v:exception =~ 'E240:'
throw 'Skipped: no connection to the X server'
throw 'Skipped: no connection to the X server'
endif
" ignore other errors
endtry
endif
endfunc
func Test_client_server()
let cmd = GetVimCommand()
if cmd == ''
return
endif
call Check_X11_Connection()
let name = 'XVIMTEST'
let cmd .= ' --servername ' . name
@@ -72,6 +76,10 @@ func Test_client_server()
endif
let g:testvar = 'myself'
call assert_equal('myself', remote_expr(v:servername, 'testvar'))
call remote_send(v:servername, ":let g:testvar2 = 75\<CR>")
call feedkeys('', 'x')
call assert_equal(75, g:testvar2)
call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:')
call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid')
call assert_equal('got it', g:myserverid->remote_read(2))
@@ -92,6 +100,55 @@ func Test_client_server()
call assert_equal('another', g:peek_result)
call assert_equal('another', remote_read(g:myserverid, 2))
if !has('gui_running')
" In GUI vim, the following tests display a dialog box
let cmd = GetVimProg() .. ' --servername ' .. name
" Run a separate instance to send a command to the server
call remote_expr(name, 'execute("only")')
call system(cmd .. ' --remote-send ":new Xfile<CR>"')
call assert_equal('2', remote_expr(name, 'winnr("$")'))
call assert_equal('Xfile', remote_expr(name, 'winbufnr(1)->bufname()'))
call remote_expr(name, 'execute("only")')
" Invoke a remote-expr. On MS-Windows, the returned value has a carriage
" return.
let l = system(cmd .. ' --remote-expr "2 + 2"')
call assert_equal(['4'], split(l, "\n"))
" Edit multiple files using --remote
call system(cmd .. ' --remote Xfile1 Xfile2 Xfile3')
call assert_equal("Xfile1\nXfile2\nXfile3\n", remote_expr(name, 'argv()'))
eval name->remote_send(":%bw!\<CR>")
" Edit files in separate tab pages
call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3')
call assert_equal('3', remote_expr(name, 'tabpagenr("$")'))
call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])'))
eval name->remote_send(":%bw!\<CR>")
" Edit a file using --remote-wait
eval name->remote_send(":source $VIMRUNTIME/plugin/rrhelper.vim\<CR>")
call system(cmd .. ' --remote-wait +enew Xfile1')
call assert_equal("Xfile1", remote_expr(name, 'bufname("#")'))
eval name->remote_send(":%bw!\<CR>")
" Edit files using --remote-tab-wait
call system(cmd .. ' --remote-tabwait +tabonly\|enew Xfile1 Xfile2')
call assert_equal('1', remote_expr(name, 'tabpagenr("$")'))
eval name->remote_send(":%bw!\<CR>")
" Error cases
if v:lang == "C" || v:lang =~ '^[Ee]n'
let l = split(system(cmd .. ' --remote +pwd'), "\n")
call assert_equal("Argument missing after: \"+pwd\"", l[1])
endif
let l = system(cmd .. ' --remote-expr "abcd"')
call assert_match('^E449: ', l)
endif
eval name->remote_send(":%bw!\<CR>")
eval name->remote_send(":qa!\<CR>")
try
call WaitForAssert({-> assert_equal("dead", job_status(job))})
@@ -102,8 +159,8 @@ func Test_client_server()
endif
endtry
call assert_fails("let x=remote_peek([])", 'E730:')
call assert_fails("let x=remote_read('vim10')", 'E277:')
call assert_fails("let x = remote_peek([])", 'E730:')
call assert_fails("let x = remote_read('vim10')", 'E277:')
endfunc
" Uncomment this line to get a debugging log

View File

@@ -23,7 +23,7 @@ endfunc
func Test_cscopeWithCscopeConnections()
call CscopeSetupOrClean(1)
" Test 0: E568: duplicate cscope database not added
" Test: E568: duplicate cscope database not added
try
set nocscopeverbose
cscope add Xcscope.out
@@ -34,44 +34,49 @@ func Test_cscopeWithCscopeConnections()
call assert_fails('cscope add', 'E560')
call assert_fails('cscope add Xcscope.out', 'E568')
call assert_fails('cscope add doesnotexist.out', 'E563')
if has('unix')
call assert_fails('cscope add /dev/null', 'E564:')
endif
" Test 1: Find this C-Symbol
" Test: Find this C-Symbol
for cmd in ['cs find s main', 'cs find 0 main']
let a = execute(cmd)
" Test 1.1 test where it moves the cursor
" Test where it moves the cursor
call assert_equal('main(void)', getline('.'))
" Test 1.2 test the output of the :cs command
" Test the output of the :cs command
call assert_match('\n(1 of 1): <<main>> main(void )', a)
endfor
" Test 2: Find this definition
for cmd in ['cs find g test_mf_hash', 'cs find 1 test_mf_hash']
" Test: Find this definition
for cmd in ['cs find g test_mf_hash',
\ 'cs find 1 test_mf_hash',
\ 'cs find 1 test_mf_hash'] " leading space ignored.
exe cmd
call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', ' static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1))
endfor
" Test 3: Find functions called by this function
" Test: Find functions called by this function
for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash']
let a = execute(cmd)
call assert_match('\n(1 of 42): <<mf_hash_init>> mf_hash_init(&ht);', a)
call assert_equal(' mf_hash_init(&ht);', getline('.'))
endfor
" Test 4: Find functions calling this function
" Test: Find functions calling this function
for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash']
let a = execute(cmd)
call assert_match('\n(1 of 1): <<main>> test_mf_hash();', a)
call assert_equal(' test_mf_hash();', getline('.'))
endfor
" Test 5: Find this text string
" Test: Find this text string
for cmd in ['cs find t Bram', 'cs find 4 Bram']
let a = execute(cmd)
call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
endfor
" Test 6: Find this egrep pattern
" Test: Find this egrep pattern
" test all matches returned by cscope
for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.']
let a = execute(cmd)
@@ -84,7 +89,7 @@ func Test_cscopeWithCscopeConnections()
call assert_fails('cnext', 'E553:')
endfor
" Test 7: Find the same egrep pattern using lcscope this time.
" Test: Find the same egrep pattern using lcscope this time.
let a = execute('lcs find e ^\#includ.')
call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a)
call assert_equal('#include <assert.h>', getline('.'))
@@ -94,7 +99,7 @@ func Test_cscopeWithCscopeConnections()
call assert_equal('#include "memfile.c"', getline('.'))
call assert_fails('lnext', 'E553:')
" Test 8: Find this file
" Test: Find this file
for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c']
enew
let a = execute(cmd)
@@ -102,7 +107,7 @@ func Test_cscopeWithCscopeConnections()
call assert_equal('Xmemfile_test.c', @%)
endfor
" Test 9: Find files #including this file
" Test: Find files #including this file
for cmd in ['cs find i assert.h', 'cs find 8 assert.h']
enew
let a = execute(cmd)
@@ -113,11 +118,11 @@ func Test_cscopeWithCscopeConnections()
call assert_equal('#include <assert.h>', getline('.'))
endfor
" Test 10: Invalid find command
" Test: Invalid find command
call assert_fails('cs find x', 'E560:')
if has('float')
" Test 11: Find places where this symbol is assigned a value
" Test: Find places where this symbol is assigned a value
" this needs a cscope >= 15.8
" unfortunately, Travis has cscope version 15.7
let cscope_version = systemlist('cscope --version')[0]
@@ -137,17 +142,17 @@ func Test_cscopeWithCscopeConnections()
endif
endif
" Test 12: leading whitespace is not removed for cscope find text
" Test: leading whitespace is not removed for cscope find text
let a = execute('cscope find t test_mf_hash')
call assert_equal(['', '(1 of 1): <<<unknown>>> test_mf_hash();'], split(a, '\n', 1))
call assert_equal(' test_mf_hash();', getline('.'))
" Test 13: test with scscope
" Test: test with scscope
let a = execute('scs find t Bram')
call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
" Test 14: cscope help
" Test: cscope help
for cmd in ['cs', 'cs help', 'cs xxx']
let a = execute(cmd)
call assert_match('^cscope commands:\n', a)
@@ -161,16 +166,16 @@ func Test_cscopeWithCscopeConnections()
let a = execute('scscope help')
call assert_match('This cscope command does not support splitting the window\.', a)
" Test 15: reset connections
" Test: reset connections
let a = execute('cscope reset')
call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a)
call assert_match('\nAll cscope databases reset', a)
" Test 16: cscope show
" Test: cscope show
let a = execute('cscope show')
call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
" Test 17: cstag and 'csto' option
" Test: cstag and 'csto' option
set csto=0
let a = execute('cstag TEST_COUNT')
call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a)
@@ -182,7 +187,7 @@ func Test_cscopeWithCscopeConnections()
call assert_fails('cstag xxx', 'E257:')
call assert_fails('cstag', 'E562:')
" Test 18: 'cst' option
" Test: 'cst' option
set nocst
call assert_fails('tag TEST_COUNT', 'E426:')
set cst
@@ -192,12 +197,24 @@ func Test_cscopeWithCscopeConnections()
let a = execute('tags')
call assert_match('1 1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a)
" Test 19: this should trigger call to cs_print_tags()
" Test: 'cscoperelative'
call mkdir('Xcscoperelative')
cd Xcscoperelative
let a = execute('cs find g test_mf_hash')
call assert_notequal('test_mf_hash(void)', getline('.'))
set cscoperelative
let a = execute('cs find g test_mf_hash')
call assert_equal('test_mf_hash(void)', getline('.'))
set nocscoperelative
cd ..
call delete('Xcscoperelative', 'd')
" Test: this should trigger call to cs_print_tags()
" Unclear how to check result though, we just exercise the code.
set cst cscopequickfix=s0
call feedkeys(":cs find s main\<CR>", 't')
" Test 20: cscope kill
" Test: cscope kill
call assert_fails('cscope kill 2', 'E261:')
call assert_fails('cscope kill xxx', 'E261:')
@@ -214,20 +231,20 @@ func Test_cscopeWithCscopeConnections()
let a = execute('cscope kill -1')
call assert_equal('', a)
" Test 21: 'csprg' option
" Test: 'csprg' option
call assert_equal('cscope', &csprg)
set csprg=doesnotexist
call assert_fails('cscope add Xcscope2.out', 'E609:')
set csprg=cscope
" Test 22: multiple cscope connections
" Test: multiple cscope connections
cscope add Xcscope.out
cscope add Xcscope2.out . -C
let a = execute('cscope show')
call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a)
" Test 23: test Ex command line completion
" Test: test Ex command line completion
call feedkeys(":cs \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"cs add find help kill reset show', @:)
@@ -243,19 +260,26 @@ func Test_cscopeWithCscopeConnections()
call feedkeys(":cs add Xcscope\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"cs add Xcscope.out Xcscope2.out', @:)
" Test 24: cscope_connection()
" Test: cscope_connection()
call assert_equal(cscope_connection(), 1)
call assert_equal(cscope_connection(0, 'out'), 1)
call assert_equal(cscope_connection(0, 'xxx'), 1)
call assert_equal(cscope_connection(1, 'out'), 1)
call assert_equal(cscope_connection(1, 'xxx'), 0)
call assert_equal(cscope_connection(2, 'out'), 0)
call assert_equal(cscope_connection(2, getcwd() .. '/Xcscope.out', 1), 1)
call assert_equal(cscope_connection(3, 'xxx', '..'), 0)
call assert_equal(cscope_connection(3, 'out', 'xxx'), 0)
call assert_equal(cscope_connection(3, 'out', '.'), 1)
call assert_equal(cscope_connection(4, 'out', '.'), 0)
" CleanUp
call assert_equal(cscope_connection(5, 'out'), 0)
call assert_equal(cscope_connection(-1, 'out'), 0)
call CscopeSetupOrClean(0)
endfunc

View File

@@ -146,9 +146,7 @@ endfunc
" In Ex-mode, backslashes at the end of a command should be halved.
func Test_Ex_echo_backslash()
" This test works only when the language is English
if v:lang != "C" && v:lang !~ '^[Ee]n'
return
endif
CheckEnglish
let bsl = '\\\\'
let bsl2 = '\\\'
call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")',

View File

@@ -84,6 +84,15 @@ func Test_expandcmd()
let $FOO="blue\tsky"
call setline(1, "$FOO")
call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>'))
" Test for expression expansion `=
let $FOO= "blue"
call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`"))
" Test for env variable with spaces
let $FOO= "foo bar baz"
call assert_equal("e foo bar baz", expandcmd("e $FOO"))
unlet $FOO
close!
endfunc

View File

@@ -659,6 +659,7 @@ func Save_mode()
return ''
endfunc
" Test for the mode() function
func Test_mode()
new
call append(0, ["Blue Ball Black", "Brown Band Bowl", ""])
@@ -782,6 +783,8 @@ func Test_mode()
call assert_equal('c-c', g:current_modes)
call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt')
call assert_equal('c-cv', g:current_modes)
call feedkeys("Qcall Save_mode()\<CR>vi\<CR>", 'xt')
call assert_equal('c-ce', g:current_modes)
" How to test Ex mode?
bwipe!
@@ -841,6 +844,16 @@ func Test_getbufvar()
call assert_equal('iso-8859-2', getbufvar(bufnr('%'), '&fenc'))
close
" Get the b: dict.
let b:testvar = 'one'
new
let b:testvar = 'two'
let thebuf = bufnr()
wincmd w
call assert_equal('two', getbufvar(thebuf, 'testvar'))
call assert_equal('two', getbufvar(thebuf, '').testvar)
bwipe!
set fileformats&
endfunc
@@ -1274,6 +1287,19 @@ func Test_inputlist()
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>3\<cr>", 'tx')
call assert_equal(3, c)
" Use backspace to delete characters in the prompt
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<BS>3\<BS>2\<cr>", 'tx')
call assert_equal(2, c)
" Use mouse to make a selection
call test_setmouse(&lines - 3, 2)
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
call assert_equal(1, c)
" Mouse click outside of the list
call test_setmouse(&lines - 6, 2)
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
call assert_equal(-2, c)
call assert_fails('call inputlist("")', 'E686:')
endfunc

View File

@@ -1,6 +1,7 @@
" Test for options
source check.vim
source view_util.vim
func Test_whichwrap()
set whichwrap=b,s
@@ -707,4 +708,12 @@ func Test_rightleftcmd()
set rightleft&
endfunc
" Test for the "debug" option
func Test_debug_option()
set debug=beep
exe "normal \<C-c>"
call assert_equal('Beep!', Screenline(&lines))
set debug&
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -2,6 +2,7 @@
source shared.vim
source screendump.vim
source term_util.vim
source check.vim
" Check that loading startup.vim works.
@@ -10,6 +11,9 @@ func Test_startup_script()
source $VIMRUNTIME/defaults.vim
call assert_equal(0, &compatible)
" Restore some options, so that the following tests doesn't break
set nomore
set noshowmode
endfunc
" Verify the order in which plugins are loaded:
@@ -322,6 +326,10 @@ func Test_q_arg()
\ lines)
endif
" Test with a non-existing error file (exits with value 3)
let out = system(GetVimCommand() .. ' -q xyz.err')
call assert_equal(3, v:shell_error)
call delete('Xtestout')
call delete('Xerrors')
endfunc
@@ -687,3 +695,268 @@ func Test_v_argv()
call assert_true(idx > 2)
call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:])
endfunc
" Test for the "-r" recovery mode option
func Test_r_arg()
" Can't catch the output of gvim.
CheckNotGui
CheckUnix
CheckEnglish
let cmd = GetVimCommand()
" There can be swap files anywhere, only check for the headers.
let expected =<< trim END
Swap files found:.*
In current directory:.*
In directory \~/tmp:.*
In directory /var/tmp:.*
In directory /tmp:.*
END
call assert_match(join(expected, ""), system(cmd .. " -r")->substitute("[\r\n]\\+", '', ''))
endfunc
" Test for the '-t' option to jump to a tag
func Test_t_arg()
let before =<< trim [CODE]
set tags=Xtags
[CODE]
let after =<< trim [CODE]
let s = bufname('') .. ':L' .. line('.') .. 'C' .. col('.')
call writefile([s], "Xtestout")
qall
[CODE]
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "first\tXfile1\t/^ \\zsfirst$/",
\ "second\tXfile1\t/^ \\zssecond$/",
\ "third\tXfile1\t/^ \\zsthird$/"],
\ 'Xtags')
call writefile([' first', ' second', ' third'], 'Xfile1')
if RunVim(before, after, '-t second')
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'))
call delete('Xtestout')
endif
call delete('Xtags')
call delete('Xfile1')
endfunc
" Test for entering the insert mode on startup
func Test_start_insertmode()
let before =<< trim [CODE]
set insertmode
[CODE]
let after =<< trim [CODE]
call writefile(['insertmode=' .. &insertmode], 'Xtestout')
qall
[CODE]
if RunVim(before, after, '')
call assert_equal(['insertmode=1'], readfile('Xtestout'))
call delete('Xtestout')
endif
endfunc
" Test for enabling the binary mode on startup
func Test_b_arg()
let after =<< trim [CODE]
call writefile(['binary=' .. &binary], 'Xtestout')
qall
[CODE]
if RunVim([], after, '-b')
call assert_equal(['binary=1'], readfile('Xtestout'))
call delete('Xtestout')
endif
endfunc
" Test for enabling the lisp mode on startup
func Test_l_arg()
let after =<< trim [CODE]
let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch
call writefile([s], 'Xtestout')
qall
[CODE]
if RunVim([], after, '-l')
call assert_equal(['lisp=1, showmatch=1'], readfile('Xtestout'))
call delete('Xtestout')
endif
endfunc
" Test for specifying a non-existing vimrc file using "-u"
func Test_missing_vimrc()
if !CanRunVimInTerminal()
throw 'Skipped: cannot run vim in terminal'
endif
let after =<< trim [CODE]
call assert_match('^E282:', v:errmsg)
call writefile(v:errors, 'Xtestout')
[CODE]
call writefile(after, 'Xafter')
let cmd = GetVimCommandCleanTerm() . ' -u Xvimrc_missing -S Xafter'
let buf = term_start(cmd, {'term_rows' : 10})
call WaitForAssert({-> assert_equal("running", term_getstatus(buf))})
call term_wait(buf)
call term_sendkeys(buf, "\n:")
call term_wait(buf)
call WaitForAssert({-> assert_match(':', term_getline(buf, 10))})
call StopVimInTerminal(buf)
call assert_equal([], readfile('Xtestout'))
call delete('Xafter')
call delete('Xtestout')
endfunc
" Test for using the $VIMINIT environment variable
func Test_VIMINIT()
let after =<< trim [CODE]
call assert_equal(1, exists('viminit_found'))
call assert_equal('yes', viminit_found)
call writefile(v:errors, 'Xtestout')
qall
[CODE]
call writefile(after, 'Xafter')
let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"'
call setenv('VIMINIT', 'let viminit_found="yes"')
exe "silent !" . cmd
call assert_equal([], readfile('Xtestout'))
call delete('Xtestout')
call delete('Xafter')
endfunc
" Test for using the $EXINIT environment variable
func Test_EXINIT()
let after =<< trim [CODE]
call assert_equal(1, exists('exinit_found'))
call assert_equal('yes', exinit_found)
call writefile(v:errors, 'Xtestout')
qall
[CODE]
call writefile(after, 'Xafter')
let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"'
call setenv('EXINIT', 'let exinit_found="yes"')
exe "silent !" . cmd
call assert_equal([], readfile('Xtestout'))
call delete('Xtestout')
call delete('Xafter')
endfunc
" Test for using the 'exrc' option
func Test_exrc()
let after =<< trim [CODE]
call assert_equal(1, &exrc)
call assert_equal(1, &secure)
call assert_equal(37, exrc_found)
call writefile(v:errors, 'Xtestout')
qall
[CODE]
call mkdir('Xdir')
call writefile(['let exrc_found=37'], 'Xdir/.exrc')
call writefile(after, 'Xdir/Xafter')
let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "cd Xdir" --cmd "set enc=utf8 exrc secure"'
exe "silent !" . cmd
call assert_equal([], readfile('Xdir/Xtestout'))
call delete('Xdir', 'rf')
endfunc
" Test for starting Vim with a non-terminal as input/output
func Test_io_not_a_terminal()
" Can't catch the output of gvim.
CheckNotGui
CheckUnix
CheckEnglish
let l = systemlist(GetVimProg() .. ' --ttyfail')
call assert_equal(['Vim: Warning: Output is not to a terminal',
\ 'Vim: Warning: Input is not from a terminal'], l)
endfunc
" Test for the "-w scriptout" argument
func Test_w_arg()
" Can't catch the output of gvim.
CheckNotGui
call writefile(["iVim Editor\<Esc>:q!\<CR>"], 'Xscriptin', 'b')
if RunVim([], [], '-s Xscriptin -w Xscriptout')
call assert_equal(["iVim Editor\e:q!\r"], readfile('Xscriptout'))
call delete('Xscriptout')
endif
call delete('Xscriptin')
" Test for failing to open the script output file. This test works only when
" the language is English.
if v:lang == "C" || v:lang =~ '^[Ee]n'
call mkdir("Xdir")
let m = system(GetVimCommand() .. " -w Xdir")
call assert_equal("Cannot open for script output: \"Xdir\"\n", m)
call delete("Xdir", 'rf')
endif
endfunc
" Test for the "-s scriptin" argument
func Test_s_arg()
" Can't catch the output of gvim.
CheckNotGui
CheckEnglish
" Test for failing to open the script input file.
let m = system(GetVimCommand() .. " -s abcxyz")
call assert_equal("Cannot open for reading: \"abcxyz\"\n", m)
call writefile([], 'Xinput')
let m = system(GetVimCommand() .. " -s Xinput -s Xinput")
call assert_equal("Attempt to open script file again: \"-s Xinput\"\n", m)
call delete('Xinput')
endfunc
" Test for the "-n" (no swap file) argument
func Test_n_arg()
let after =<< trim [CODE]
call assert_equal(0, &updatecount)
call writefile(v:errors, 'Xtestout')
qall
[CODE]
if RunVim([], after, '-n')
call assert_equal([], readfile('Xtestout'))
call delete('Xtestout')
endif
call delete('Xafter')
endfunc
" Test for the "-h" (help) argument
func Test_h_arg()
" Can't catch the output of gvim.
CheckNotGui
let l = systemlist(GetVimProg() .. ' -h')
call assert_match('^VIM - Vi IMproved', l[0])
let l = systemlist(GetVimProg() .. ' -?')
call assert_match('^VIM - Vi IMproved', l[0])
endfunc
" Test for the "-F" (farsi) argument
func Test_F_arg()
" Can't catch the output of gvim.
CheckNotGui
let l = systemlist(GetVimProg() .. ' -F')
call assert_match('^E27:', l[0])
endfunc
" Test for the "-E" (improved Ex mode) argument
func Test_E_arg()
let after =<< trim [CODE]
call assert_equal('cv', mode(1))
call writefile(v:errors, 'Xtestout')
qall
[CODE]
if RunVim([], after, '-E')
call assert_equal([], readfile('Xtestout'))
call delete('Xtestout')
endif
call delete('Xafter')
endfunc
" Test for too many edit argument errors
func Test_too_many_edit_args()
" Can't catch the output of gvim.
CheckNotGui
CheckEnglish
let l = systemlist(GetVimProg() .. ' - -')
call assert_match('^Too many edit arguments: "-"', l[1])
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -36,16 +36,6 @@ func Test_terminal_basic()
au TerminalOpen * let b:done = 'yes'
let buf = Run_shell_in_terminal({})
if has("unix")
call assert_match('^/dev/', job_info(g:job).tty_out)
call assert_match('^/dev/', term_gettty(''))
else
" ConPTY works on anonymous pipe.
if !has('conpty')
call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
call assert_match('^\\\\.\\pipe\\', ''->term_gettty())
endif
endif
call assert_equal('t', mode())
call assert_equal('yes', b:done)
call assert_match('%aR[^\n]*running]', execute('ls'))
@@ -2186,6 +2176,49 @@ func Test_term_gettitle()
exe term . 'bwipe!'
endfunc
func Test_term_gettty()
let buf = Run_shell_in_terminal({})
let gettty = term_gettty(buf)
if has('unix') && executable('tty')
" Find tty using the tty shell command.
call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
call term_sendkeys(buf, "tty\r")
call WaitForAssert({-> assert_notequal('', term_getline(buf, 3))})
let tty = term_getline(buf, 2)
call assert_equal(tty, gettty)
endif
let gettty0 = term_gettty(buf, 0)
let gettty1 = term_gettty(buf, 1)
call assert_equal(gettty, gettty0)
call assert_equal(job_info(g:job).tty_out, gettty0)
call assert_equal(job_info(g:job).tty_in, gettty1)
if has('unix')
" For unix, term_gettty(..., 0) and term_gettty(..., 1)
" are identical according to :help term_gettty()
call assert_equal(gettty0, gettty1)
call assert_match('^/dev/', gettty)
else
" ConPTY works on anonymous pipe.
if !has('conpty')
call assert_match('^\\\\.\\pipe\\', gettty0)
call assert_match('^\\\\.\\pipe\\', gettty1)
endif
endif
call assert_fails('call term_gettty(buf, 2)', 'E475:')
call assert_fails('call term_gettty(buf, -1)', 'E475:')
call assert_equal('', term_gettty(buf + 1))
call StopShellInTerminal(buf)
call term_wait(buf)
exe buf . 'bwipe'
endfunc
" When drawing the statusline the cursor position may not have been updated
" yet.
" 1. create a terminal, make it show 2 lines

View File

@@ -1026,6 +1026,41 @@ func Test_comment_nested()
%bw!
endfunc
" Test for a space character in 'comments' setting
func Test_comment_space()
new
setlocal comments=b:\ > fo+=ro
exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
let expected =<< trim END
A
> B
C
D
> E
> F
> G
> H
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for the 'O' flag in 'comments'
func Test_comment_O()
new
setlocal comments=Ob:* fo+=ro
exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
let expected =<< trim END
A
* B
* C
* D
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for 'a' and 'w' flags in 'formatoptions'
func Test_fo_a_w()
new
@@ -1035,4 +1070,23 @@ func Test_fo_a_w()
%bw!
endfunc
" Test for 'j' flag in 'formatoptions'
func Test_fo_j()
new
setlocal fo+=j comments=://
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 comment2', getline(1))
setlocal fo-=j
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 // comment2', getline(1))
" Test with nested comments
setlocal fo+=j comments=n:>,n:)
call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
normal J
call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -2012,9 +2012,7 @@ endfunc
" Test for verbose messages with :try :catch, and :finally {{{1
func Test_try_catch_verbose()
" This test works only when the language is English
if v:lang != "C" && v:lang !~ '^[Ee]n'
return
endif
CheckEnglish
set verbose=14

View File

@@ -87,8 +87,10 @@ enddef
def Test_disassemble_store()
let res = execute('disass s:ScriptFuncStore')
assert_match('<SNR>\d*_ScriptFuncStore.*'
\ .. 'let localnr = 1.*'
\ .. 'localnr = 2.*'
\ .. ' STORE 2 in $0.*'
\ .. 'let localstr = ''abc''.*'
\ .. 'localstr = ''xyz''.*'
\ .. ' STORE $1.*'
\ .. 'v:char = ''abc''.*'
@@ -360,31 +362,27 @@ def Test_disassemble_const_expr()
enddef
def WithFunc()
let funky1: func
let funky2: func = function("len")
let party1: partial
let party2: partial = funcref("UserFunc")
let Funky1: func
let Funky2: func = function("len")
let Party2: func = funcref("UserFunc")
enddef
def Test_disassemble_function()
let instr = execute('disassemble WithFunc')
assert_match('WithFunc.*'
\ .. 'let funky1: func.*'
\ .. 'let Funky1: func.*'
\ .. '0 PUSHFUNC "\[none]".*'
\ .. '1 STORE $0.*'
\ .. 'let funky2: func = function("len").*'
\ .. 'let Funky2: func = function("len").*'
\ .. '2 PUSHS "len".*'
\ .. '3 BCALL function(argc 1).*'
\ .. '4 STORE $1.*'
\ .. 'let party1: partial.*'
\ .. '5 PUSHPARTIAL "\[none]".*'
\ .. '6 STORE $2.*'
\ .. 'let party2: partial = funcref("UserFunc").*'
\ .. '7 PUSHS "UserFunc".*'
\ .. '8 BCALL funcref(argc 1).*'
\ .. '9 STORE $3.*'
\ .. '10 PUSHNR 0.*'
\ .. '11 RETURN.*'
\ .. 'let Party2: func = funcref("UserFunc").*'
\ .. '\d PUSHS "UserFunc".*'
\ .. '\d BCALL funcref(argc 1).*'
\ .. '\d STORE $2.*'
\ .. '\d PUSHNR 0.*'
\ .. '\d RETURN.*'
\, instr)
enddef
@@ -753,10 +751,10 @@ def Test_disassemble_compare()
\ ['#{a:1} is #{x:2}', 'COMPAREDICT is'],
\ ['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
\
\ ['{->33} == {->44}', 'COMPAREPARTIAL =='],
\ ['{->33} != {->44}', 'COMPAREPARTIAL !='],
\ ['{->33} is {->44}', 'COMPAREPARTIAL is'],
\ ['{->33} isnot {->44}', 'COMPAREPARTIAL isnot'],
\ ['{->33} == {->44}', 'COMPAREFUNC =='],
\ ['{->33} != {->44}', 'COMPAREFUNC !='],
\ ['{->33} is {->44}', 'COMPAREFUNC is'],
\ ['{->33} isnot {->44}', 'COMPAREFUNC isnot'],
\
\ ['77 == g:xx', 'COMPAREANY =='],
\ ['77 != g:xx', 'COMPAREANY !='],

View File

@@ -460,8 +460,8 @@ func Test_expr4_fails()
call CheckDefFailureMult(['let j: job', 'let chan: channel', 'let r = j == chan'], 'Cannot compare job with channel')
call CheckDefFailureMult(['let j: job', 'let x: list<any>', 'let r = j == x'], 'Cannot compare job with list')
call CheckDefFailureMult(['let j: job', 'let x: func', 'let r = j == x'], 'Cannot compare job with func')
call CheckDefFailureMult(['let j: job', 'let x: partial', 'let r = j == x'], 'Cannot compare job with partial')
call CheckDefFailureMult(['let j: job', 'let Xx: func', 'let r = j == Xx'], 'Cannot compare job with func')
call CheckDefFailureMult(['let j: job', 'let Xx: func', 'let r = j == Xx'], 'Cannot compare job with func')
endfunc
" test addition, subtraction, concatenation

View File

@@ -0,0 +1,400 @@
" Test various aspects of the Vim9 script language.
source check.vim
source view_util.vim
" Check that "lines" inside ":def" results in an "error" message.
func CheckDefFailure(lines, error)
call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef')
call assert_fails('so Xdef', a:error, a:lines)
call delete('Xdef')
endfunc
func CheckScriptFailure(lines, error)
call writefile(a:lines, 'Xdef')
call assert_fails('so Xdef', a:error, a:lines)
call delete('Xdef')
endfunc
func Test_def_basic()
def SomeFunc(): string
return 'yes'
enddef
call assert_equal('yes', SomeFunc())
endfunc
def ReturnString(): string
return 'string'
enddef
def ReturnNumber(): number
return 123
enddef
let g:notNumber = 'string'
def ReturnGlobal(): number
return g:notNumber
enddef
def Test_return_something()
assert_equal('string', ReturnString())
assert_equal(123, ReturnNumber())
assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string')
enddef
let s:nothing = 0
def ReturnNothing()
s:nothing = 1
if true
return
endif
s:nothing = 2
enddef
def Test_return_nothing()
ReturnNothing()
assert_equal(1, s:nothing)
enddef
func Increment()
let g:counter += 1
endfunc
def Test_call_ufunc_count()
g:counter = 1
Increment()
Increment()
Increment()
" works with and without :call
assert_equal(4, g:counter)
call assert_equal(4, g:counter)
unlet g:counter
enddef
def MyVarargs(arg: string, ...rest: list<string>): string
let res = arg
for s in rest
res ..= ',' .. s
endfor
return res
enddef
def Test_call_varargs()
assert_equal('one', MyVarargs('one'))
assert_equal('one,two', MyVarargs('one', 'two'))
assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef
def MyDefaultArgs(name = 'string'): string
return name
enddef
def Test_call_default_args()
assert_equal('string', MyDefaultArgs())
assert_equal('one', MyDefaultArgs('one'))
assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
call CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef'], 'E1001:')
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
func TakesOneArg(arg)
echo a:arg
endfunc
def Test_call_wrong_args()
call CheckDefFailure(['TakesOneArg()'], 'E119:')
call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:')
call CheckDefFailure(['bufnr(xxx)'], 'E1001:')
enddef
" Default arg and varargs
def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
let res = one .. ',' .. two
for s in rest
res ..= ',' .. s
endfor
return res
enddef
def Test_call_def_varargs()
call assert_fails('call MyDefVarargs()', 'E119:')
assert_equal('one,foo', MyDefVarargs('one'))
assert_equal('one,two', MyDefVarargs('one', 'two'))
assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
enddef
def Test_using_var_as_arg()
call writefile(['def Func(x: number)', 'let x = 234', 'enddef'], 'Xdef')
call assert_fails('so Xdef', 'E1006:')
call delete('Xdef')
enddef
def Test_call_func_defined_later()
call assert_equal('one', DefinedLater('one'))
call assert_fails('call NotDefined("one")', 'E117:')
enddef
func DefinedLater(arg)
return a:arg
endfunc
def FuncWithForwardCall()
return DefinedEvenLater("yes")
enddef
def DefinedEvenLater(arg: string): string
return arg
enddef
def Test_error_in_nested_function()
" Error in called function requires unwinding the call stack.
assert_fails('call FuncWithForwardCall()', 'E1029')
enddef
def Test_return_type_wrong()
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string')
CheckScriptFailure(['def Func(): number', 'return', 'enddef'], 'E1003:')
CheckScriptFailure(['def Func(): list', 'return []', 'enddef'], 'E1008:')
CheckScriptFailure(['def Func(): dict', 'return {}', 'enddef'], 'E1008:')
enddef
def Test_arg_type_wrong()
CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
enddef
def Test_vim9script_call()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
var = arg
enddef
MyFunc('foobar')
assert_equal('foobar', var)
let str = 'barfoo'
str->MyFunc()
assert_equal('barfoo', var)
let g:value = 'value'
g:value->MyFunc()
assert_equal('value', var)
let listvar = []
def ListFunc(arg: list<number>)
listvar = arg
enddef
[1, 2, 3]->ListFunc()
assert_equal([1, 2, 3], listvar)
let dictvar = {}
def DictFunc(arg: dict<number>)
dictvar = arg
enddef
{'a': 1, 'b': 2}->DictFunc()
assert_equal(#{a: 1, b: 2}, dictvar)
def CompiledDict()
{'a': 3, 'b': 4}->DictFunc()
enddef
CompiledDict()
assert_equal(#{a: 3, b: 4}, dictvar)
#{a: 3, b: 4}->DictFunc()
assert_equal(#{a: 3, b: 4}, dictvar)
('text')->MyFunc()
assert_equal('text', var)
("some")->MyFunc()
assert_equal('some', var)
END
writefile(lines, 'Xcall.vim')
source Xcall.vim
delete('Xcall.vim')
enddef
def Test_vim9script_call_fail_decl()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
let var = 123
enddef
END
writefile(lines, 'Xcall_decl.vim')
assert_fails('source Xcall_decl.vim', 'E1054:')
delete('Xcall_decl.vim')
enddef
def Test_vim9script_call_fail_const()
let lines =<< trim END
vim9script
const var = ''
def MyFunc(arg: string)
var = 'asdf'
enddef
END
writefile(lines, 'Xcall_const.vim')
assert_fails('source Xcall_const.vim', 'E46:')
delete('Xcall_const.vim')
enddef
" Test that inside :function a Python function can be defined, :def is not
" recognized.
func Test_function_python()
CheckFeature python3
let py = 'python3'
execute py "<< EOF"
def do_something():
return 1
EOF
endfunc
def Test_delfunc()
let lines =<< trim END
vim9script
def GoneSoon()
echo 'hello'
enddef
def CallGoneSoon()
GoneSoon()
enddef
delfunc GoneSoon
CallGoneSoon()
END
writefile(lines, 'XToDelFunc')
assert_fails('so XToDelFunc', 'E933')
assert_fails('so XToDelFunc', 'E933')
delete('XToDelFunc')
enddef
def Test_redef_failure()
call writefile(['def Func0(): string', 'return "Func0"', 'enddef'], 'Xdef')
so Xdef
call writefile(['def Func1(): string', 'return "Func1"', 'enddef'], 'Xdef')
so Xdef
call writefile(['def! Func0(): string', 'enddef'], 'Xdef')
call assert_fails('so Xdef', 'E1027:')
call writefile(['def Func2(): string', 'return "Func2"', 'enddef'], 'Xdef')
so Xdef
call delete('Xdef')
call assert_equal(0, Func0())
call assert_equal('Func1', Func1())
call assert_equal('Func2', Func2())
delfunc! Func0
delfunc! Func1
delfunc! Func2
enddef
" Test for internal functions returning different types
func Test_InternalFuncRetType()
let lines =<< trim END
def RetFloat(): float
return ceil(1.456)
enddef
def RetListAny(): list<any>
return items({'k' : 'v'})
enddef
def RetListString(): list<string>
return split('a:b:c', ':')
enddef
def RetListDictAny(): list<dict<any>>
return getbufinfo()
enddef
def RetDictNumber(): dict<number>
return wordcount()
enddef
def RetDictString(): dict<string>
return environ()
enddef
END
call writefile(lines, 'Xscript')
source Xscript
call assert_equal(2.0, RetFloat())
call assert_equal([['k', 'v']], RetListAny())
call assert_equal(['a', 'b', 'c'], RetListString())
call assert_notequal([], RetListDictAny())
call assert_notequal({}, RetDictNumber())
call assert_notequal({}, RetDictString())
call delete('Xscript')
endfunc
" Test for passing too many or too few arguments to internal functions
func Test_internalfunc_arg_error()
let l =<< trim END
def! FArgErr(): float
return ceil(1.1, 2)
enddef
END
call writefile(l, 'Xinvalidarg')
call assert_fails('so Xinvalidarg', 'E118:')
let l =<< trim END
def! FArgErr(): float
return ceil()
enddef
END
call writefile(l, 'Xinvalidarg')
call assert_fails('so Xinvalidarg', 'E119:')
call delete('Xinvalidarg')
endfunc
let s:funcResult = 0
def FuncNoArgNoRet()
funcResult = 11
enddef
def FuncNoArgRetNumber(): number
funcResult = 22
return 1234
enddef
def FuncOneArgNoRet(arg: number)
funcResult = arg
enddef
def FuncOneArgRetNumber(arg: number): number
funcResult = arg
return arg
enddef
def Test_func_type()
let Ref1: func()
funcResult = 0
Ref1 = FuncNoArgNoRet
Ref1()
assert_equal(11, funcResult)
enddef
def Test_func_type_fails()
CheckDefFailure(['let ref1: func()'], 'E704:')
CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1013: type mismatch, expected func() but got func(): number')
CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1013: type mismatch, expected func() but got func(number)')
CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1013: type mismatch, expected func() but got func(number): number')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -21,13 +21,6 @@ def Test_syntax()
let other: list<string> = ['asdf']
enddef
func Test_def_basic()
def SomeFunc(): string
return 'yes'
enddef
call assert_equal('yes', SomeFunc())
endfunc
let s:appendToMe = 'xxx'
let s:addToMe = 111
let g:existing = 'yes'
@@ -66,10 +59,9 @@ def Test_assignment()
if has('float')
let float1: float = 3.4
endif
let funky1: func
let funky2: func = function('len')
let party1: partial
let party2: partial = funcref('Test_syntax')
let Funky1: func
let Funky2: func = function('len')
let Party2: func = funcref('Test_syntax')
" type becomes list<any>
let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
@@ -112,6 +104,8 @@ def Test_assignment()
call CheckDefFailure(['&notex += 3'], 'E113:')
call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
call CheckDefFailure(['&path += 3'], 'E1013:')
" test freeing ISN_STOREOPT
call CheckDefFailure(['&ts = 3', 'let asdf'], 'E1022:')
&ts = 8
g:inc_counter += 1
@@ -133,6 +127,9 @@ def Test_assignment()
assert_equal('noneagain', v:errmsg)
call CheckDefFailure(['v:errmsg += "more"'], 'E1013:')
call CheckDefFailure(['v:errmsg += 123'], 'E1013:')
enddef
def Test_assignment_default()
" Test default values.
let thebool: bool
@@ -152,11 +149,8 @@ def Test_assignment()
let theblob: blob
assert_equal(0z, theblob)
let thefunc: func
assert_equal(test_null_function(), thefunc)
let thepartial: partial
assert_equal(test_null_partial(), thepartial)
let Thefunc: func
assert_equal(test_null_function(), Thefunc)
let thelist: list<any>
assert_equal([], thelist)
@@ -171,6 +165,9 @@ def Test_assignment()
let thechannel: channel
assert_equal(test_null_channel(), thechannel)
endif
let nr = 1234 | nr = 5678
assert_equal(5678, nr)
enddef
func Test_assignment_failure()
@@ -208,7 +205,7 @@ func Test_assignment_failure()
call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
call CheckDefFailure(['let var: dict <number>'], 'E1007:')
call CheckDefFailure(['let var: dict <number>'], 'E1068:')
call CheckDefFailure(['let var: dict<number'], 'E1009:')
endfunc
@@ -253,156 +250,15 @@ enddef
func Test_block_failure()
call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
call CheckDefFailure(['}'], 'E1025:')
call CheckDefFailure(['{', 'echo 1'], 'E1026:')
endfunc
def ReturnString(): string
return 'string'
def Test_cmd_modifier()
tab echo '0'
call CheckDefFailure(['5tab echo 3'], 'E16:')
enddef
def ReturnNumber(): number
return 123
enddef
let g:notNumber = 'string'
def ReturnGlobal(): number
return g:notNumber
enddef
def Test_return_something()
assert_equal('string', ReturnString())
assert_equal(123, ReturnNumber())
assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string')
enddef
let s:nothing = 0
def ReturnNothing()
s:nothing = 1
if true
return
endif
s:nothing = 2
enddef
def Test_return_nothing()
ReturnNothing()
assert_equal(1, s:nothing)
enddef
func Increment()
let g:counter += 1
endfunc
def Test_call_ufunc_count()
g:counter = 1
Increment()
Increment()
Increment()
" works with and without :call
assert_equal(4, g:counter)
call assert_equal(4, g:counter)
unlet g:counter
enddef
def MyVarargs(arg: string, ...rest: list<string>): string
let res = arg
for s in rest
res ..= ',' .. s
endfor
return res
enddef
def Test_call_varargs()
assert_equal('one', MyVarargs('one'))
assert_equal('one,two', MyVarargs('one', 'two'))
assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef
def MyDefaultArgs(name = 'string'): string
return name
enddef
def Test_call_default_args()
assert_equal('string', MyDefaultArgs())
assert_equal('one', MyDefaultArgs('one'))
assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
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
func TakesOneArg(arg)
echo a:arg
endfunc
def Test_call_wrong_args()
call CheckDefFailure(['TakesOneArg()'], 'E119:')
call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:')
call CheckDefFailure(['bufnr(xxx)'], 'E1001:')
enddef
" Default arg and varargs
def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
let res = one .. ',' .. two
for s in rest
res ..= ',' .. s
endfor
return res
enddef
def Test_call_def_varargs()
call assert_fails('call MyDefVarargs()', 'E119:')
assert_equal('one,foo', MyDefVarargs('one'))
assert_equal('one,two', MyDefVarargs('one', 'two'))
assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
enddef
def Test_using_var_as_arg()
call writefile(['def Func(x: number)', 'let x = 234', 'enddef'], 'Xdef')
call assert_fails('so Xdef', 'E1006:')
call delete('Xdef')
enddef
def Test_call_func_defined_later()
call assert_equal('one', DefinedLater('one'))
call assert_fails('call NotDefined("one")', 'E117:')
enddef
func DefinedLater(arg)
return a:arg
endfunc
def FuncWithForwardCall()
return DefinedEvenLater("yes")
enddef
def DefinedEvenLater(arg: string): string
return arg
enddef
def Test_error_in_nested_function()
" Error in called function requires unwinding the call stack.
assert_fails('call FuncWithForwardCall()', 'E1029')
enddef
def Test_return_type_wrong()
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string')
CheckScriptFailure(['def Func(): number', 'return', 'enddef'], 'E1003:')
CheckScriptFailure(['def Func(): list', 'return []', 'enddef'], 'E1008:')
CheckScriptFailure(['def Func(): dict', 'return {}', 'enddef'], 'E1008:')
enddef
def Test_arg_type_wrong()
CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
enddef
def Test_try_catch()
let l = []
@@ -480,6 +336,16 @@ def Test_try_catch_fails()
call CheckDefFailure(['catch'], 'E603:')
call CheckDefFailure(['try', 'echo 0', 'catch','catch'], 'E1033:')
call CheckDefFailure(['try', 'echo 0', 'catch /pat'], 'E1067:')
call CheckDefFailure(['finally'], 'E606:')
call CheckDefFailure(['try', 'echo 0', 'finally', 'echo 1', 'finally'], 'E607:')
call CheckDefFailure(['endtry'], 'E602:')
call CheckDefFailure(['while 1', 'endtry'], 'E170:')
call CheckDefFailure(['for i in range(5)', 'endtry'], 'E170:')
call CheckDefFailure(['if 2', 'endtry'], 'E171:')
call CheckDefFailure(['try', 'echo 1', 'endtry'], 'E1032:')
call CheckDefFailure(['throw'], 'E471:')
call CheckDefFailure(['throw xxx'], 'E1001:')
enddef
let s:export_script_lines =<< trim END
@@ -711,82 +577,6 @@ def Test_vim9script_fails()
assert_fails('export something', 'E1042')
enddef
def Test_vim9script_call()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
var = arg
enddef
MyFunc('foobar')
assert_equal('foobar', var)
let str = 'barfoo'
str->MyFunc()
assert_equal('barfoo', var)
let g:value = 'value'
g:value->MyFunc()
assert_equal('value', var)
let listvar = []
def ListFunc(arg: list<number>)
listvar = arg
enddef
[1, 2, 3]->ListFunc()
assert_equal([1, 2, 3], listvar)
let dictvar = {}
def DictFunc(arg: dict<number>)
dictvar = arg
enddef
{'a': 1, 'b': 2}->DictFunc()
assert_equal(#{a: 1, b: 2}, dictvar)
def CompiledDict()
{'a': 3, 'b': 4}->DictFunc()
enddef
CompiledDict()
assert_equal(#{a: 3, b: 4}, dictvar)
#{a: 3, b: 4}->DictFunc()
assert_equal(#{a: 3, b: 4}, dictvar)
('text')->MyFunc()
assert_equal('text', var)
("some")->MyFunc()
assert_equal('some', var)
END
writefile(lines, 'Xcall.vim')
source Xcall.vim
delete('Xcall.vim')
enddef
def Test_vim9script_call_fail_decl()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
let var = 123
enddef
END
writefile(lines, 'Xcall_decl.vim')
assert_fails('source Xcall_decl.vim', 'E1054:')
delete('Xcall_decl.vim')
enddef
def Test_vim9script_call_fail_const()
let lines =<< trim END
vim9script
const var = ''
def MyFunc(arg: string)
var = 'asdf'
enddef
END
writefile(lines, 'Xcall_const.vim')
assert_fails('source Xcall_const.vim', 'E46:')
delete('Xcall_const.vim')
enddef
def Test_vim9script_reload()
let lines =<< trim END
vim9script
@@ -905,17 +695,6 @@ def Test_fixed_size_list()
assert_equal([2, 99, 3, 4, 5], l)
enddef
" Test that inside :function a Python function can be defined, :def is not
" recognized.
func Test_function_python()
CheckFeature python3
let py = 'python3'
execute py "<< EOF"
def do_something():
return 1
EOF
endfunc
def IfElse(what: number): string
let res = ''
if what == 1
@@ -939,6 +718,7 @@ def Test_if_elseif_else_fails()
call CheckDefFailure(['else'], 'E581:')
call CheckDefFailure(['endif'], 'E580:')
call CheckDefFailure(['if true', 'elseif xxx'], 'E1001:')
call CheckDefFailure(['if true', 'echo 1'], 'E171:')
enddef
let g:bool_true = v:true
@@ -951,6 +731,16 @@ def Test_if_const_expr()
endif
assert_equal(true, res)
g:glob = 2
if false
execute('let g:glob = 3')
endif
assert_equal(2, g:glob)
if true
execute('let g:glob = 3')
endif
assert_equal(3, g:glob)
res = false
if g:bool_true ? true : false
res = true
@@ -1055,27 +845,6 @@ def Test_if_const_expr_fails()
call CheckDefFailure(["if has('aaa') ? true false"], 'E109:')
enddef
def Test_delfunc()
let lines =<< trim END
vim9script
def GoneSoon()
echo 'hello'
enddef
def CallGoneSoon()
GoneSoon()
enddef
delfunc GoneSoon
CallGoneSoon()
END
writefile(lines, 'XToDelFunc')
assert_fails('so XToDelFunc', 'E933')
assert_fails('so XToDelFunc', 'E933')
delete('XToDelFunc')
enddef
def Test_execute_cmd()
new
setline(1, 'default')
@@ -1092,10 +861,13 @@ def Test_execute_cmd()
execute cmd_first .. cmd_last
assert_equal('execute-var-var', getline(1))
bwipe!
call CheckDefFailure(['execute xxx'], 'E1001:')
enddef
def Test_echo_cmd()
echo 'something'
echo 'some'
echon 'thing'
assert_match('^something$', Screenline(&lines))
let str1 = 'some'
@@ -1141,6 +913,7 @@ def Test_for_loop_fails()
call CheckDefFailure(['for i in "text"'], 'E1024:')
call CheckDefFailure(['for i in xxx'], 'E1001:')
call CheckDefFailure(['endfor'], 'E588:')
call CheckDefFailure(['for i in range(3)', 'echo 3'], 'E170:')
enddef
def Test_while_loop()
@@ -1166,6 +939,7 @@ def Test_while_loop_fails()
call CheckDefFailure(['if true', 'continue'], 'E586:')
call CheckDefFailure(['break'], 'E587:')
call CheckDefFailure(['if true', 'break'], 'E587:')
call CheckDefFailure(['while 1', 'echo 3'], 'E170:')
enddef
def Test_interrupt_loop()
@@ -1185,6 +959,8 @@ def Test_interrupt_loop()
assert_true(caught, 'should have caught an exception')
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
setline(1, 'something')
@@ -1207,82 +983,4 @@ def Test_substitute_cmd()
delete('Xvim9lines')
enddef
def Test_redef_failure()
call writefile(['def Func0(): string', 'return "Func0"', 'enddef'], 'Xdef')
so Xdef
call writefile(['def Func1(): string', 'return "Func1"', 'enddef'], 'Xdef')
so Xdef
call writefile(['def! Func0(): string', 'enddef'], 'Xdef')
call assert_fails('so Xdef', 'E1027:')
call writefile(['def Func2(): string', 'return "Func2"', 'enddef'], 'Xdef')
so Xdef
call delete('Xdef')
call assert_equal(0, Func0())
call assert_equal('Func1', Func1())
call assert_equal('Func2', Func2())
delfunc! Func0
delfunc! Func1
delfunc! Func2
enddef
" Test for internal functions returning different types
func Test_InternalFuncRetType()
let lines =<< trim END
def RetFloat(): float
return ceil(1.456)
enddef
def RetListAny(): list<any>
return items({'k' : 'v'})
enddef
def RetListString(): list<string>
return split('a:b:c', ':')
enddef
def RetListDictAny(): list<dict<any>>
return getbufinfo()
enddef
def RetDictNumber(): dict<number>
return wordcount()
enddef
def RetDictString(): dict<string>
return environ()
enddef
END
call writefile(lines, 'Xscript')
source Xscript
call assert_equal(2.0, RetFloat())
call assert_equal([['k', 'v']], RetListAny())
call assert_equal(['a', 'b', 'c'], RetListString())
call assert_notequal([], RetListDictAny())
call assert_notequal({}, RetDictNumber())
call assert_notequal({}, RetDictString())
call delete('Xscript')
endfunc
" Test for passing too many or too few arguments to internal functions
func Test_internalfunc_arg_error()
let l =<< trim END
def! FArgErr(): float
return ceil(1.1, 2)
enddef
END
call writefile(l, 'Xinvalidarg')
call assert_fails('so Xinvalidarg', 'E118:')
let l =<< trim END
def! FArgErr(): float
return ceil()
enddef
END
call writefile(l, 'Xinvalidarg')
call assert_fails('so Xinvalidarg', 'E119:')
call delete('Xinvalidarg')
endfunc
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -760,6 +760,11 @@ func Test_viminfo_perm()
call setfperm('Xviminfo', '--x------')
call assert_fails('rviminfo Xviminfo', 'E195:')
call delete('Xviminfo')
" Try to write the viminfo to a directory
call mkdir('Xdir')
call assert_fails('wviminfo Xdir', 'E886:')
call delete('Xdir', 'rf')
endfunc
" Test for writing to an existing viminfo file merges the file marks

View File

@@ -951,6 +951,9 @@ func_clear_items(ufunc_T *fp)
VIM_CLEAR(fp->uf_arg_types);
VIM_CLEAR(fp->uf_def_arg_idx);
VIM_CLEAR(fp->uf_va_name);
while (fp->uf_type_list.ga_len > 0)
vim_free(((type_T **)fp->uf_type_list.ga_data)
[--fp->uf_type_list.ga_len]);
ga_clear(&fp->uf_type_list);
#ifdef FEAT_PROFILE
VIM_CLEAR(fp->uf_tml_count);
@@ -1321,7 +1324,7 @@ call_user_func(
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
++ex_nesting_level;
eval1(&p, rettv, TRUE);
(void)eval1(&p, rettv, TRUE);
--ex_nesting_level;
}
else
@@ -3013,6 +3016,7 @@ ex_function(exarg_T *eap)
fp->uf_args = newargs;
fp->uf_def_args = default_args;
fp->uf_ret_type = &t_any;
fp->uf_func_type = &t_func_any;
if (eap->cmdidx == CMD_def)
{
@@ -3022,7 +3026,7 @@ ex_function(exarg_T *eap)
SOURCING_LNUM = sourcing_lnum_top;
// parse the argument types
ga_init2(&fp->uf_type_list, sizeof(type_T), 5);
ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
if (argtypes.ga_len > 0)
{

View File

@@ -738,6 +738,28 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
513,
/**/
512,
/**/
511,
/**/
510,
/**/
509,
/**/
508,
/**/
507,
/**/
506,
/**/
505,
/**/
504,
/**/
503,
/**/
502,
/**/

View File

@@ -1751,6 +1751,7 @@ void *vim_memset(void *, int, size_t);
# define INIT3(a, b, c)
# define INIT4(a, b, c, d)
# define INIT5(a, b, c, d, e)
# define INIT6(a, b, c, d, e, f)
#else
# ifndef INIT
# define INIT(x) x
@@ -1758,6 +1759,7 @@ void *vim_memset(void *, int, size_t);
# define INIT3(a, b, c) = {a, b, c}
# define INIT4(a, b, c, d) = {a, b, c, d}
# define INIT5(a, b, c, d, e) = {a, b, c, d, e}
# define INIT6(a, b, c, d, e, f) = {a, b, c, d, e, f}
# define DO_INIT
# endif
#endif

View File

@@ -29,8 +29,8 @@ typedef enum {
ISN_STORE, // pop into local variable isn_arg.number
ISN_STOREV, // pop into v: variable isn_arg.number
ISN_STOREG, // pop into global variable isn_arg.string
ISN_STORES, // pop into scirpt variable isn_arg.loadstore
ISN_STORESCRIPT, // pop into scirpt variable isn_arg.script
ISN_STORES, // pop into script variable isn_arg.loadstore
ISN_STORESCRIPT, // pop into script variable isn_arg.script
ISN_STOREOPT, // pop into option isn_arg.string
ISN_STOREENV, // pop into environment variable isn_arg.string
ISN_STOREREG, // pop into register isn_arg.number
@@ -191,7 +191,7 @@ typedef struct {
// arguments to ISN_LOADS and ISN_STORES
typedef struct {
char_u *ls_name; // variable name
char_u *ls_name; // variable name (with s: for ISN_STORES)
int ls_sid; // script ID
} loadstore_T;

View File

@@ -120,7 +120,7 @@ struct cctx_S {
scope_T *ctx_scope; // current scope, NULL at toplevel
garray_T ctx_type_stack; // type of each item on the stack
garray_T *ctx_type_list; // space for adding types
garray_T *ctx_type_list; // list of pointers to allocated types
};
static char e_var_notfound[] = N_("E1001: variable not found: %s");
@@ -223,8 +223,28 @@ check_defined(char_u *p, int len, cctx_T *cctx)
return OK;
}
/*
* Allocate memory for a type_T and add the pointer to type_gap, so that it can
* be freed later.
*/
static type_T *
get_list_type(type_T *member_type, garray_T *type_list)
alloc_type(garray_T *type_gap)
{
type_T *type;
if (ga_grow(type_gap, 1) == FAIL)
return NULL;
type = ALLOC_CLEAR_ONE(type_T);
if (type != NULL)
{
((type_T **)type_gap->ga_data)[type_gap->ga_len] = type;
++type_gap->ga_len;
}
return type;
}
static type_T *
get_list_type(type_T *member_type, garray_T *type_gap)
{
type_T *type;
@@ -241,17 +261,18 @@ get_list_type(type_T *member_type, garray_T *type_list)
return &t_list_string;
// Not a common type, create a new entry.
if (ga_grow(type_list, 1) == FAIL)
type = alloc_type(type_gap);
if (type == NULL)
return &t_any;
type = ((type_T *)type_list->ga_data) + type_list->ga_len;
++type_list->ga_len;
type->tt_type = VAR_LIST;
type->tt_member = member_type;
type->tt_argcount = 0;
type->tt_args = NULL;
return type;
}
static type_T *
get_dict_type(type_T *member_type, garray_T *type_list)
get_dict_type(type_T *member_type, garray_T *type_gap)
{
type_T *type;
@@ -268,15 +289,93 @@ get_dict_type(type_T *member_type, garray_T *type_list)
return &t_dict_string;
// Not a common type, create a new entry.
if (ga_grow(type_list, 1) == FAIL)
type = alloc_type(type_gap);
if (type == NULL)
return &t_any;
type = ((type_T *)type_list->ga_data) + type_list->ga_len;
++type_list->ga_len;
type->tt_type = VAR_DICT;
type->tt_member = member_type;
type->tt_argcount = 0;
type->tt_args = NULL;
return type;
}
/*
* Get a function type, based on the return type "ret_type".
* If "argcount" is -1 or 0 a predefined type can be used.
* If "argcount" > 0 always create a new type, so that arguments can be added.
*/
static type_T *
get_func_type(type_T *ret_type, int argcount, garray_T *type_gap)
{
type_T *type;
// recognize commonly used types
if (argcount <= 0)
{
if (ret_type == &t_void)
{
if (argcount == 0)
return &t_func_0_void;
else
return &t_func_void;
}
if (ret_type == &t_any)
{
if (argcount == 0)
return &t_func_0_any;
else
return &t_func_any;
}
if (ret_type == &t_number)
{
if (argcount == 0)
return &t_func_0_number;
else
return &t_func_number;
}
if (ret_type == &t_string)
{
if (argcount == 0)
return &t_func_0_string;
else
return &t_func_string;
}
}
// Not a common type or has arguments, create a new entry.
type = alloc_type(type_gap);
if (type == NULL)
return &t_any;
type->tt_type = VAR_FUNC;
type->tt_member = ret_type;
type->tt_args = NULL;
return type;
}
/*
* For a function type, reserve space for "argcount" argument types (including
* vararg).
*/
static int
func_type_add_arg_types(
type_T *functype,
int argcount,
int min_argcount,
garray_T *type_gap)
{
if (ga_grow(type_gap, 1) == FAIL)
return FAIL;
functype->tt_args = ALLOC_CLEAR_MULT(type_T *, argcount);
if (functype->tt_args == NULL)
return FAIL;
((type_T **)type_gap->ga_data)[type_gap->ga_len] = (void *)functype->tt_args;
++type_gap->ga_len;
functype->tt_argcount = argcount;
functype->tt_min_argcount = min_argcount;
return OK;
}
/*
* Return the type_T for a typval. Only for primitive types.
*/
@@ -752,12 +851,12 @@ generate_PUSHBLOB(cctx_T *cctx, blob_T *blob)
* Consumes "name".
*/
static int
generate_PUSHFUNC(cctx_T *cctx, char_u *name)
generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, &t_func_void)) == NULL)
if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, type)) == NULL)
return FAIL;
isn->isn_arg.string = name;
@@ -774,8 +873,7 @@ generate_PUSHPARTIAL(cctx_T *cctx, partial_T *part)
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type(cctx, ISN_PUSHPARTIAL,
&t_partial_any)) == NULL)
if ((isn = generate_instr_type(cctx, ISN_PUSHPARTIAL, &t_func_any)) == NULL)
return FAIL;
isn->isn_arg.partial = part;
@@ -942,7 +1040,6 @@ generate_NEWLIST(cctx_T *cctx, int count)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
garray_T *type_list = cctx->ctx_type_list;
type_T *type;
type_T *member;
@@ -954,13 +1051,13 @@ generate_NEWLIST(cctx_T *cctx, int count)
// drop the value types
stack->ga_len -= count;
// Use the first value type for the list member type. Use "void" for an
// Use the first value type for the list member type. Use "any" for an
// empty list.
if (count > 0)
member = ((type_T **)stack->ga_data)[stack->ga_len];
else
member = &t_void;
type = get_list_type(member, type_list);
type = get_list_type(member, cctx->ctx_type_list);
// add the list type to the type stack
if (ga_grow(stack, 1) == FAIL)
@@ -979,7 +1076,6 @@ generate_NEWDICT(cctx_T *cctx, int count)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
garray_T *type_list = cctx->ctx_type_list;
type_T *type;
type_T *member;
@@ -997,7 +1093,7 @@ generate_NEWDICT(cctx_T *cctx, int count)
member = ((type_T **)stack->ga_data)[stack->ga_len + 1];
else
member = &t_void;
type = get_dict_type(member, type_list);
type = get_dict_type(member, cctx->ctx_type_list);
// add the dict type to the type stack
if (ga_grow(stack, 1) == FAIL)
@@ -1024,7 +1120,7 @@ generate_FUNCREF(cctx_T *cctx, int dfunc_idx)
if (ga_grow(stack, 1) == FAIL)
return FAIL;
((type_T **)stack->ga_data)[stack->ga_len] = &t_partial_any;
((type_T **)stack->ga_data)[stack->ga_len] = &t_func_any;
// TODO: argument and return types
++stack->ga_len;
@@ -1298,6 +1394,8 @@ generate_EXEC(cctx_T *cctx, char_u *line)
static char e_white_both[] =
N_("E1004: white space required before and after '%s'");
static char e_white_after[] = N_("E1069: white space required after '%s'");
static char e_no_white_before[] = N_("E1068: No white space allowed before '%s'");
/*
* Reserve space for a local variable.
@@ -1385,11 +1483,11 @@ skip_type(char_u *start)
/*
* Parse the member type: "<type>" and return "type" with the member set.
* Use "type_list" if a new type needs to be added.
* Use "type_gap" if a new type needs to be added.
* Returns NULL in case of failure.
*/
static type_T *
parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
parse_type_member(char_u **arg, type_T *type, garray_T *type_gap)
{
type_T *member_type;
int prev_called_emsg = called_emsg;
@@ -1397,14 +1495,14 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
if (**arg != '<')
{
if (*skipwhite(*arg) == '<')
emsg(_("E1007: No white space allowed before <"));
semsg(_(e_no_white_before), "<");
else
emsg(_("E1008: Missing <type>"));
return type;
}
*arg = skipwhite(*arg + 1);
member_type = parse_type(arg, type_list);
member_type = parse_type(arg, type_gap);
*arg = skipwhite(*arg);
if (**arg != '>' && called_emsg == prev_called_emsg)
@@ -1415,8 +1513,8 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
++*arg;
if (type->tt_type == VAR_LIST)
return get_list_type(member_type, type_list);
return get_dict_type(member_type, type_list);
return get_list_type(member_type, type_gap);
return get_dict_type(member_type, type_gap);
}
/*
@@ -1424,7 +1522,7 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
* Return &t_any for failure.
*/
type_T *
parse_type(char_u **arg, garray_T *type_list)
parse_type(char_u **arg, garray_T *type_gap)
{
char_u *p = *arg;
size_t len;
@@ -1466,7 +1564,7 @@ parse_type(char_u **arg, garray_T *type_list)
if (len == 4 && STRNCMP(*arg, "dict", len) == 0)
{
*arg += len;
return parse_type_member(arg, &t_dict_any, type_list);
return parse_type_member(arg, &t_dict_any, type_gap);
}
break;
case 'f':
@@ -1476,15 +1574,101 @@ parse_type(char_u **arg, garray_T *type_list)
*arg += len;
return &t_float;
#else
emsg(_("E1055: This Vim is not compiled with float support"));
emsg(_("E1076: This Vim is not compiled with float support"));
return &t_any;
#endif
}
if (len == 4 && STRNCMP(*arg, "func", len) == 0)
{
type_T *type;
type_T *ret_type = &t_any;
int argcount = -1;
int flags = 0;
int first_optional = -1;
type_T *arg_type[MAX_FUNC_ARGS + 1];
// func({type}, ...): {type}
*arg += len;
// TODO: arguments and return type
return &t_func_any;
if (**arg == '(')
{
// "func" may or may not return a value, "func()" does
// not return a value.
ret_type = &t_void;
p = ++*arg;
argcount = 0;
while (*p != NUL && *p != ')')
{
if (STRNCMP(p, "...", 3) == 0)
{
flags |= TTFLAG_VARARGS;
break;
}
arg_type[argcount++] = parse_type(&p, type_gap);
if (*p == '?')
{
if (first_optional == -1)
first_optional = argcount;
++p;
}
else if (first_optional != -1)
{
emsg(_("E1007: mandatory argument after optional argument"));
return &t_any;
}
if (*p != ',' && *skipwhite(p) == ',')
{
semsg(_(e_no_white_before), ",");
return &t_any;
}
if (*p == ',')
{
++p;
if (!VIM_ISWHITE(*p))
semsg(_(e_white_after), ",");
}
p = skipwhite(p);
if (argcount == MAX_FUNC_ARGS)
{
emsg(_("E740: Too many argument types"));
return &t_any;
}
}
p = skipwhite(p);
if (*p != ')')
{
emsg(_(e_missing_close));
return &t_any;
}
*arg = p + 1;
}
if (**arg == ':')
{
// parse return type
++*arg;
if (!VIM_ISWHITE(*p))
semsg(_(e_white_after), ":");
*arg = skipwhite(*arg);
ret_type = parse_type(arg, type_gap);
}
type = get_func_type(ret_type,
flags == 0 && first_optional == -1 ? argcount : 99,
type_gap);
if (flags != 0)
type->tt_flags = flags;
if (argcount > 0)
{
if (func_type_add_arg_types(type, argcount,
first_optional == -1 ? argcount : first_optional,
type_gap) == FAIL)
return &t_any;
mch_memmove(type->tt_args, arg_type,
sizeof(type_T *) * argcount);
}
return type;
}
break;
case 'j':
@@ -1498,7 +1682,7 @@ parse_type(char_u **arg, garray_T *type_list)
if (len == 4 && STRNCMP(*arg, "list", len) == 0)
{
*arg += len;
return parse_type_member(arg, &t_list_any, type_list);
return parse_type_member(arg, &t_list_any, type_gap);
}
break;
case 'n':
@@ -1508,14 +1692,6 @@ parse_type(char_u **arg, garray_T *type_list)
return &t_number;
}
break;
case 'p':
if (len == 7 && STRNCMP(*arg, "partial", len) == 0)
{
*arg += len;
// TODO: arguments and return type
return &t_partial_any;
}
break;
case 's':
if (len == 6 && STRNCMP(*arg, "string", len) == 0)
{
@@ -1574,7 +1750,7 @@ equal_type(type_T *type1, type_T *type2)
* "type2" and "dest" may be the same.
*/
static void
common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_list)
common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
{
if (equal_type(type1, type2))
{
@@ -1588,11 +1764,11 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_list)
{
type_T *common;
common_type(type1->tt_member, type2->tt_member, &common, type_list);
common_type(type1->tt_member, type2->tt_member, &common, type_gap);
if (type1->tt_type == VAR_LIST)
*dest = get_list_type(common, type_list);
*dest = get_list_type(common, type_gap);
else
*dest = get_dict_type(common, type_list);
*dest = get_dict_type(common, type_gap);
return;
}
// TODO: VAR_FUNC and VAR_PARTIAL
@@ -1650,7 +1826,62 @@ type_name(type_T *type, char **tofree)
return *tofree;
}
}
// TODO: function and partial argument types
if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL)
{
garray_T ga;
int i;
ga_init2(&ga, 1, 100);
if (ga_grow(&ga, 20) == FAIL)
return "[unknown]";
*tofree = ga.ga_data;
STRCPY(ga.ga_data, "func(");
ga.ga_len += 5;
for (i = 0; i < type->tt_argcount; ++i)
{
char *arg_free;
char *arg_type = type_name(type->tt_args[i], &arg_free);
int len;
if (i > 0)
{
STRCPY(ga.ga_data + ga.ga_len, ", ");
ga.ga_len += 2;
}
len = (int)STRLEN(arg_type);
if (ga_grow(&ga, len + 6) == FAIL)
{
vim_free(arg_free);
return "[unknown]";
}
*tofree = ga.ga_data;
STRCPY(ga.ga_data + ga.ga_len, arg_type);
ga.ga_len += len;
vim_free(arg_free);
}
if (type->tt_member == &t_void)
STRCPY(ga.ga_data + ga.ga_len, ")");
else
{
char *ret_free;
char *ret_name = type_name(type->tt_member, &ret_free);
int len;
len = (int)STRLEN(ret_name) + 4;
if (ga_grow(&ga, len) == FAIL)
{
vim_free(ret_free);
return "[unknown]";
}
*tofree = ga.ga_data;
STRCPY(ga.ga_data + ga.ga_len, "): ");
STRCPY(ga.ga_data + ga.ga_len + 3, ret_name);
vim_free(ret_free);
}
return ga.ga_data;
}
return name;
}
@@ -1743,7 +1974,9 @@ free_imported(cctx_T *cctx)
}
/*
* Generate an instruction to load script-local variable "name".
* Generate an instruction to load script-local variable "name", without the
* leading "s:".
* Also finds imported variables.
*/
static int
compile_load_scriptvar(
@@ -1808,7 +2041,7 @@ compile_load_scriptvar(
}
else
{
// TODO: check this is a variable, not a function
// TODO: check this is a variable, not a function?
generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT,
import->imp_sid,
import->imp_var_vals_idx,
@@ -1822,6 +2055,17 @@ compile_load_scriptvar(
return FAIL;
}
static int
generate_funcref(cctx_T *cctx, char_u *name)
{
ufunc_T *ufunc = find_func(name, cctx);
if (ufunc == NULL)
return FAIL;
return generate_PUSHFUNC(cctx, vim_strsave(name), ufunc->uf_func_type);
}
/*
* Compile a variable name into a load instruction.
* "end" points to just after the name.
@@ -1926,10 +2170,21 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|| (len == 5 && STRNCMP("false", *arg, 5) == 0))
res = generate_PUSHBOOL(cctx, **arg == 't'
? VVAL_TRUE : VVAL_FALSE);
else if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
== SCRIPT_VERSION_VIM9)
// in Vim9 script "var" can be script-local.
res = compile_load_scriptvar(cctx, name, *arg, &end, error);
else
{
// "var" can be script-local even without using "s:" if it
// already exists.
if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
== SCRIPT_VERSION_VIM9
|| lookup_script(*arg, len) == OK)
res = compile_load_scriptvar(cctx, name, *arg, &end,
FALSE);
// When the name starts with an uppercase letter or "x:" it
// can be a user defined function.
if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
res = generate_funcref(cctx, name);
}
}
}
if (gen_load)
@@ -1962,14 +2217,14 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount)
if (*p != ',' && *skipwhite(p) == ',')
{
emsg(_("E1068: No white space allowed before ,"));
semsg(_(e_no_white_before), ",");
p = skipwhite(p);
}
if (*p == ',')
{
++p;
if (!VIM_ISWHITE(*p))
emsg(_("E1069: white space required after ,"));
semsg(_(e_white_after), ",");
}
p = skipwhite(p);
}
@@ -2024,11 +2279,10 @@ compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
// builtin function
idx = find_internal_func(name);
if (idx >= 0)
{
res = generate_BCALL(cctx, idx, argcount);
goto theend;
}
semsg(_(e_unknownfunc), namebuf);
else
semsg(_(e_unknownfunc), namebuf);
goto theend;
}
// If we can find the function by name generate the right call.
@@ -2139,6 +2393,8 @@ type_mismatch(type_T *expected, type_T *actual)
static int
check_type(type_T *expected, type_T *actual, int give_msg)
{
int ret = OK;
if (expected->tt_type != VAR_UNKNOWN)
{
if (expected->tt_type != actual->tt_type)
@@ -2149,17 +2405,21 @@ check_type(type_T *expected, type_T *actual, int give_msg)
}
if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
{
int ret;
// void is used for an empty list or dict
if (actual->tt_member == &t_void)
ret = OK;
else
if (actual->tt_member != &t_void)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
if (ret == FAIL && give_msg)
type_mismatch(expected, actual);
return ret;
}
else if (expected->tt_type == VAR_FUNC)
{
if (expected->tt_member != &t_any)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
if (ret == OK && expected->tt_argcount != -1
&& (actual->tt_argcount < expected->tt_min_argcount
|| actual->tt_argcount > expected->tt_argcount))
ret = FAIL;
}
if (ret == FAIL && give_msg)
type_mismatch(expected, actual);
}
return OK;
}
@@ -2232,6 +2492,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
ufunc = rettv.vval.v_partial->pt_func;
++ufunc->uf_refcount;
clear_tv(&rettv);
ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
// The function will have one line: "return {expr}".
// Compile it into instructions.
@@ -2276,6 +2537,7 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
ufunc = rettv.vval.v_partial->pt_func;
++ufunc->uf_refcount;
clear_tv(&rettv);
ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
// The function will have one line: "return {expr}".
// Compile it into instructions.
@@ -3554,7 +3816,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
type = &t_string;
if (is_decl)
{
semsg(_("E1065: Cannot declare an environment variable: %s"), name);
semsg(_("E1065: Cannot declare an environment variable: %s"),
name);
goto theend;
}
}
@@ -3636,7 +3899,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (is_decl)
{
semsg(_("E1054: Variable already declared in the script: %s"),
name);
name);
goto theend;
}
}
@@ -3678,17 +3941,20 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
goto theend;
}
// +=, /=, etc. require an existing variable
if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE)
{
if (oplen > 1 && !heredoc)
{
// +=, /=, etc. require an existing variable
semsg(_("E1020: cannot use an operator on a new variable: %s"),
name);
goto theend;
}
// new local variable
if ((type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL)
&& var_check_func_name(name, TRUE))
goto theend;
idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
if (idx < 0)
goto theend;
@@ -3772,7 +4038,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (idx >= 0 && (is_decl || !has_type))
{
lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
if (!has_type)
if (new_local && !has_type)
{
if (stacktype->tt_type == VAR_VOID)
{
@@ -3780,11 +4046,19 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
goto theend;
}
else
lvar->lv_type = stacktype;
{
// An empty list or dict has a &t_void member, for a
// variable that implies &t_any.
if (stacktype == &t_list_empty)
lvar->lv_type = &t_list_any;
else if (stacktype == &t_dict_empty)
lvar->lv_type = &t_dict_any;
else
lvar->lv_type = stacktype;
}
}
else
if (check_type(lvar->lv_type, stacktype, TRUE) == FAIL)
goto theend;
else if (check_type(lvar->lv_type, stacktype, TRUE) == FAIL)
goto theend;
}
else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL)
goto theend;
@@ -3821,7 +4095,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
generate_PUSHBLOB(cctx, NULL);
break;
case VAR_FUNC:
generate_PUSHFUNC(cctx, NULL);
generate_PUSHFUNC(cctx, NULL, &t_func_void);
break;
case VAR_PARTIAL:
generate_PUSHPARTIAL(cctx, NULL);
@@ -3914,7 +4188,24 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
idx = get_script_item_idx(sid, rawname, TRUE);
// TODO: specific type
if (idx < 0)
generate_OLDSCRIPT(cctx, ISN_STORES, name, sid, &t_any);
{
char_u *name_s = name;
// Include s: in the name for store_var()
if (name[1] != ':')
{
int len = (int)STRLEN(name) + 3;
name_s = alloc(len);
if (name_s == NULL)
name_s = name;
else
vim_snprintf((char *)name_s, len, "s:%s", name);
}
generate_OLDSCRIPT(cctx, ISN_STORES, name_s, sid, &t_any);
if (name_s != name)
vim_free(name_s);
}
else
generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
sid, idx, &t_any);
@@ -3995,6 +4286,18 @@ compile_fill_jump_to_end(endlabel_T **el, cctx_T *cctx)
}
}
static void
compile_free_jump_to_end(endlabel_T **el)
{
while (*el != NULL)
{
endlabel_T *cur = (*el);
*el = cur->el_next;
vim_free(cur);
}
}
/*
* Create a new scope and set up the generic items.
*/
@@ -4026,6 +4329,20 @@ drop_scope(cctx_T *cctx)
return;
}
cctx->ctx_scope = scope->se_outer;
switch (scope->se_type)
{
case IF_SCOPE:
compile_free_jump_to_end(&scope->se_u.se_if.is_end_label); break;
case FOR_SCOPE:
compile_free_jump_to_end(&scope->se_u.se_for.fs_end_label); break;
case WHILE_SCOPE:
compile_free_jump_to_end(&scope->se_u.se_while.ws_end_label); break;
case TRY_SCOPE:
compile_free_jump_to_end(&scope->se_u.se_try.ts_end_label); break;
case NO_SCOPE:
case BLOCK_SCOPE:
break;
}
vim_free(scope);
}
@@ -4972,6 +5289,7 @@ compile_finally(char_u *arg, cctx_T *cctx)
// Fill in the "end" label in jumps at the end of the blocks.
compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx);
isn->isn_arg.try.try_finally = instr->ga_len;
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Previous catch without match jumps here
@@ -4979,7 +5297,6 @@ compile_finally(char_u *arg, cctx_T *cctx)
isn->isn_arg.jump.jump_where = instr->ga_len;
}
isn->isn_arg.try.try_finally = instr->ga_len;
// TODO: set index in ts_finally_label jumps
return arg;
@@ -5206,13 +5523,17 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
{
int is_ex_command;
// Bail out on the first error to avoid a flood of errors and report
// the right line number when inside try/catch.
if (emsg_before != called_emsg)
goto erret;
if (line != NULL && *line == '|')
// the line continues after a '|'
++line;
else if (line != NULL && *line != NUL)
{
if (emsg_before == called_emsg)
semsg(_("E488: Trailing characters: %s"), line);
semsg(_("E488: Trailing characters: %s"), line);
goto erret;
}
else
@@ -5350,13 +5671,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
line = p;
continue;
}
if (ea.cmdidx == CMD_let)
{
line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
if (line == NULL)
goto erret;
continue;
}
// CMD_let cannot happen, compile_assignment() above is used
iemsg("Command from find_ex_command() not handled");
goto erret;
}
@@ -5464,6 +5779,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
}
if (line == NULL)
goto erret;
line = skipwhite(line);
if (cctx.ctx_type_stack.ga_len < 0)
{
@@ -5507,6 +5823,43 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
dfunc->df_varcount = cctx.ctx_max_local;
}
{
int varargs = ufunc->uf_va_name != NULL;
int argcount = ufunc->uf_args.ga_len - (varargs ? 1 : 0);
// Create a type for the function, with the return type and any
// argument types.
// A vararg is included in uf_args.ga_len but not in uf_arg_types.
// The type is included in "tt_args".
ufunc->uf_func_type = get_func_type(ufunc->uf_ret_type,
ufunc->uf_args.ga_len, &ufunc->uf_type_list);
if (ufunc->uf_args.ga_len > 0)
{
if (func_type_add_arg_types(ufunc->uf_func_type,
ufunc->uf_args.ga_len,
argcount - ufunc->uf_def_args.ga_len,
&ufunc->uf_type_list) == FAIL)
{
ret = FAIL;
goto erret;
}
if (ufunc->uf_arg_types == NULL)
{
int i;
// lambda does not have argument types.
for (i = 0; i < argcount; ++i)
ufunc->uf_func_type->tt_args[i] = &t_any;
}
else
mch_memmove(ufunc->uf_func_type->tt_args,
ufunc->uf_arg_types, sizeof(type_T *) * argcount);
if (varargs)
ufunc->uf_func_type->tt_args[argcount] =
ufunc->uf_va_type == NULL ? &t_any : ufunc->uf_va_type;
}
}
ret = OK;
erret:
@@ -5524,6 +5877,9 @@ erret:
if (!dfunc->df_deleted)
--def_functions.ga_len;
while (cctx.ctx_scope != NULL)
drop_scope(&cctx);
// Don't execute this function body.
ga_clear_strings(&ufunc->uf_lines);

View File

@@ -377,7 +377,7 @@ store_var(char_u *name, typval_T *tv)
funccal_entry_T entry;
save_funccal(&entry);
set_var_const(name, NULL, tv, FALSE, 0);
set_var_const(name, NULL, tv, FALSE, LET_NO_COMMAND);
restore_funccal();
}
@@ -739,7 +739,7 @@ call_def_function(
--ectx.ec_stack.ga_len;
if (di == NULL)
store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
store_var(name, STACK_TV_BOT(0));
else
{
clear_tv(&di->di_tv);
@@ -1828,7 +1828,7 @@ ex_disassemble(exarg_T *eap)
iptr->isn_arg.loadstore.ls_sid);
smsg("%4d LOADS s:%s from %s", current,
iptr->isn_arg.string, si->sn_name);
iptr->isn_arg.loadstore.ls_name, si->sn_name);
}
break;
case ISN_LOADG:
@@ -1865,7 +1865,7 @@ ex_disassemble(exarg_T *eap)
iptr->isn_arg.loadstore.ls_sid);
smsg("%4d STORES %s in %s", current,
iptr->isn_arg.string, si->sn_name);
iptr->isn_arg.loadstore.ls_name, si->sn_name);
}
break;
case ISN_STORESCRIPT: