# HG changeset patch # User Bram Moolenaar # Date 1644696003 -3600 # Node ID 5c4ab8d4472c29b4c190060a54f02f110179fdbe # Parent 407a08328369125a6a5a18323d2c7da0201c3711 patch 8.2.4360: Vim9: allowing use of "s:" leads to inconsistencies Commit: https://github.com/vim/vim/commit/a749a42ed25534c88c636e5ab6603f1f97b857a4 Author: Bram Moolenaar Date: Sat Feb 12 19:52:25 2022 +0000 patch 8.2.4360: Vim9: allowing use of "s:" leads to inconsistencies Problem: Vim9: allowing use of "s:" leads to inconsistencies. Solution: Disallow using "s:" in Vim9 script at the script level. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -2828,7 +2828,7 @@ EXTERN char e_unknown_error_while_execut INIT(= N_("E1099: Unknown error while executing %s")); EXTERN char e_command_not_supported_in_vim9_script_missing_var_str[] INIT(= N_("E1100: Command not supported in Vim9 script (missing :var?): %s")); -EXTERN char e_cannot_declare_script_variable_in_function[] +EXTERN char e_cannot_declare_script_variable_in_function_str[] INIT(= N_("E1101: Cannot declare a script variable in a function: %s")); EXTERN char e_lambda_function_not_found_str[] INIT(= N_("E1102: Lambda function not found: %s")); @@ -3232,4 +3232,6 @@ EXTERN char e_critical_error_in_python3_ #ifdef FEAT_EVAL EXTERN char e_function_name_must_start_with_capital_str[] INIT(= N_("E1267: Function name must start with a capital: %s")); +EXTERN char e_cannot_use_s_colon_in_vim9_script_str[] + INIT(= N_("E1268: Cannot use s: in Vim9 script: %s")); #endif diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -878,6 +878,14 @@ get_lval( return lp->ll_name_end; } + // Cannot use "s:var" at the Vim9 script level. "s: type" is OK. + if (in_vim9script() && at_script_level() + && name[0] == 's' && name[1] == ':' && !VIM_ISWHITE(name[2])) + { + semsg(_(e_cannot_use_s_colon_in_vim9_script_str), name); + return NULL; + } + // Find the end of the name. p = find_name_end(name, &expr_start, &expr_end, fne_flags); lp->ll_name_end = p; @@ -3732,6 +3740,12 @@ eval7( emsg(_(e_cannot_use_underscore_here)); ret = FAIL; } + else if (evaluate && in_vim9script() && len > 2 + && s[0] == 's' && s[1] == ':') + { + semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s); + ret = FAIL; + } else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') { // "name(..." recursive! diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -24,6 +24,7 @@ int call_user_func_check(ufunc_T *fp, in void save_funccal(funccal_entry_T *entry); void restore_funccal(void); funccall_T *get_current_funccal(void); +int at_script_level(void); void delete_script_functions(int sid); void free_all_functions(void); int builtin_function(char_u *name, int len); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -6,6 +6,7 @@ source term_util.vim let s:appendToMe = 'xxx' let s:addToMe = 111 +let s:newVar = '' let g:existing = 'yes' let g:inc_counter = 1 let $SOME_ENV_VAR = 'some' @@ -124,12 +125,12 @@ def Test_assignment() END v9.CheckScriptSuccess(lines) - s:appendToMe ..= 'yyy' - assert_equal('xxxyyy', s:appendToMe) - s:addToMe += 222 - assert_equal(333, s:addToMe) - s:newVar = 'new' - assert_equal('new', s:newVar) + appendToMe ..= 'yyy' + assert_equal('xxxyyy', appendToMe) + addToMe += 222 + assert_equal(333, addToMe) + newVar = 'new' + assert_equal('new', newVar) set ts=7 var ts: number = &ts @@ -1195,7 +1196,7 @@ def Test_assignment_default() assert_equal(5678, nr) enddef -let scriptvar = 'init' +let s:scriptvar = 'init' def Test_assignment_var_list() var lines =<< trim END @@ -1243,17 +1244,17 @@ def Test_assignment_var_list() END v9.CheckDefAndScriptSuccess(lines) - [g:globalvar, s:scriptvar, b:bufvar] = ['global', 'script', 'buf'] + [g:globalvar, scriptvar, b:bufvar] = ['global', 'script', 'buf'] assert_equal('global', g:globalvar) - assert_equal('script', s:scriptvar) + assert_equal('script', scriptvar) assert_equal('buf', b:bufvar) lines =<< trim END vim9script - var s:scriptvar = 'init' - [g:globalvar, s:scriptvar, w:winvar] = ['global', 'script', 'win'] + var scriptvar = 'init' + [g:globalvar, scriptvar, w:winvar] = ['global', 'script', 'win'] assert_equal('global', g:globalvar) - assert_equal('script', s:scriptvar) + assert_equal('script', scriptvar) assert_equal('win', w:winvar) END v9.CheckScriptSuccess(lines) @@ -1398,7 +1399,7 @@ def Test_assignment_failure() v9.CheckDefFailure(["var xnr = xnr + 1"], 'E1001:', 1) v9.CheckScriptFailure(['vim9script', 'var xnr = xnr + 4'], 'E121:') - v9.CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:') + v9.CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = notfound', 'enddef', 'defcompile'], 'E1001:') v9.CheckDefFailure(['var name: list = [123]'], 'expected list but got list') v9.CheckDefFailure(['var name: list = ["xx"]'], 'expected list but got list') @@ -1719,9 +1720,9 @@ def Test_var_declaration() g:var_uninit = name name = 'text' g:var_test = name - # prefixing s: is optional - s:name = 'prefixed' - g:var_prefixed = s:name + # prefixing s: is not allowed + name = 'prefixed' + g:var_prefixed = name const FOO: number = 123 assert_equal(123, FOO) @@ -1764,9 +1765,9 @@ def Test_var_declaration() var xyz: string # comment # type is inferred - var s:dict = {['a']: 222} + var dict = {['a']: 222} def GetDictVal(key: any) - g:dict_val = s:dict[key] + g:dict_val = dict[key] enddef GetDictVal('a') @@ -1879,13 +1880,13 @@ def Test_var_declaration_fails() enddef def Test_script_local_in_legacy() - # OK to define script-local later when prefixed with s: + # OK to define script-local later but before compiling var lines =<< trim END def SetLater() - s:legvar = 'two' + legvar = 'two' enddef + let s:legvar = 'one' defcompile - let s:legvar = 'one' call SetLater() call assert_equal('two', s:legvar) END @@ -1902,7 +1903,7 @@ def Test_script_local_in_legacy() END v9.CheckScriptSuccess(lines) - # Not OK to leave out s: prefix when script-local defined later + # Not OK to leave out s: prefix when script-local defined after compiling lines =<< trim END def SetLaterNoPrefix() legvar = 'two' @@ -1944,15 +1945,15 @@ def Test_var_type_check() lines =<< trim END vim9script - var s:l: list - s:l = [] + var l: list + l = [] END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script - var s:d: dict - s:d = {} + var d: dict + d = {} END v9.CheckScriptSuccess(lines) @@ -2124,7 +2125,7 @@ def Test_unlet() 'vim9script', 'var svar = 123', 'unlet s:svar', - ], 'E1081:') + ], 'E1268:') v9.CheckScriptFailure([ 'vim9script', 'var svar = 123', @@ -2267,14 +2268,14 @@ def Test_script_funcref_case() lines =<< trim END vim9script - var s:Len = (s: string): number => len(s) + 2 + var Len = (s: string): number => len(s) + 2 assert_equal(6, Len('asdf')) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script - var s:len = (s: string): number => len(s) + 1 + var len = (s: string): number => len(s) + 1 END v9.CheckScriptFailure(lines, 'E704:') enddef diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -2001,9 +2001,9 @@ def Test_insert() v9.CheckDefExecAndScriptFailure(lines, 'E1131:', 1) assert_equal([1, 2, 3], insert([2, 3], 1)) - assert_equal([1, 2, 3], insert([2, 3], s:number_one)) + assert_equal([1, 2, 3], insert([2, 3], number_one)) assert_equal([1, 2, 3], insert([1, 2], 3, 2)) - assert_equal([1, 2, 3], insert([1, 2], 3, s:number_two)) + assert_equal([1, 2, 3], insert([1, 2], 3, number_two)) assert_equal(['a', 'b', 'c'], insert(['b', 'c'], 'a')) assert_equal(0z1234, insert(0z34, 0x12)) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -1718,7 +1718,12 @@ def Test_var_not_cmd() lines =<< trim END s:notexist:repl END - v9.CheckDefAndScriptFailure(lines, ['E488: Trailing characters: :repl', 'E121: Undefined variable: s:notexist'], 1) + v9.CheckDefAndScriptFailure(lines, ['E488: Trailing characters: :repl', 'E1268:'], 1) + + lines =<< trim END + notexist:repl + END + v9.CheckDefAndScriptFailure(lines, ['E476:', 'E492:'], 1) lines =<< trim END s-pat-repl diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -300,11 +300,11 @@ def Test_disassemble_push() vim9script import autoload 'autoscript.vim' - def s:AutoloadFunc() + def AutoloadFunc() &operatorfunc = autoscript.Opfunc enddef - var res = execute('disass s:AutoloadFunc') + var res = execute('disass AutoloadFunc') assert_match('\d*_AutoloadFunc.*' .. '&operatorfunc = autoscript.Opfunc\_s*' .. '0 AUTOLOAD autoscript#Opfunc\_s*' .. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2329,7 +2329,7 @@ def Test_expr8_lambda_vim9script() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_funcref() +def Test_expr8funcref() var lines =<< trim END def RetNumber(): number return 123 @@ -2344,7 +2344,7 @@ def Test_expr8_funcref() func g:GlobalFunc() return 'global' endfunc - func s:ScriptFunc() + func ScriptFunc() return 'script' endfunc def Test() @@ -2353,7 +2353,7 @@ def Test_expr8_funcref() Ref = g:GlobalFunc assert_equal('global', Ref()) - Ref = s:ScriptFunc + Ref = ScriptFunc assert_equal('script', Ref()) Ref = ScriptFunc assert_equal('script', Ref()) @@ -3347,7 +3347,7 @@ func Test_expr8_fails() call v9.CheckDefAndScriptFailure(["var x = ¬exist"], 'E113:', 1) call v9.CheckDefAndScriptFailure(["&grepprg = [343]"], ['E1012:', 'E730:'], 1) - call v9.CheckDefExecAndScriptFailure(["echo s:doesnt_exist"], 'E121:', 1) + call v9.CheckDefExecAndScriptFailure(["echo s:doesnt_exist"], ['E121:', 'E1268:'], 1) call v9.CheckDefExecAndScriptFailure(["echo g:doesnt_exist"], 'E121:', 1) call v9.CheckDefAndScriptFailure(["echo a:somevar"], ['E1075:', 'E121:'], 1) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -713,7 +713,7 @@ def Test_nested_function() lines =<< trim END vim9script - def s:_Func() + def _Func() echo 'bad' enddef END @@ -930,7 +930,7 @@ def Test_global_local_function() def g:Funcy() echo 'funcy' enddef - s:Funcy() + Funcy() END v9.CheckScriptFailure(lines, 'E117:') enddef @@ -1441,10 +1441,10 @@ enddef def Test_use_script_func_name_with_prefix() var lines =<< trim END vim9script - func s:Getit() + func g:Getit() return 'it' endfunc - var Fn = s:Getit + var Fn = g:Getit assert_equal('it', Fn()) END v9.CheckScriptSuccess(lines) @@ -2849,7 +2849,7 @@ def Test_nested_inline_lambda() lines =<< trim END vim9script - def s:Func() + def Func() range(10) ->mapnew((_, _) => ({ key: range(10)->mapnew((_, _) => { @@ -3168,7 +3168,7 @@ def Test_invalid_function_name() vim9script def s: list END - v9.CheckScriptFailure(lines, 'E129:') + v9.CheckScriptFailure(lines, 'E1268:') lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -1124,7 +1124,7 @@ def Test_vim9_reload_noclear() lines =<< trim END vim9script noclear g:loadCount += 1 - var s:reloaded = 'init' + var reloaded = 'init' import './XExportReload' as exp def Again(): string @@ -1133,13 +1133,13 @@ def Test_vim9_reload_noclear() exp.TheFunc() - if exists('s:loaded') | finish | endif - var s:loaded = true + if exists('loaded') | finish | endif + var loaded = true - var s:notReloaded = 'yes' - s:reloaded = 'first' + var notReloaded = 'yes' + reloaded = 'first' def g:Values(): list - return [s:reloaded, s:notReloaded, Again(), Once(), exp.exported] + return [reloaded, notReloaded, Again(), Once(), exp.exported] enddef def Once(): string diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -69,7 +69,10 @@ def Test_delfunction() 'func CheckMe()', ' return 123', 'endfunc', - 'assert_equal(123, s:CheckMe())', + 'func DoTest()', + ' call assert_equal(123, s:CheckMe())', + 'endfunc', + 'DoTest()', ]) # Check function in script namespace cannot be deleted @@ -178,11 +181,55 @@ def Test_wrong_type() v9.CheckDefFailure(['var Ref: string', 'var res = Ref()'], 'E1085:') enddef +def Test_script_namespace() + # defining a function or variable with s: is not allowed + var lines =<< trim END + vim9script + def s:Function() + enddef + END + v9.CheckScriptFailure(lines, 'E1268:') + + for decl in ['var', 'const', 'final'] + lines =<< trim END + vim9script + var s:var = 'var' + END + v9.CheckScriptFailure([ + 'vim9script', + decl .. ' s:var = "var"', + ], 'E1268:') + endfor + + # Calling a function or using a variable with s: is not allowed at script + # level + lines =<< trim END + vim9script + def Function() + enddef + s:Function() + END + v9.CheckScriptFailure(lines, 'E1268:') + lines =<< trim END + vim9script + def Function() + enddef + call s:Function() + END + v9.CheckScriptFailure(lines, 'E1268:') + lines =<< trim END + vim9script + var var = 'var' + echo s:var + END + v9.CheckScriptFailure(lines, 'E1268:') +enddef + def Test_script_wrong_type() var lines =<< trim END vim9script - var s:dict: dict - s:dict['a'] = ['x'] + var dict: dict + dict['a'] = ['x'] END v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got list', 3) enddef diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -73,8 +73,8 @@ endfunc export def CheckScriptFailure(lines: list, error: string, lnum = -3) var cwd = getcwd() - var fname = 'XScriptFailure' .. s:sequence - s:sequence += 1 + var fname = 'XScriptFailure' .. sequence + sequence += 1 writefile(lines, fname) try assert_fails('so ' .. fname, error, lines, lnum) @@ -86,8 +86,8 @@ enddef export def CheckScriptFailureList(lines: list, errors: list, lnum = -3) var cwd = getcwd() - var fname = 'XScriptFailure' .. s:sequence - s:sequence += 1 + var fname = 'XScriptFailure' .. sequence + sequence += 1 writefile(lines, fname) try assert_fails('so ' .. fname, errors, lines, lnum) @@ -99,8 +99,8 @@ enddef export def CheckScriptSuccess(lines: list) var cwd = getcwd() - var fname = 'XScriptSuccess' .. s:sequence - s:sequence += 1 + var fname = 'XScriptSuccess' .. sequence + sequence += 1 writefile(lines, fname) try exe 'so ' .. fname diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3010,6 +3010,18 @@ get_current_funccal(void) } /* + * Return TRUE when currently at the script level: + * - not in a function + * - not executing an autocommand + * Note that when an autocommand sources a script the result is FALSE; + */ + int +at_script_level(void) +{ + return current_funccal == NULL && autocmd_match == NULL; +} + +/* * Mark all functions of script "sid" as deleted. */ void @@ -4205,6 +4217,12 @@ define_function(exarg_T *eap, char_u *na } else { + if (vim9script && p[0] == 's' && p[1] == ':') + { + semsg(_(e_cannot_use_s_colon_in_vim9_script_str), p); + return NULL; + } + name = save_function_name(&p, &is_global, eap->skip, TFN_NO_AUTOLOAD | TFN_NEW_FUNC, &fudi); paren = (vim_strchr(p, '(') != NULL); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4360, +/**/ 4359, /**/ 4358, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1394,7 +1394,7 @@ compile_lhs( if (is_decl) { if (script_namespace) - semsg(_(e_cannot_declare_script_variable_in_function), + semsg(_(e_cannot_declare_script_variable_in_function_str), lhs->lhs_name); else semsg(_(e_variable_already_declared_in_script_str),