# HG changeset patch # User Bram Moolenaar # Date 1541868306 -3600 # Node ID 67e3103d6e18eb8bc2e964ea5fa7c0e44d8a4450 # Parent d0449907002451b5633bdcb29319a7329a68ff75 patch 8.1.0515: reloading a script gives errors for existing functions commit https://github.com/vim/vim/commit/ded5f1bed7ff2d138b3ee0f9610d17290b62692d Author: Bram Moolenaar Date: Sat Nov 10 17:33:29 2018 +0100 patch 8.1.0515: reloading a script gives errors for existing functions Problem: Reloading a script gives errors for existing functions. Solution: Allow redefining a function once when reloading a script. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9673,9 +9673,13 @@ See |:verbose-cmd| for more information. deleted if there are no more references to it. *E127* *E122* When a function by this name already exists and [!] is - not used an error message is given. When [!] is used, - an existing function is silently replaced. Unless it - is currently being executed, that is an error. + not used an error message is given. There is one + exception: When sourcing a script again, a function + that was previously defined in that script will be + silently replaced. + When [!] is used, an existing function is silently + replaced. Unless it is currently being executed, that + is an error. NOTE: Use ! wisely. If used without care it can cause an existing function to be replaced unexpectedly, which is hard to debug. diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -5519,6 +5519,7 @@ chk_modeline( #ifdef FEAT_EVAL save_current_sctx = current_sctx; current_sctx.sc_sid = SID_MODELINE; + current_sctx.sc_seq = 0; current_sctx.sc_lnum = 0; #endif retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -4344,6 +4344,7 @@ do_source( #ifdef FEAT_EVAL sctx_T save_current_sctx; static scid_T last_current_SID = 0; + static int last_current_SID_seq = 0; funccal_entry_T funccalp_entry; int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; @@ -4508,11 +4509,11 @@ do_source( * Also starts profiling timer for nested script. */ save_funccal(&funccalp_entry); - /* - * Check if this script was sourced before to finds its SID. - * If it's new, generate a new SID. - */ + // Check if this script was sourced before to finds its SID. + // If it's new, generate a new SID. + // Always use a new sequence number. save_current_sctx = current_sctx; + current_sctx.sc_seq = ++last_current_SID_seq; current_sctx.sc_lnum = 0; # ifdef UNIX stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -326,7 +326,7 @@ EXTERN int want_garbage_collect INIT(= F EXTERN int garbage_collect_at_exit INIT(= FALSE); // Script CTX being sourced or was sourced to define the current function. -EXTERN sctx_T current_sctx INIT(= {0 COMMA 0}); +EXTERN sctx_T current_sctx INIT(= {0 COMMA 0 COMMA 0}); #endif EXTERN int did_source_packages INIT(= FALSE); diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -2953,6 +2953,7 @@ exe_commands(mparm_T *parmp) sourcing_name = (char_u *)"command line"; #ifdef FEAT_EVAL current_sctx.sc_sid = SID_CARG; + current_sctx.sc_seq = 0; #endif for (i = 0; i < parmp->n_commands; ++i) { @@ -3183,6 +3184,7 @@ process_env( #ifdef FEAT_EVAL save_current_sctx = current_sctx; current_sctx.sc_sid = SID_ENV; + current_sctx.sc_seq = 0; current_sctx.sc_lnum = 0; #endif do_cmdline_cmd(initstr); diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -415,7 +415,7 @@ struct vimoption char_u *def_val[2]; // default values for variable (vi and vim) #ifdef FEAT_EVAL sctx_T script_ctx; // script context where the option was last set -# define SCTX_INIT , {0, 0} +# define SCTX_INIT , {0, 0, 0} #else # define SCTX_INIT #endif @@ -5959,6 +5959,7 @@ set_string_option_direct( else { script_ctx.sc_sid = set_sid; + script_ctx.sc_seq = 0; script_ctx.sc_lnum = 0; } set_option_sctx_idx(idx, opt_flags, script_ctx); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -84,6 +84,7 @@ typedef struct VimMenu vimmenu_T; */ typedef struct { scid_T sc_sid; // script ID + int sc_seq; // sourcing sequence number linenr_T sc_lnum; // line number } sctx_T; diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -1138,3 +1138,30 @@ func Test_func_range_with_edit() call delete('Xfuncrange2') bwipe! endfunc + +func Test_func_exists_on_reload() + call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists') + call assert_equal(0, exists('*ExistingFunction')) + source Xfuncexists + call assert_equal(1, exists('*ExistingFunction')) + " Redefining a function when reloading a script is OK. + source Xfuncexists + call assert_equal(1, exists('*ExistingFunction')) + + " But redefining in another script is not OK. + call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2') + call assert_fails('source Xfuncexists2', 'E122:') + + delfunc ExistingFunction + call assert_equal(0, exists('*ExistingFunction')) + call writefile([ + \ 'func ExistingFunction()', 'echo "yes"', 'endfunc', + \ 'func ExistingFunction()', 'echo "no"', 'endfunc', + \ ], 'Xfuncexists') + call assert_fails('source Xfuncexists', 'E122:') + call assert_equal(1, exists('*ExistingFunction')) + + call delete('Xfuncexists2') + call delete('Xfuncexists') + delfunc ExistingFunction +endfunc diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2330,14 +2330,19 @@ ex_function(exarg_T *eap) fp = find_func(name); if (fp != NULL) { - if (!eap->forceit) + // Function can be replaced with "function!" and when sourcing the + // same script again, but only once. + if (!eap->forceit + && (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid + || fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) { emsg_funcname(e_funcexts, name); goto erret; } if (fp->uf_calls > 0) { - emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), + emsg_funcname( + N_("E127: Cannot redefine function %s: It is in use"), name); goto erret; } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -793,6 +793,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 515, +/**/ 514, /**/ 513,