# HG changeset patch # User Bram Moolenaar # Date 1644353103 -3600 # Node ID e311a80f8cbe08d9cc328e001000c6c4d3e5b66a # Parent 81772f74373848e83e9504b09d3353d0967ad420 patch 8.2.4332: Vim9: incomplete test for existing script variable in block Commit: https://github.com/vim/vim/commit/dce2441a603f2c9343a4a46091283a32420d80a2 Author: Bram Moolenaar Date: Tue Feb 8 20:35:30 2022 +0000 patch 8.2.4332: Vim9: incomplete test for existing script variable in block Problem: Vim9: incomplete test for existing script variable in block. Solution: Add a couple more tests. Fix uncovered problem. diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -2,8 +2,8 @@ int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx); int arg_exists(char_u *name, size_t len, int *idxp, type_T **type, int *gen_load_outer, cctx_T *cctx); int script_is_vim9(void); -int script_var_exists(char_u *name, size_t len, cctx_T *cctx); -int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg); +int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack); +int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg); int need_type_where(type_T *actual, type_T *expected, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const); int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); 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 @@ -1057,6 +1057,43 @@ def Test_call_wrong_args() END v9.CheckScriptSuccess(lines) + # with another variable in another block + lines =<< trim END + vim9script + if true + var name = 'piet' + # define a function so that the variable isn't cleared + def GetItem(): string + return item + enddef + endif + if true + var name = 'peter' + def FuncOne(name: string) + echo name + enddef + endif + END + v9.CheckScriptFailure(lines, 'E1168:') + + # only variable in another block is OK + lines =<< trim END + vim9script + if true + var name = 'piet' + # define a function so that the variable isn't cleared + def GetItem(): string + return item + enddef + endif + if true + def FuncOne(name: string) + echo name + enddef + endif + END + v9.CheckScriptSuccess(lines) + # argument name declared later is only found when compiling lines =<< trim END vim9script diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -55,6 +55,7 @@ func_tbl_get(void) * If "argtypes" is not NULL also get the type: "arg: type" (:def function). * If "types_optional" is TRUE a missing type is OK, use "any". * If "evalarg" is not NULL use it to check for an already declared name. + * If "eap" is not NULL use it to check for an already declared name. * Return a pointer to after the type. * When something is wrong return "arg". */ @@ -65,6 +66,7 @@ one_function_arg( garray_T *argtypes, int types_optional, evalarg_T *evalarg, + exarg_T *eap, int is_vararg, int skip) { @@ -87,7 +89,8 @@ one_function_arg( // Vim9 script: cannot use script var name for argument. In function: also // check local vars and arguments. if (!skip && argtypes != NULL && check_defined(arg, p - arg, - evalarg == NULL ? NULL : evalarg->eval_cctx, TRUE) == FAIL) + evalarg == NULL ? NULL : evalarg->eval_cctx, + eap == NULL ? NULL : eap->cstack, TRUE) == FAIL) return arg; if (newargs != NULL && ga_grow(newargs, 1) == FAIL) @@ -210,7 +213,7 @@ get_function_args( int *varargs, garray_T *default_args, int skip, - exarg_T *eap, + exarg_T *eap, // can be NULL garray_T *lines_to_free) { int mustend = FALSE; @@ -279,7 +282,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, TRUE, skip); + evalarg, eap, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') @@ -295,7 +298,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, FALSE, skip); + evalarg, eap, FALSE, skip); if (p == arg) break; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4332, +/**/ 4331, /**/ 4330, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -152,11 +152,12 @@ arg_exists( * Lookup a script-local variable in the current script, possibly defined in a * block that contains the function "cctx->ctx_ufunc". * "cctx" is NULL at the script level. + * "cstack_T" is NULL in a function. * If "len" is <= 0 "name" must be NUL terminated. * Return NULL when not found. */ static sallvar_T * -find_script_var(char_u *name, size_t len, cctx_T *cctx) +find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; @@ -183,11 +184,22 @@ find_script_var(char_u *name, size_t len if (cctx == NULL) { - // Not in a function scope, find variable with block id equal to or - // smaller than the current block id. + // Not in a function scope, find variable with block ID equal to or + // smaller than the current block id. If "cstack" is not NULL go up + // the block scopes (more accurate). while (sav != NULL) { - if (sav->sav_block_id <= si->sn_current_block_id) + if (cstack != NULL) + { + int idx; + + for (idx = cstack->cs_idx; idx >= 0; --idx) + if (cstack->cs_block_id[idx] == sav->sav_block_id) + break; + if (idx >= 0) + break; + } + else if (sav->sav_block_id <= si->sn_current_block_id) break; sav = sav->sav_next; } @@ -225,10 +237,11 @@ script_is_vim9() /* * Lookup a variable (without s: prefix) in the current script. * "cctx" is NULL at the script level. + * "cstack" is NULL in a function. * Returns OK or FAIL. */ int -script_var_exists(char_u *name, size_t len, cctx_T *cctx) +script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) { if (current_sctx.sc_sid <= 0) return FAIL; @@ -236,7 +249,7 @@ script_var_exists(char_u *name, size_t l { // Check script variables that were visible where the function was // defined. - if (find_script_var(name, len, cctx) != NULL) + if (find_script_var(name, len, cctx, cstack) != NULL) return OK; } else @@ -267,7 +280,7 @@ variable_exists(char_u *name, size_t len return (cctx != NULL && (lookup_local(name, len, NULL, cctx) == OK || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)) - || script_var_exists(name, len, cctx) == OK + || script_var_exists(name, len, cctx, NULL) == OK || find_imported(name, len, FALSE, cctx) != NULL; } @@ -309,7 +322,12 @@ item_exists(char_u *name, size_t len, in * Return FAIL and give an error if it defined. */ int -check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) +check_defined( + char_u *p, + size_t len, + cctx_T *cctx, + cstack_T *cstack, + int is_arg) { int c = p[len]; ufunc_T *ufunc = NULL; @@ -318,7 +336,7 @@ check_defined(char_u *p, size_t len, cct if (len == 1 && *p == '_') return OK; - if (script_var_exists(p, len, cctx) == OK) + if (script_var_exists(p, len, cctx, cstack) == OK) { if (is_arg) semsg(_(e_argument_already_declared_in_script_str), p); @@ -526,7 +544,7 @@ get_script_item_idx(int sid, char_u *nam return -1; if (sid == current_sctx.sc_sid) { - sallvar_T *sav = find_script_var(name, 0, cctx); + sallvar_T *sav = find_script_var(name, 0, cctx, NULL); if (sav == NULL) return -2; @@ -884,7 +902,8 @@ compile_nested_function(exarg_T *eap, cc semsg(_(e_namespace_not_supported_str), name_start); return NULL; } - if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL) + if (check_defined(name_start, name_end - name_start, cctx, + NULL, FALSE) == FAIL) return NULL; if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) { @@ -1356,9 +1375,9 @@ compile_lhs( && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2, - cctx) + cctx, NULL) : script_var_exists(var_start, lhs->lhs_varlen, - cctx)) == OK; + cctx, NULL)) == OK; imported_T *import = find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); @@ -1442,8 +1461,8 @@ compile_lhs( } } } - else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE) - == FAIL) + else if (check_defined(var_start, lhs->lhs_varlen, cctx, + NULL, FALSE) == FAIL) return FAIL; } } @@ -2470,7 +2489,7 @@ check_args_shadowing(ufunc_T *ufunc, cct for (i = 0; i < ufunc->uf_args.ga_len; ++i) { arg = ((char_u **)(ufunc->uf_args.ga_data))[i]; - if (check_defined(arg, STRLEN(arg), cctx, TRUE) == FAIL) + if (check_defined(arg, STRLEN(arg), cctx, NULL, TRUE) == FAIL) { r = FAIL; break; diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -501,7 +501,7 @@ compile_load( { // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. - if (script_var_exists(*arg, len, cctx) == OK + if (script_var_exists(*arg, len, cctx, NULL) == OK || find_imported(name, 0, FALSE, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -600,7 +600,8 @@ handle_import( goto erret; } else if (imported == NULL - && check_defined(as_name, STRLEN(as_name), cctx, FALSE) == FAIL) + && check_defined(as_name, STRLEN(as_name), cctx, NULL, + FALSE) == FAIL) goto erret; if (imported == NULL)