To: vim_dev@googlegroups.com Subject: Patch 8.2.4332 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4332 Problem: Vim9: incomplete test for existing script variable in block. Solution: Add a couple more tests. Fix uncovered problem. Files: src/userfunc.c, src/vim9compile.c, src/proto/vim9compile.pro, src/vim9script.c, src/vim9expr.c, src/testdir/test_vim9_func.vim *** ../vim-8.2.4331/src/userfunc.c 2022-02-07 21:53:58.085529486 +0000 --- src/userfunc.c 2022-02-08 20:18:36.215334673 +0000 *************** *** 55,60 **** --- 55,61 ---- * 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,70 **** --- 66,72 ---- garray_T *argtypes, int types_optional, evalarg_T *evalarg, + exarg_T *eap, int is_vararg, int skip) { *************** *** 87,93 **** // 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) return arg; if (newargs != NULL && ga_grow(newargs, 1) == FAIL) --- 89,96 ---- // 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, ! eap == NULL ? NULL : eap->cstack, TRUE) == FAIL) return arg; if (newargs != NULL && ga_grow(newargs, 1) == FAIL) *************** *** 210,216 **** int *varargs, garray_T *default_args, int skip, ! exarg_T *eap, garray_T *lines_to_free) { int mustend = FALSE; --- 213,219 ---- int *varargs, garray_T *default_args, int skip, ! exarg_T *eap, // can be NULL garray_T *lines_to_free) { int mustend = FALSE; *************** *** 279,285 **** arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') --- 282,288 ---- arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, eap, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') *************** *** 295,301 **** arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, FALSE, skip); if (p == arg) break; --- 298,304 ---- arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, eap, FALSE, skip); if (p == arg) break; *** ../vim-8.2.4331/src/vim9compile.c 2022-02-07 21:53:58.085529486 +0000 --- src/vim9compile.c 2022-02-08 20:30:32.738346943 +0000 *************** *** 152,162 **** * 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. * 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) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; --- 152,163 ---- * 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, cstack_T *cstack) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; *************** *** 183,193 **** if (cctx == NULL) { ! // Not in a function scope, find variable with block id equal to or ! // smaller than the current block id. while (sav != NULL) { ! if (sav->sav_block_id <= si->sn_current_block_id) break; sav = sav->sav_next; } --- 184,205 ---- if (cctx == NULL) { ! // 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 (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,234 **** /* * Lookup a variable (without s: prefix) in the current script. * "cctx" is NULL at the script level. * Returns OK or FAIL. */ int ! script_var_exists(char_u *name, size_t len, cctx_T *cctx) { if (current_sctx.sc_sid <= 0) return FAIL; --- 237,247 ---- /* * 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, cstack_T *cstack) { if (current_sctx.sc_sid <= 0) return FAIL; *************** *** 236,242 **** { // Check script variables that were visible where the function was // defined. ! if (find_script_var(name, len, cctx) != NULL) return OK; } else --- 249,255 ---- { // Check script variables that were visible where the function was // defined. ! if (find_script_var(name, len, cctx, cstack) != NULL) return OK; } else *************** *** 267,273 **** 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 || find_imported(name, len, FALSE, cctx) != NULL; } --- 280,286 ---- 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, NULL) == OK || find_imported(name, len, FALSE, cctx) != NULL; } *************** *** 309,315 **** * Return FAIL and give an error if it defined. */ int ! check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) { int c = p[len]; ufunc_T *ufunc = NULL; --- 322,333 ---- * Return FAIL and give an error if it defined. */ int ! 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,324 **** if (len == 1 && *p == '_') return OK; ! if (script_var_exists(p, len, cctx) == OK) { if (is_arg) semsg(_(e_argument_already_declared_in_script_str), p); --- 336,342 ---- if (len == 1 && *p == '_') return OK; ! if (script_var_exists(p, len, cctx, cstack) == OK) { if (is_arg) semsg(_(e_argument_already_declared_in_script_str), p); *************** *** 526,532 **** return -1; if (sid == current_sctx.sc_sid) { ! sallvar_T *sav = find_script_var(name, 0, cctx); if (sav == NULL) return -2; --- 544,550 ---- return -1; if (sid == current_sctx.sc_sid) { ! sallvar_T *sav = find_script_var(name, 0, cctx, NULL); if (sav == NULL) return -2; *************** *** 884,890 **** semsg(_(e_namespace_not_supported_str), name_start); return NULL; } ! if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL) return NULL; if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) { --- 902,909 ---- semsg(_(e_namespace_not_supported_str), name_start); return NULL; } ! 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,1364 **** && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2, ! cctx) : script_var_exists(var_start, lhs->lhs_varlen, ! cctx)) == OK; imported_T *import = find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); --- 1375,1383 ---- && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2, ! cctx, NULL) : script_var_exists(var_start, lhs->lhs_varlen, ! cctx, NULL)) == OK; imported_T *import = find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); *************** *** 1442,1449 **** } } } ! else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE) ! == FAIL) return FAIL; } } --- 1461,1468 ---- } } } ! else if (check_defined(var_start, lhs->lhs_varlen, cctx, ! NULL, FALSE) == FAIL) return FAIL; } } *************** *** 2470,2476 **** 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) { r = FAIL; break; --- 2489,2495 ---- 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, NULL, TRUE) == FAIL) { r = FAIL; break; *** ../vim-8.2.4331/src/proto/vim9compile.pro 2022-02-02 16:20:22.386554561 +0000 --- src/proto/vim9compile.pro 2022-02-08 20:29:00.926470151 +0000 *************** *** 2,9 **** 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 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); --- 2,9 ---- 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, 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); *** ../vim-8.2.4331/src/vim9script.c 2022-02-08 19:12:15.260593033 +0000 --- src/vim9script.c 2022-02-08 20:20:55.675136052 +0000 *************** *** 600,606 **** goto erret; } else if (imported == NULL ! && check_defined(as_name, STRLEN(as_name), cctx, FALSE) == FAIL) goto erret; if (imported == NULL) --- 600,607 ---- goto erret; } else if (imported == NULL ! && check_defined(as_name, STRLEN(as_name), cctx, NULL, ! FALSE) == FAIL) goto erret; if (imported == NULL) *** ../vim-8.2.4331/src/vim9expr.c 2022-02-06 18:36:46.297746545 +0000 --- src/vim9expr.c 2022-02-08 20:23:39.622907487 +0000 *************** *** 501,507 **** { // "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 || find_imported(name, 0, FALSE, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); --- 501,507 ---- { // "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, NULL) == OK || find_imported(name, 0, FALSE, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); *** ../vim-8.2.4331/src/testdir/test_vim9_func.vim 2022-02-08 19:23:31.712319067 +0000 --- src/testdir/test_vim9_func.vim 2022-02-08 20:03:40.448780266 +0000 *************** *** 1057,1062 **** --- 1057,1099 ---- 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 *** ../vim-8.2.4331/src/version.c 2022-02-08 19:23:31.712319067 +0000 --- src/version.c 2022-02-08 20:00:27.981022674 +0000 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4332, /**/ -- How To Keep A Healthy Level Of Insanity: 3. Every time someone asks you to do something, ask if they want fries with that. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///