To: vim_dev@googlegroups.com Subject: Patch 8.2.2558 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2558 Problem: No error if a lambda argument shadows a variable. Solution: Check that the argument name shadows a local, argument or script variable. (closes #7898) Files: src/vim9compile.c, src/proto/vim9compile.pro, src/userfunc.c, src/vim9script.c, src/errors.h, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_script.vim *** ../vim-8.2.2557/src/vim9compile.c 2021-02-21 22:20:18.865587667 +0100 --- src/vim9compile.c 2021-02-28 16:30:37.338839896 +0100 *************** *** 379,386 **** static int variable_exists(char_u *name, size_t len, cctx_T *cctx) { ! return lookup_local(name, len, NULL, cctx) == OK ! || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK || script_var_exists(name, len, FALSE, cctx) == OK || find_imported(name, len, cctx) != NULL; } --- 379,387 ---- static int variable_exists(char_u *name, size_t len, cctx_T *cctx) { ! return (cctx != NULL ! && (lookup_local(name, len, NULL, cctx) == OK ! || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)) || script_var_exists(name, len, FALSE, cctx) == OK || find_imported(name, len, cctx) != NULL; } *************** *** 389,405 **** * Check if "p[len]" is already defined, either in script "import_sid" or in * compilation context "cctx". "cctx" is NULL at the script level. * Does not check the global namespace. * Return FAIL and give an error if it defined. */ int ! check_defined(char_u *p, size_t len, cctx_T *cctx) { int c = p[len]; ufunc_T *ufunc = NULL; p[len] = NUL; ! if (script_var_exists(p, len, FALSE, cctx) == OK ! || (cctx != NULL && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL --- 390,415 ---- * Check if "p[len]" is already defined, either in script "import_sid" or in * compilation context "cctx". "cctx" is NULL at the script level. * Does not check the global namespace. + * If "is_arg" is TRUE the error message is for an argument name. * 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; + if (script_var_exists(p, len, FALSE, cctx) == OK) + { + if (is_arg) + semsg(_(e_argument_already_declared_in_script_str), p); + else + semsg(_(e_variable_already_declared_in_script_str), p); + return FAIL; + } + p[len] = NUL; ! if ((cctx != NULL && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL *************** *** 409,416 **** if (ufunc == NULL || !func_is_global(ufunc) || (p[0] == 'g' && p[1] == ':')) { p[len] = c; - semsg(_(e_name_already_defined_str), p); return FAIL; } } --- 419,429 ---- if (ufunc == NULL || !func_is_global(ufunc) || (p[0] == 'g' && p[1] == ':')) { + if (is_arg) + semsg(_(e_argument_name_shadows_existing_variable_str), p); + else + semsg(_(e_name_already_defined_str), p); p[len] = c; return FAIL; } } *************** *** 5120,5126 **** semsg(_(e_namespace_not_supported_str), name_start); return NULL; } ! if (check_defined(name_start, name_end - name_start, cctx) == FAIL) return NULL; eap->arg = name_end; --- 5133,5139 ---- semsg(_(e_namespace_not_supported_str), name_start); return NULL; } ! if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL) return NULL; eap->arg = name_end; *************** *** 5686,5692 **** semsg(_(e_cannot_declare_script_variable_in_function), lhs->lhs_name); else ! semsg(_(e_variable_already_declared_in_script), lhs->lhs_name); return FAIL; } --- 5699,5705 ---- semsg(_(e_cannot_declare_script_variable_in_function), lhs->lhs_name); else ! semsg(_(e_variable_already_declared_in_script_str), lhs->lhs_name); return FAIL; } *************** *** 5723,5729 **** } } } ! else if (check_defined(var_start, lhs->lhs_varlen, cctx) == FAIL) return FAIL; } --- 5736,5742 ---- } } } ! else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE) == FAIL) return FAIL; } *** ../vim-8.2.2557/src/proto/vim9compile.pro 2021-02-21 22:20:18.865587667 +0100 --- src/proto/vim9compile.pro 2021-02-28 16:16:11.594135585 +0100 *************** *** 1,6 **** /* vim9compile.c */ int script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx); ! int check_defined(char_u *p, size_t len, cctx_T *cctx); int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2); int use_typecheck(type_T *actual, type_T *expected); int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); --- 1,6 ---- /* vim9compile.c */ int script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx); ! int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg); int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2); int use_typecheck(type_T *actual, type_T *expected); int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); *** ../vim-8.2.2557/src/userfunc.c 2021-02-23 12:05:40.910627834 +0100 --- src/userfunc.c 2021-02-28 16:16:34.086043138 +0100 *************** *** 55,60 **** --- 55,61 ---- * Get one function argument. * If "argtypes" is not NULL also get the type: "arg: type". * 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. * Return a pointer to after the type. * When something is wrong return "arg". */ *************** *** 64,69 **** --- 65,71 ---- garray_T *newargs, garray_T *argtypes, int types_optional, + evalarg_T *evalarg, int skip) { char_u *p = arg; *************** *** 81,93 **** return arg; } ! // Vim9 script: cannot use script var name for argument. ! if (!skip && argtypes != NULL && script_var_exists(arg, p - arg, ! FALSE, NULL) == OK) ! { ! semsg(_(e_variable_already_declared_in_script), arg); return arg; - } if (newargs != NULL && ga_grow(newargs, 1) == FAIL) return arg; --- 83,93 ---- return 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) return arg; if (newargs != NULL && ga_grow(newargs, 1) == FAIL) return arg; *************** *** 173,178 **** --- 173,179 ---- garray_T *newargs, garray_T *argtypes, // NULL unless using :def int types_optional, // types optional if "argtypes" is not NULL + evalarg_T *evalarg, // context or NULL int *varargs, garray_T *default_args, int skip, *************** *** 247,253 **** arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! skip); if (p == arg) break; if (*skipwhite(p) == '=') --- 248,254 ---- arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, skip); if (p == arg) break; if (*skipwhite(p) == '=') *************** *** 260,266 **** else { arg = p; ! p = one_function_arg(p, newargs, argtypes, types_optional, skip); if (p == arg) break; --- 261,268 ---- else { arg = p; ! p = one_function_arg(p, newargs, argtypes, types_optional, ! evalarg, skip); if (p == arg) break; *************** *** 576,582 **** // be found after the arguments. s = *arg + 1; ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, ! types_optional ? &argtypes : NULL, types_optional, NULL, NULL, TRUE, NULL, NULL); if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL) { --- 578,584 ---- // be found after the arguments. s = *arg + 1; ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, ! types_optional ? &argtypes : NULL, types_optional, evalarg, NULL, NULL, TRUE, NULL, NULL); if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL) { *************** *** 592,598 **** pnewargs = NULL; *arg += 1; ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, ! types_optional ? &argtypes : NULL, types_optional, &varargs, NULL, FALSE, NULL, NULL); if (ret == FAIL || (s = skip_arrow(*arg, equal_arrow, &ret_type, --- 594,600 ---- pnewargs = NULL; *arg += 1; ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, ! types_optional ? &argtypes : NULL, types_optional, evalarg, &varargs, NULL, FALSE, NULL, NULL); if (ret == FAIL || (s = skip_arrow(*arg, equal_arrow, &ret_type, *************** *** 683,689 **** fp->uf_refcount = 1; set_ufunc_name(fp, name); - hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; ga_init(&fp->uf_def_args); if (types_optional) --- 685,690 ---- *************** *** 726,731 **** --- 727,734 ---- pt->pt_refcount = 1; rettv->vval.v_partial = pt; rettv->v_type = VAR_PARTIAL; + + hash_add(&func_hashtab, UF2HIKEY(fp)); } eval_lavars_used = old_eval_lavars; *************** *** 3278,3284 **** ++p; if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, ! &varargs, &default_args, eap->skip, eap, &line_to_free) == FAIL) goto errret_2; whitep = p; --- 3281,3287 ---- ++p; if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, ! NULL, &varargs, &default_args, eap->skip, eap, &line_to_free) == FAIL) goto errret_2; whitep = p; *** ../vim-8.2.2557/src/vim9script.c 2021-02-27 23:39:17.264324205 +0100 --- src/vim9script.c 2021-02-28 16:15:48.558230896 +0100 *************** *** 370,376 **** if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; ! if (check_defined(p, arg - p, cctx) == FAIL) goto erret; as_name = vim_strnsave(p, arg - p); arg = skipwhite_and_linebreak(arg, evalarg); --- 370,376 ---- if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; ! if (check_defined(p, arg - p, cctx, FALSE) == FAIL) goto erret; as_name = vim_strnsave(p, arg - p); arg = skipwhite_and_linebreak(arg, evalarg); *************** *** 555,561 **** } else { ! if (check_defined(name, len, cctx) == FAIL) goto erret; imported = new_imported(gap != NULL ? gap --- 555,561 ---- } else { ! if (check_defined(name, len, cctx, FALSE) == FAIL) goto erret; imported = new_imported(gap != NULL ? gap *************** *** 567,573 **** { imported->imp_name = name; ((char_u **)names.ga_data)[i] = NULL; ! } else { // "import This as That ..." --- 567,573 ---- { imported->imp_name = name; ((char_u **)names.ga_data)[i] = NULL; ! } else { // "import This as That ..." *** ../vim-8.2.2557/src/errors.h 2021-02-20 17:03:57.980112625 +0100 --- src/errors.h 2021-02-28 16:42:45.915838092 +0100 *************** *** 147,153 **** INIT(= N_("E1052: Cannot declare an option: %s")); EXTERN char e_could_not_import_str[] INIT(= N_("E1053: Could not import \"%s\"")); ! EXTERN char e_variable_already_declared_in_script[] INIT(= N_("E1054: Variable already declared in the script: %s")); EXTERN char e_missing_name_after_dots[] INIT(= N_("E1055: Missing name after ...")); --- 147,153 ---- INIT(= N_("E1052: Cannot declare an option: %s")); EXTERN char e_could_not_import_str[] INIT(= N_("E1053: Could not import \"%s\"")); ! EXTERN char e_variable_already_declared_in_script_str[] INIT(= N_("E1054: Variable already declared in the script: %s")); EXTERN char e_missing_name_after_dots[] INIT(= N_("E1055: Missing name after ...")); *************** *** 369,371 **** --- 369,375 ---- INIT(= N_("E1165: Cannot use a range with an assignment: %s")); EXTERN char e_cannot_use_range_with_dictionary[] INIT(= N_("E1166: Cannot use a range with a dictionary")); + EXTERN char e_argument_name_shadows_existing_variable_str[] + INIT(= N_("E1167: Argument name shadows existing variable: %s")); + EXTERN char e_argument_already_declared_in_script_str[] + INIT(= N_("E1168: Argument already declared in the script: %s")); *** ../vim-8.2.2557/src/testdir/test_vim9_func.vim 2021-02-21 22:20:18.865587667 +0100 --- src/testdir/test_vim9_func.vim 2021-02-28 16:43:08.755748418 +0100 *************** *** 596,602 **** echo nr enddef END ! CheckScriptFailure(lines, 'E1054:') lines =<< trim END vim9script --- 596,602 ---- echo nr enddef END ! CheckScriptFailure(lines, 'E1168:') lines =<< trim END vim9script *************** *** 699,704 **** --- 699,729 ---- Ref = (x, y, z) => 0 END CheckDefAndScriptFailure(lines, 'E1012:') + + lines =<< trim END + var one = 1 + var l = [1, 2, 3] + echo map(l, (one) => one) + END + CheckDefFailure(lines, 'E1167:') + CheckScriptFailure(['vim9script'] + lines, 'E1168:') + + lines =<< trim END + def ShadowLocal() + var one = 1 + var l = [1, 2, 3] + echo map(l, (one) => one) + enddef + END + CheckDefFailure(lines, 'E1167:') + + lines =<< trim END + def Shadowarg(one: number) + var l = [1, 2, 3] + echo map(l, (one) => one) + enddef + END + CheckDefFailure(lines, 'E1167:') enddef def Test_lambda_return_type() *** ../vim-8.2.2557/src/testdir/test_vim9_expr.vim 2021-02-24 12:27:25.435824605 +0100 --- src/testdir/test_vim9_expr.vim 2021-02-28 16:23:41.552372401 +0100 *************** *** 2843,2849 **** # lambda method call l = [2, 5] ! l->((l) => add(l, 8))() assert_equal([2, 5, 8], l) # dict member --- 2843,2849 ---- # lambda method call l = [2, 5] ! l->((ll) => add(ll, 8))() assert_equal([2, 5, 8], l) # dict member *************** *** 3034,3041 **** enddef func Test_expr7_trailing_fails() ! call CheckDefFailure(['var l = [2]', 'l->((l) => add(l, 8))'], 'E107:', 2) ! call CheckDefFailure(['var l = [2]', 'l->((l) => add(l, 8)) ()'], 'E274:', 2) endfunc func Test_expr_fails() --- 3034,3041 ---- enddef func Test_expr7_trailing_fails() ! call CheckDefFailure(['var l = [2]', 'l->((ll) => add(ll, 8))'], 'E107:', 2) ! call CheckDefFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2) endfunc func Test_expr_fails() *** ../vim-8.2.2557/src/testdir/test_vim9_script.vim 2021-02-27 22:41:12.661749199 +0100 --- src/testdir/test_vim9_script.vim 2021-02-28 16:25:43.039918366 +0100 *************** *** 1119,1125 **** import exported from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim') # try to import something that is already defined import_already_defined =<< trim END --- 1119,1125 ---- import exported from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim') # try to import something that is already defined import_already_defined =<< trim END *************** *** 1128,1134 **** import * as exported from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim') # try to import something that is already defined import_already_defined =<< trim END --- 1128,1134 ---- import * as exported from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim') # try to import something that is already defined import_already_defined =<< trim END *************** *** 1137,1143 **** import {exported} from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim') # try changing an imported const var import_assign_to_const =<< trim END --- 1137,1143 ---- import {exported} from './Xexport.vim' END writefile(import_already_defined, 'Ximport.vim') ! assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim') # try changing an imported const var import_assign_to_const =<< trim END *** ../vim-8.2.2557/src/version.c 2021-02-27 23:39:17.264324205 +0100 --- src/version.c 2021-02-28 15:43:25.314473462 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2558, /**/ -- A programmer's wife asks him: "Please run to the store and pick up a loaf of bread. If they have eggs, get a dozen". The programmer comes home with 12 loafs of bread. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///