]> granicus.if.org Git - vim/commitdiff
patch 8.2.2558: no error if a lambda argument shadows a variable v8.2.2558
authorBram Moolenaar <Bram@vim.org>
Sun, 28 Feb 2021 15:55:11 +0000 (16:55 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 28 Feb 2021 15:55:11 +0000 (16:55 +0100)
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)

src/errors.h
src/proto/vim9compile.pro
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_func.vim
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9compile.c
src/vim9script.c

index 775c5324b03945750314aa7e4f95272abf278701..db958e05419a69b604af147c786bd38ec802f9d7 100644 (file)
@@ -147,7 +147,7 @@ EXTERN char e_cannot_declare_an_option[]
        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[]
+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,3 +369,7 @@ EXTERN char e_cannot_use_range_with_assignment_str[]
        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"));
index 410910f001966a5fd09ea26591e0f1849a8b0bcd..4f2fffe686f84969f8a746c904ea80a2a5f5b96d 100644 (file)
@@ -1,6 +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_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);
index cda1ffdb4f58b13a170c0df9657093178de39715..26a69324220e6eb938bf722ef1d0cb399000b9ac 100644 (file)
@@ -2843,7 +2843,7 @@ def Test_expr7_trailing()
 
   # lambda method call
   l = [2, 5]
-  l->((l) => add(l, 8))()
+  l->((ll) => add(ll, 8))()
   assert_equal([2, 5, 8], l)
 
   # dict member
@@ -3034,8 +3034,8 @@ def Test_expr7_subscript_linebreak()
 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)
+  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()
index 05becd72c2431dd81a6f9d77a85bdaa2e8e9c0f4..bc65bb163ec51e906052c921875b27bc5cc7b9c3 100644 (file)
@@ -596,7 +596,7 @@ def Test_call_wrong_args()
       echo nr
     enddef
   END
-  CheckScriptFailure(lines, 'E1054:')
+  CheckScriptFailure(lines, 'E1168:')
 
   lines =<< trim END
     vim9script
@@ -699,6 +699,31 @@ def Test_call_lambda_args()
     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()
index 629d0b8363ed0e1c432f85988135cc271a1e6158..5d1e4015ada0ddf5926f05db7e647f38ac6ce9eb 100644 (file)
@@ -1119,7 +1119,7 @@ def Test_vim9_import_export()
     import exported from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
-  assert_fails('source Ximport.vim', 'E1073:', '', 3, '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,7 +1128,7 @@ def Test_vim9_import_export()
     import * as exported from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
-  assert_fails('source Ximport.vim', 'E1073:', '', 3, '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,7 +1137,7 @@ def Test_vim9_import_export()
     import {exported} from './Xexport.vim'
   END
   writefile(import_already_defined, 'Ximport.vim')
-  assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
+  assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')
 
   # try changing an imported const
   var import_assign_to_const =<< trim END
index b49cef17846a90f15db4527c0a73462ac9baca38..04c2641203f6bd4132f4632a5dec518bf3c68474 100644 (file)
@@ -55,6 +55,7 @@ func_tbl_get(void)
  * 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,6 +65,7 @@ one_function_arg(
        garray_T    *newargs,
        garray_T    *argtypes,
        int         types_optional,
+       evalarg_T   *evalarg,
        int         skip)
 {
     char_u     *p = arg;
@@ -81,13 +83,11 @@ one_function_arg(
        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);
+    // 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,6 +173,7 @@ get_function_args(
     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,7 +248,7 @@ get_function_args(
 
                arg = p;
                p = one_function_arg(p, newargs, argtypes, types_optional,
-                                                                        skip);
+                                                               evalarg, skip);
                if (p == arg)
                    break;
                if (*skipwhite(p) == '=')
@@ -260,7 +261,8 @@ get_function_args(
        else
        {
            arg = p;
-           p = one_function_arg(p, newargs, argtypes, types_optional, skip);
+           p = one_function_arg(p, newargs, argtypes, types_optional,
+                                                               evalarg, skip);
            if (p == arg)
                break;
 
@@ -576,7 +578,7 @@ get_lambda_tv(
     // be found after the arguments.
     s = *arg + 1;
     ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
-           types_optional ? &argtypes : NULL, types_optional,
+           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,7 +594,7 @@ get_lambda_tv(
        pnewargs = NULL;
     *arg += 1;
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
-           types_optional ? &argtypes : NULL, types_optional,
+           types_optional ? &argtypes : NULL, types_optional, evalarg,
                                            &varargs, NULL, FALSE, NULL, NULL);
     if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
@@ -683,7 +685,6 @@ get_lambda_tv(
 
        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)
@@ -726,6 +727,8 @@ get_lambda_tv(
        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,7 +3281,7 @@ define_function(exarg_T *eap, char_u *name_arg)
     ++p;
     if (get_function_args(&p, ')', &newargs,
                        eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
-                        &varargs, &default_args, eap->skip,
+                        NULL, &varargs, &default_args, eap->skip,
                         eap, &line_to_free) == FAIL)
        goto errret_2;
     whitep = p;
index 8eead06d5216c3b3e0b46a962ff9b4ae99d61401..b623c42187cf67667bae0b345e56c88acc525daa 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2558,
 /**/
     2557,
 /**/
index 0be2b29d8b7a40c02f9d7b0523b8806a695438a9..f7bdf313c7f7c5b1af2e689c4e423cad77233cf6 100644 (file)
@@ -379,8 +379,9 @@ script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx)
     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
+    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,17 +390,26 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
  * 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)
+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 (script_var_exists(p, len, FALSE, cctx) == OK
-           || (cctx != NULL
+    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,8 +419,11 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
        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;
-           semsg(_(e_name_already_defined_str), p);
            return FAIL;
        }
     }
@@ -5120,7 +5133,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
        semsg(_(e_namespace_not_supported_str), name_start);
        return NULL;
     }
-    if (check_defined(name_start, name_end - name_start, cctx) == FAIL)
+    if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL)
        return NULL;
 
     eap->arg = name_end;
@@ -5686,7 +5699,7 @@ compile_lhs(
                            semsg(_(e_cannot_declare_script_variable_in_function),
                                                                lhs->lhs_name);
                        else
-                           semsg(_(e_variable_already_declared_in_script),
+                           semsg(_(e_variable_already_declared_in_script_str),
                                                                lhs->lhs_name);
                        return FAIL;
                    }
@@ -5723,7 +5736,7 @@ compile_lhs(
                        }
                    }
                }
-               else if (check_defined(var_start, lhs->lhs_varlen, cctx)
+               else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE)
                                                                       == FAIL)
                    return FAIL;
            }
index 39d2eeb11bba871db15b0a183190df4ad459fe54..68d7f5e19567a5d063b13339ffe5490d399d798f 100644 (file)
@@ -370,7 +370,7 @@ handle_import(
            if (eval_isnamec1(*arg))
                while (eval_isnamec(*arg))
                    ++arg;
-           if (check_defined(p, arg - p, cctx) == FAIL)
+           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,7 +555,7 @@ handle_import(
            }
            else
            {
-               if (check_defined(name, len, cctx) == FAIL)
+               if (check_defined(name, len, cctx, FALSE) == FAIL)
                    goto erret;
 
                imported = new_imported(gap != NULL ? gap
@@ -567,7 +567,7 @@ handle_import(
                {
                    imported->imp_name = name;
                    ((char_u **)names.ga_data)[i] = NULL;
-               } 
+               }
                else
                {
                    // "import This as That ..."