diff src/vim9compile.c @ 22236:3d0632b260fd v8.2.1667

patch 8.2.1667: local function name cannot shadow a global function name Commit: https://github.com/vim/vim/commit/0f769815c82bf93812842e1ad56fcc52c10ff3e5 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 12 18:32:34 2020 +0200 patch 8.2.1667: local function name cannot shadow a global function name Problem: Local function name cannot shadow a global function name. Solution: Ignore global functions when checking a script-local or scoped function name. (closes #6926)
author Bram Moolenaar <Bram@vim.org>
date Sat, 12 Sep 2020 18:45:04 +0200
parents f9b4576a618b
children d7c1e3efa88e
line wrap: on
line diff
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -292,12 +292,14 @@ lookup_script(char_u *name, size_t len, 
 /*
  * Check if "p[len]" is already defined, either in script "import_sid" or in
  * compilation context "cctx".
+ * 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];
+    int		c = p[len];
+    ufunc_T	*ufunc = NULL;
 
     p[len] = NUL;
     if (lookup_script(p, len, FALSE) == OK
@@ -305,11 +307,16 @@ check_defined(char_u *p, size_t len, cct
 		&& (lookup_local(p, len, cctx) != NULL
 		    || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
 	    || find_imported(p, len, cctx) != NULL
-	    || find_func_even_dead(p, FALSE, cctx) != NULL)
-    {
-	p[len] = c;
-	semsg(_(e_name_already_defined_str), p);
-	return FAIL;
+	    || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
+    {
+	// A local or script-local function can shadow a global function.
+	if (ufunc == NULL || !func_is_global(ufunc)
+		|| (p[0] == 'g' && p[1] == ':'))
+	{
+	    p[len] = c;
+	    semsg(_(e_name_already_defined_str), p);
+	    return FAIL;
+	}
     }
     p[len] = c;
     return OK;
@@ -2114,10 +2121,16 @@ generate_funcref(cctx_T *cctx, char_u *n
 /*
  * Compile a variable name into a load instruction.
  * "end" points to just after the name.
+ * "is_expr" is TRUE when evaluating an expression, might be a funcref.
  * When "error" is FALSE do not give an error when not found.
  */
     static int
-compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
+compile_load(
+	char_u **arg,
+	char_u *end_arg,
+	cctx_T	*cctx,
+	int	is_expr,
+	int	error)
 {
     type_T	*type;
     char_u	*name = NULL;
@@ -2214,10 +2227,11 @@ compile_load(char_u **arg, char_u *end_a
 			|| find_imported(name, 0, cctx) != NULL)
 		   res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
 
-		// When the name starts with an uppercase letter or "x:" it
-		// can be a user defined function.
+		// When evaluating an expression and the name starts with an
+		// uppercase letter or "x:" it can be a user defined function.
 		// TODO: this is just guessing
-		if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
+		if (res == FAIL && is_expr
+				   && (ASCII_ISUPPER(*name) || name[1] == ':'))
 		    res = generate_funcref(cctx, name);
 	    }
 	}
@@ -2368,8 +2382,9 @@ compile_call(
     }
 
     // If we can find the function by name generate the right call.
+    // Skip global functions here, a local funcref takes precedence.
     ufunc = find_func(name, FALSE, cctx);
-    if (ufunc != NULL)
+    if (ufunc != NULL && !func_is_global(ufunc))
     {
 	res = generate_CALL(cctx, ufunc, argcount);
 	goto theend;
@@ -2380,7 +2395,7 @@ compile_call(
     // Not for eome#Func(), it will be loaded later.
     p = namebuf;
     if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload
-	    && compile_load(&p, namebuf + varlen, cctx, FALSE) == OK)
+	    && compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK)
     {
 	garray_T    *stack = &cctx->ctx_type_stack;
 	type_T	    *type;
@@ -2390,6 +2405,13 @@ compile_call(
 	goto theend;
     }
 
+    // If we can find a global function by name generate the right call.
+    if (ufunc != NULL)
+    {
+	res = generate_CALL(cctx, ufunc, argcount);
+	goto theend;
+    }
+
     // A global function may be defined only later.  Need to figure out at
     // runtime.  Also handles a FuncRef at runtime.
     if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload)
@@ -3548,7 +3570,7 @@ compile_expr7(
 	{
 	    if (generate_ppconst(cctx, ppconst) == FAIL)
 		return FAIL;
-	    r = compile_load(arg, p, cctx, TRUE);
+	    r = compile_load(arg, p, cctx, TRUE, TRUE);
 	}
 	if (r == FAIL)
 	    return FAIL;
@@ -5002,6 +5024,11 @@ compile_assignment(char_u *arg, exarg_T 
 			      : ((type_T **)stack->ga_data)[stack->ga_len - 1];
 		if (lvar != NULL && (is_decl || !has_type))
 		{
+		    if ((stacktype->tt_type == VAR_FUNC
+				|| stacktype->tt_type == VAR_PARTIAL)
+			    && var_wrong_func_name(name, TRUE))
+			goto theend;
+
 		    if (new_local && !has_type)
 		    {
 			if (stacktype->tt_type == VAR_VOID)
@@ -5009,12 +5036,6 @@ compile_assignment(char_u *arg, exarg_T 
 			    emsg(_(e_cannot_use_void_value));
 			    goto theend;
 			}
-			else if ((stacktype->tt_type == VAR_FUNC
-				    || stacktype->tt_type == VAR_PARTIAL)
-					    && var_wrong_func_name(name, TRUE))
-			{
-			    goto theend;
-			}
 			else
 			{
 			    // An empty list or dict has a &t_void member,