Skip to content

Commit 057e84a

Browse files
committed
patch 8.2.2558: no error if a lambda argument shadows a variable
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)
1 parent 087b5ff commit 057e84a

9 files changed

Lines changed: 80 additions & 33 deletions

File tree

src/errors.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ EXTERN char e_cannot_declare_an_option[]
147147
INIT(= N_("E1052: Cannot declare an option: %s"));
148148
EXTERN char e_could_not_import_str[]
149149
INIT(= N_("E1053: Could not import \"%s\""));
150-
EXTERN char e_variable_already_declared_in_script[]
150+
EXTERN char e_variable_already_declared_in_script_str[]
151151
INIT(= N_("E1054: Variable already declared in the script: %s"));
152152
EXTERN char e_missing_name_after_dots[]
153153
INIT(= N_("E1055: Missing name after ..."));
@@ -369,3 +369,7 @@ EXTERN char e_cannot_use_range_with_assignment_str[]
369369
INIT(= N_("E1165: Cannot use a range with an assignment: %s"));
370370
EXTERN char e_cannot_use_range_with_dictionary[]
371371
INIT(= N_("E1166: Cannot use a range with a dictionary"));
372+
EXTERN char e_argument_name_shadows_existing_variable_str[]
373+
INIT(= N_("E1167: Argument name shadows existing variable: %s"));
374+
EXTERN char e_argument_already_declared_in_script_str[]
375+
INIT(= N_("E1168: Argument already declared in the script: %s"));

src/proto/vim9compile.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* vim9compile.c */
22
int script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx);
3-
int check_defined(char_u *p, size_t len, cctx_T *cctx);
3+
int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg);
44
int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2);
55
int use_typecheck(type_T *actual, type_T *expected);
66
int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);

src/testdir/test_vim9_expr.vim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,7 +2843,7 @@ def Test_expr7_trailing()
28432843

28442844
# lambda method call
28452845
l = [2, 5]
2846-
l->((l) => add(l, 8))()
2846+
l->((ll) => add(ll, 8))()
28472847
assert_equal([2, 5, 8], l)
28482848

28492849
# dict member
@@ -3034,8 +3034,8 @@ def Test_expr7_subscript_linebreak()
30343034
enddef
30353035

30363036
func Test_expr7_trailing_fails()
3037-
call CheckDefFailure(['var l = [2]', 'l->((l) => add(l, 8))'], 'E107:', 2)
3038-
call CheckDefFailure(['var l = [2]', 'l->((l) => add(l, 8)) ()'], 'E274:', 2)
3037+
call CheckDefFailure(['var l = [2]', 'l->((ll) => add(ll, 8))'], 'E107:', 2)
3038+
call CheckDefFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2)
30393039
endfunc
30403040

30413041
func Test_expr_fails()

src/testdir/test_vim9_func.vim

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ def Test_call_wrong_args()
596596
echo nr
597597
enddef
598598
END
599-
CheckScriptFailure(lines, 'E1054:')
599+
CheckScriptFailure(lines, 'E1168:')
600600

601601
lines =<< trim END
602602
vim9script
@@ -699,6 +699,31 @@ def Test_call_lambda_args()
699699
Ref = (x, y, z) => 0
700700
END
701701
CheckDefAndScriptFailure(lines, 'E1012:')
702+
703+
lines =<< trim END
704+
var one = 1
705+
var l = [1, 2, 3]
706+
echo map(l, (one) => one)
707+
END
708+
CheckDefFailure(lines, 'E1167:')
709+
CheckScriptFailure(['vim9script'] + lines, 'E1168:')
710+
711+
lines =<< trim END
712+
def ShadowLocal()
713+
var one = 1
714+
var l = [1, 2, 3]
715+
echo map(l, (one) => one)
716+
enddef
717+
END
718+
CheckDefFailure(lines, 'E1167:')
719+
720+
lines =<< trim END
721+
def Shadowarg(one: number)
722+
var l = [1, 2, 3]
723+
echo map(l, (one) => one)
724+
enddef
725+
END
726+
CheckDefFailure(lines, 'E1167:')
702727
enddef
703728

704729
def Test_lambda_return_type()

src/testdir/test_vim9_script.vim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ def Test_vim9_import_export()
11191119
import exported from './Xexport.vim'
11201120
END
11211121
writefile(import_already_defined, 'Ximport.vim')
1122-
assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
1122+
assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')
11231123

11241124
# try to import something that is already defined
11251125
import_already_defined =<< trim END
@@ -1128,7 +1128,7 @@ def Test_vim9_import_export()
11281128
import * as exported from './Xexport.vim'
11291129
END
11301130
writefile(import_already_defined, 'Ximport.vim')
1131-
assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
1131+
assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')
11321132

11331133
# try to import something that is already defined
11341134
import_already_defined =<< trim END
@@ -1137,7 +1137,7 @@ def Test_vim9_import_export()
11371137
import {exported} from './Xexport.vim'
11381138
END
11391139
writefile(import_already_defined, 'Ximport.vim')
1140-
assert_fails('source Ximport.vim', 'E1073:', '', 3, 'Ximport.vim')
1140+
assert_fails('source Ximport.vim', 'E1054:', '', 3, 'Ximport.vim')
11411141

11421142
# try changing an imported const
11431143
var import_assign_to_const =<< trim END

src/userfunc.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func_tbl_get(void)
5555
* Get one function argument.
5656
* If "argtypes" is not NULL also get the type: "arg: type".
5757
* If "types_optional" is TRUE a missing type is OK, use "any".
58+
* If "evalarg" is not NULL use it to check for an already declared name.
5859
* Return a pointer to after the type.
5960
* When something is wrong return "arg".
6061
*/
@@ -64,6 +65,7 @@ one_function_arg(
6465
garray_T *newargs,
6566
garray_T *argtypes,
6667
int types_optional,
68+
evalarg_T *evalarg,
6769
int skip)
6870
{
6971
char_u *p = arg;
@@ -81,13 +83,11 @@ one_function_arg(
8183
return arg;
8284
}
8385

84-
// Vim9 script: cannot use script var name for argument.
85-
if (!skip && argtypes != NULL && script_var_exists(arg, p - arg,
86-
FALSE, NULL) == OK)
87-
{
88-
semsg(_(e_variable_already_declared_in_script), arg);
86+
// Vim9 script: cannot use script var name for argument. In function: also
87+
// check local vars and arguments.
88+
if (!skip && argtypes != NULL && check_defined(arg, p - arg,
89+
evalarg == NULL ? NULL : evalarg->eval_cctx, TRUE) == FAIL)
8990
return arg;
90-
}
9191

9292
if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
9393
return arg;
@@ -173,6 +173,7 @@ get_function_args(
173173
garray_T *newargs,
174174
garray_T *argtypes, // NULL unless using :def
175175
int types_optional, // types optional if "argtypes" is not NULL
176+
evalarg_T *evalarg, // context or NULL
176177
int *varargs,
177178
garray_T *default_args,
178179
int skip,
@@ -247,7 +248,7 @@ get_function_args(
247248

248249
arg = p;
249250
p = one_function_arg(p, newargs, argtypes, types_optional,
250-
skip);
251+
evalarg, skip);
251252
if (p == arg)
252253
break;
253254
if (*skipwhite(p) == '=')
@@ -260,7 +261,8 @@ get_function_args(
260261
else
261262
{
262263
arg = p;
263-
p = one_function_arg(p, newargs, argtypes, types_optional, skip);
264+
p = one_function_arg(p, newargs, argtypes, types_optional,
265+
evalarg, skip);
264266
if (p == arg)
265267
break;
266268

@@ -576,7 +578,7 @@ get_lambda_tv(
576578
// be found after the arguments.
577579
s = *arg + 1;
578580
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
579-
types_optional ? &argtypes : NULL, types_optional,
581+
types_optional ? &argtypes : NULL, types_optional, evalarg,
580582
NULL, NULL, TRUE, NULL, NULL);
581583
if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
582584
{
@@ -592,7 +594,7 @@ get_lambda_tv(
592594
pnewargs = NULL;
593595
*arg += 1;
594596
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
595-
types_optional ? &argtypes : NULL, types_optional,
597+
types_optional ? &argtypes : NULL, types_optional, evalarg,
596598
&varargs, NULL, FALSE, NULL, NULL);
597599
if (ret == FAIL
598600
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
@@ -683,7 +685,6 @@ get_lambda_tv(
683685

684686
fp->uf_refcount = 1;
685687
set_ufunc_name(fp, name);
686-
hash_add(&func_hashtab, UF2HIKEY(fp));
687688
fp->uf_args = newargs;
688689
ga_init(&fp->uf_def_args);
689690
if (types_optional)
@@ -726,6 +727,8 @@ get_lambda_tv(
726727
pt->pt_refcount = 1;
727728
rettv->vval.v_partial = pt;
728729
rettv->v_type = VAR_PARTIAL;
730+
731+
hash_add(&func_hashtab, UF2HIKEY(fp));
729732
}
730733

731734
eval_lavars_used = old_eval_lavars;
@@ -3278,7 +3281,7 @@ define_function(exarg_T *eap, char_u *name_arg)
32783281
++p;
32793282
if (get_function_args(&p, ')', &newargs,
32803283
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
3281-
&varargs, &default_args, eap->skip,
3284+
NULL, &varargs, &default_args, eap->skip,
32823285
eap, &line_to_free) == FAIL)
32833286
goto errret_2;
32843287
whitep = p;

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2558,
753755
/**/
754756
2557,
755757
/**/

src/vim9compile.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,9 @@ script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx)
379379
static int
380380
variable_exists(char_u *name, size_t len, cctx_T *cctx)
381381
{
382-
return lookup_local(name, len, NULL, cctx) == OK
383-
|| arg_exists(name, len, NULL, NULL, NULL, cctx) == OK
382+
return (cctx != NULL
383+
&& (lookup_local(name, len, NULL, cctx) == OK
384+
|| arg_exists(name, len, NULL, NULL, NULL, cctx) == OK))
384385
|| script_var_exists(name, len, FALSE, cctx) == OK
385386
|| find_imported(name, len, cctx) != NULL;
386387
}
@@ -389,17 +390,26 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
389390
* Check if "p[len]" is already defined, either in script "import_sid" or in
390391
* compilation context "cctx". "cctx" is NULL at the script level.
391392
* Does not check the global namespace.
393+
* If "is_arg" is TRUE the error message is for an argument name.
392394
* Return FAIL and give an error if it defined.
393395
*/
394396
int
395-
check_defined(char_u *p, size_t len, cctx_T *cctx)
397+
check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
396398
{
397399
int c = p[len];
398400
ufunc_T *ufunc = NULL;
399401

402+
if (script_var_exists(p, len, FALSE, cctx) == OK)
403+
{
404+
if (is_arg)
405+
semsg(_(e_argument_already_declared_in_script_str), p);
406+
else
407+
semsg(_(e_variable_already_declared_in_script_str), p);
408+
return FAIL;
409+
}
410+
400411
p[len] = NUL;
401-
if (script_var_exists(p, len, FALSE, cctx) == OK
402-
|| (cctx != NULL
412+
if ((cctx != NULL
403413
&& (lookup_local(p, len, NULL, cctx) == OK
404414
|| arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
405415
|| find_imported(p, len, cctx) != NULL
@@ -409,8 +419,11 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
409419
if (ufunc == NULL || !func_is_global(ufunc)
410420
|| (p[0] == 'g' && p[1] == ':'))
411421
{
422+
if (is_arg)
423+
semsg(_(e_argument_name_shadows_existing_variable_str), p);
424+
else
425+
semsg(_(e_name_already_defined_str), p);
412426
p[len] = c;
413-
semsg(_(e_name_already_defined_str), p);
414427
return FAIL;
415428
}
416429
}
@@ -5120,7 +5133,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
51205133
semsg(_(e_namespace_not_supported_str), name_start);
51215134
return NULL;
51225135
}
5123-
if (check_defined(name_start, name_end - name_start, cctx) == FAIL)
5136+
if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL)
51245137
return NULL;
51255138

51265139
eap->arg = name_end;
@@ -5686,7 +5699,7 @@ compile_lhs(
56865699
semsg(_(e_cannot_declare_script_variable_in_function),
56875700
lhs->lhs_name);
56885701
else
5689-
semsg(_(e_variable_already_declared_in_script),
5702+
semsg(_(e_variable_already_declared_in_script_str),
56905703
lhs->lhs_name);
56915704
return FAIL;
56925705
}
@@ -5723,7 +5736,7 @@ compile_lhs(
57235736
}
57245737
}
57255738
}
5726-
else if (check_defined(var_start, lhs->lhs_varlen, cctx)
5739+
else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE)
57275740
== FAIL)
57285741
return FAIL;
57295742
}

src/vim9script.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ handle_import(
370370
if (eval_isnamec1(*arg))
371371
while (eval_isnamec(*arg))
372372
++arg;
373-
if (check_defined(p, arg - p, cctx) == FAIL)
373+
if (check_defined(p, arg - p, cctx, FALSE) == FAIL)
374374
goto erret;
375375
as_name = vim_strnsave(p, arg - p);
376376
arg = skipwhite_and_linebreak(arg, evalarg);
@@ -555,7 +555,7 @@ handle_import(
555555
}
556556
else
557557
{
558-
if (check_defined(name, len, cctx) == FAIL)
558+
if (check_defined(name, len, cctx, FALSE) == FAIL)
559559
goto erret;
560560

561561
imported = new_imported(gap != NULL ? gap
@@ -567,7 +567,7 @@ handle_import(
567567
{
568568
imported->imp_name = name;
569569
((char_u **)names.ga_data)[i] = NULL;
570-
}
570+
}
571571
else
572572
{
573573
// "import This as That ..."

0 commit comments

Comments
 (0)