changeset 20275:350bb78345ba v8.2.0693

patch 8.2.0693: closure using argument not tested Commit: https://github.com/vim/vim/commit/2fd4cd755c3e87e733b7363ac13e5c0fe0297a80 Author: Bram Moolenaar <Bram@vim.org> Date: Sun May 3 22:30:49 2020 +0200 patch 8.2.0693: closure using argument not tested Problem: Closure using argument not tested. Solution: Add a test, make it work.
author Bram Moolenaar <Bram@vim.org>
date Sun, 03 May 2020 22:45:03 +0200
parents ffb9ba069032
children 36edcb98d341
files src/testdir/test_vim9_func.vim src/version.c src/vim9compile.c
diffstat 3 files changed, 85 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -700,5 +700,26 @@ def Test_closure_two_indirect_refs()
   unlet g:Read
 enddef
 
+def MakeArgRefs(theArg: string)
+  let local = 'loc_val'
+  g:UseArg = {s -> theArg .. '/' .. local .. '/' .. s}
+enddef
+
+def MakeArgRefsVarargs(theArg: string, ...rest: list<string>)
+  let local = 'the_loc'
+  g:UseVararg = {s -> theArg .. '/' .. local .. '/' .. s .. '/' .. join(rest)}
+enddef
+
+def Test_closure_using_argument()
+  MakeArgRefs('arg_val')
+  assert_equal('arg_val/loc_val/call_val', g:UseArg('call_val'))
+
+  MakeArgRefsVarargs('arg_val', 'one', 'two')
+  assert_equal('arg_val/the_loc/call_val/one two', g:UseVararg('call_val'))
+
+  unlet g:UseArg
+  unlet g:UseVararg
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- 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 */
 /**/
+    693,
+/**/
     692,
 /**/
     691,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -186,37 +186,74 @@ lookup_local(char_u *name, size_t len, c
 }
 
 /*
- * Lookup an argument in the current function.
- * Returns the argument index or -1 if not found.
+ * Lookup an argument in the current function and an enclosing function.
+ * Returns the argument index in "idxp"
+ * Returns the argument type in "type"
+ * Sets "gen_load_outer" to TRUE if found in outer scope.
+ * Returns OK when found, FAIL otherwise.
  */
     static int
-lookup_arg(char_u *name, size_t len, cctx_T *cctx)
+lookup_arg(
+	char_u	*name,
+	size_t	len,
+	int	*idxp,
+	type_T	**type,
+	int	*gen_load_outer,
+	cctx_T	*cctx)
 {
     int	    idx;
+    char_u  *va_name;
 
     if (len == 0)
-	return -1;
+	return FAIL;
     for (idx = 0; idx < cctx->ctx_ufunc->uf_args.ga_len; ++idx)
     {
 	char_u *arg = FUNCARG(cctx->ctx_ufunc, idx);
 
-	if (STRNCMP(name, arg, len) == 0 && STRLEN(arg) == len)
-	    return idx;
-    }
-    return -1;
-}
-
-/*
- * Lookup a vararg argument in the current function.
- * Returns TRUE if there is a match.
- */
-    static int
-lookup_vararg(char_u *name, size_t len, cctx_T *cctx)
-{
-    char_u  *va_name = cctx->ctx_ufunc->uf_va_name;
-
-    return len > 0 && va_name != NULL
-		 && STRNCMP(name, va_name, len) == 0 && STRLEN(va_name) == len;
+	if (STRNCMP(name, arg, len) == 0 && arg[len] == NUL)
+	{
+	    if (idxp != NULL)
+	    {
+		// Arguments are located above the frame pointer.  One further
+		// if there is a vararg argument
+		*idxp = idx - (cctx->ctx_ufunc->uf_args.ga_len
+							    + STACK_FRAME_SIZE)
+			      + (cctx->ctx_ufunc->uf_va_name != NULL ? -1 : 0);
+
+		if (cctx->ctx_ufunc->uf_arg_types != NULL)
+		    *type = cctx->ctx_ufunc->uf_arg_types[idx];
+		else
+		    *type = &t_any;
+	    }
+	    return OK;
+	}
+    }
+
+    va_name = cctx->ctx_ufunc->uf_va_name;
+    if (va_name != NULL
+		    && STRNCMP(name, va_name, len) == 0 && va_name[len] == NUL)
+    {
+	if (idxp != NULL)
+	{
+	    // varargs is always the last argument
+	    *idxp = -STACK_FRAME_SIZE - 1;
+	    *type = cctx->ctx_ufunc->uf_va_type;
+	}
+	return OK;
+    }
+
+    if (cctx->ctx_outer != NULL)
+    {
+	// Lookup the name for an argument of the outer function.
+	if (lookup_arg(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
+									 == OK)
+	{
+	    *gen_load_outer = TRUE;
+	    return OK;
+	}
+    }
+
+    return FAIL;
 }
 
 /*
@@ -1584,7 +1621,7 @@ reserve_local(cctx_T *cctx, char_u *name
 {
     lvar_T  *lvar;
 
-    if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx))
+    if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK)
     {
 	emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len);
 	return NULL;
@@ -2452,26 +2489,10 @@ compile_load(char_u **arg, char_u *end_a
 	if (name == NULL)
 	    return FAIL;
 
-	idx = lookup_arg(*arg, len, cctx);
-	if (idx >= 0)
+	if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
 	{
-	    if (cctx->ctx_ufunc->uf_arg_types != NULL)
-		type = cctx->ctx_ufunc->uf_arg_types[idx];
-	    else
-		type = &t_any;
-
-	    // Arguments are located above the frame pointer.
-	    idx -= cctx->ctx_ufunc->uf_args.ga_len + STACK_FRAME_SIZE;
-	    if (cctx->ctx_ufunc->uf_va_name != NULL)
-		--idx;
-	    gen_load = TRUE;
-	}
-	else if (lookup_vararg(*arg, len, cctx))
-	{
-	    // varargs is always the last argument
-	    idx = -STACK_FRAME_SIZE - 1;
-	    type = cctx->ctx_ufunc->uf_va_type;
-	    gen_load = TRUE;
+	    if (!gen_load_outer)
+		gen_load = TRUE;
 	}
 	else
 	{