]> granicus.if.org Git - vim/commitdiff
patch 8.2.4202: Vim9: cannot export function that exists globally v8.2.4202
authorBram Moolenaar <Bram@vim.org>
Mon, 24 Jan 2022 13:54:45 +0000 (13:54 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 24 Jan 2022 13:54:45 +0000 (13:54 +0000)
Problem:    Vim9: cannot export function that exists globally.
Solution:   When checking if a function already exists only check for
            script-local functions. (closes #9615)

src/proto/userfunc.pro
src/testdir/test_vim9_import.vim
src/userfunc.c
src/version.c
src/vim.h
src/vim9compile.c
src/vim9instr.c

index 475b70af4c5b12ea55aec5cdb1dfba13133ccbf1..464d7f239a805260432d42a3725e1ebab5a81d4c 100644 (file)
@@ -8,7 +8,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
 void emsg_funcname(char *ermsg, char_u *name);
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
 char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
-ufunc_T *find_func_even_dead(char_u *name, int is_global);
+ufunc_T *find_func_even_dead(char_u *name, int flags);
 ufunc_T *find_func(char_u *name, int is_global);
 int func_is_global(ufunc_T *ufunc);
 int func_name_refcount(char_u *name);
index 2ad5057565308a6ffb5f74671339359f3f24f1c4..4e0b5332e6baebf2dff51b38f73079ff1c6682c3 100644 (file)
@@ -965,6 +965,37 @@ def Run_Test_import_in_spellsuggest_expr()
   set nospell spellsuggest& verbose=0
 enddef
 
+def Test_export_shadows_global_function()
+  mkdir('Xdir/autoload', 'p')
+  var save_rtp = &rtp
+  exe 'set rtp^=' .. getcwd() .. '/Xdir'
+
+  var lines =<< trim END
+      vim9script
+      export def Shadow(): string
+        return 'Shadow()'
+      enddef
+  END
+  writefile(lines, 'Xdir/autoload/shadow.vim')
+
+  lines =<< trim END
+      vim9script
+
+      def g:Shadow(): string
+        return 'global'
+      enddef
+
+      import autoload 'shadow.vim'
+      assert_equal('Shadow()', shadow.Shadow())
+  END
+  CheckScriptSuccess(lines)
+
+  delfunc g:Shadow
+  bwipe!
+  delete('Xdir', 'rf')
+  &rtp = save_rtp
+enddef
+
 def Test_export_fails()
   CheckScriptFailure(['export var some = 123'], 'E1042:')
   CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')
index 650dfc9d129160de06876dbed1bbb4dfe71a4c45..8c204398ca017096634fcf592435a39367bf597b 100644 (file)
@@ -1941,16 +1941,18 @@ find_func_with_prefix(char_u *name, int sid)
 
 /*
  * Find a function by name, return pointer to it in ufuncs.
- * When "is_global" is true don't find script-local or imported functions.
+ * When "flags" has FFED_IS_GLOBAL don't find script-local or imported
+ * functions.
+ * When "flags" has "FFED_NO_GLOBAL" don't find global functions.
  * Return NULL for unknown function.
  */
     ufunc_T *
-find_func_even_dead(char_u *name, int is_global)
+find_func_even_dead(char_u *name, int flags)
 {
     hashitem_T *hi;
     ufunc_T    *func;
 
-    if (!is_global)
+    if ((flags & FFED_IS_GLOBAL) == 0)
     {
        int     find_script_local = in_vim9script() && eval_isnamec1(*name)
                                           && (name[1] != ':' || *name == 's');
@@ -1965,10 +1967,13 @@ find_func_even_dead(char_u *name, int is_global)
        }
     }
 
-    hi = hash_find(&func_hashtab,
+    if ((flags & FFED_NO_GLOBAL) == 0)
+    {
+       hi = hash_find(&func_hashtab,
                                STRNCMP(name, "g:", 2) == 0 ? name + 2 : name);
-    if (!HASHITEM_EMPTY(hi))
-       return HI2UF(hi);
+       if (!HASHITEM_EMPTY(hi))
+           return HI2UF(hi);
+    }
 
     // Find autoload function if this is an autoload script.
     return find_func_with_prefix(name[0] == 's' && name[1] == ':'
@@ -1983,7 +1988,7 @@ find_func_even_dead(char_u *name, int is_global)
     ufunc_T *
 find_func(char_u *name, int is_global)
 {
-    ufunc_T    *fp = find_func_even_dead(name, is_global);
+    ufunc_T    *fp = find_func_even_dead(name, is_global ? FFED_IS_GLOBAL : 0);
 
     if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0)
        return fp;
@@ -2354,7 +2359,7 @@ func_clear_free(ufunc_T *fp, int force)
     int
 copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
 {
-    ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
+    ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
     ufunc_T *fp = NULL;
 
     if (ufunc == NULL)
@@ -4464,6 +4469,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
        hashtab_T       *ht;
        char_u          *find_name = name;
        int             var_conflict = FALSE;
+       int             ffed_flags = is_global ? FFED_IS_GLOBAL : 0;
 
        v = find_var(name, &ht, TRUE);
        if (v != NULL && (in_vim9script() || v->di_tv.v_type == VAR_FUNC))
@@ -4481,6 +4487,9 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
                    v = find_var(find_name, &ht, TRUE);
                    if (v != NULL)
                        var_conflict = TRUE;
+                   // Only check if the function already exists in the script,
+                   // global functions can be shadowed.
+                   ffed_flags |= FFED_NO_GLOBAL;
                }
                else
                {
@@ -4508,7 +4517,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
            goto erret;
        }
 
-       fp = find_func_even_dead(find_name, is_global);
+       fp = find_func_even_dead(find_name, ffed_flags);
        if (vim9script)
        {
            char_u *uname = untrans_function_name(name);
index 1ba9e1d0264d2b82ee2c6a3ff75dec2ece98da12..99a7555b6142fab5ea0f4d169409ce1d454cdea9 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4202,
 /**/
     4201,
 /**/
index f418e04c8f25ae3aa68ae83a5d11c438116c8571..9aac6d30d9b8742c2ab0830038d9d2b9d173049b 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2798,4 +2798,8 @@ long elapsed(DWORD start_tick);
 #define VSE_SHELL      1       // escape for a shell command
 #define VSE_BUFFER     2       // escape for a ":buffer" command
 
+// Flags used by find_func_even_dead()
+#define FFED_IS_GLOBAL 1       // "g:" was used
+#define FFED_NO_GLOBAL 2       // only check for script-local functions
+
 #endif // VIM__H
index 2b706ad188153cd9236cfcd184e03488e7dd3da5..9ad1e7ddc97bd2fb8573c6a202862c88ece81b73 100644 (file)
@@ -332,7 +332,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
                && (lookup_local(p, len, NULL, cctx) == OK
                    || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
            || find_imported(p, len, FALSE, cctx) != NULL
-           || (ufunc = find_func_even_dead(p, FALSE)) != NULL)
+           || (ufunc = find_func_even_dead(p, 0)) != NULL)
     {
        // A local or script-local function can shadow a global function.
        if (ufunc == NULL || ((ufunc->uf_flags & FC_DEAD) == 0
index 493683acb9515987101f77ebb31ac86f4d4ed940..b6e28b0b948eca29d0a1a6419e40f0f05359e42d 100644 (file)
@@ -2050,7 +2050,7 @@ delete_instr(isn_T *isn)
        case ISN_NEWFUNC:
            {
                char_u  *lambda = isn->isn_arg.newfunc.nf_lambda;
-               ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
+               ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
 
                if (ufunc != NULL)
                {