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.
This commit is contained in:
Bram Moolenaar
2018-11-10 17:33:29 +01:00
parent 1bbb619483
commit ded5f1bed7
10 changed files with 55 additions and 11 deletions

View File

@@ -9673,9 +9673,13 @@ See |:verbose-cmd| for more information.
deleted if there are no more references to it. deleted if there are no more references to it.
*E127* *E122* *E127* *E122*
When a function by this name already exists and [!] is When a function by this name already exists and [!] is
not used an error message is given. When [!] is used, not used an error message is given. There is one
an existing function is silently replaced. Unless it exception: When sourcing a script again, a function
is currently being executed, that is an error. 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 NOTE: Use ! wisely. If used without care it can cause
an existing function to be replaced unexpectedly, an existing function to be replaced unexpectedly,
which is hard to debug. which is hard to debug.

View File

@@ -5519,6 +5519,7 @@ chk_modeline(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
save_current_sctx = current_sctx; save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_MODELINE; current_sctx.sc_sid = SID_MODELINE;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0; current_sctx.sc_lnum = 0;
#endif #endif
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);

View File

@@ -4344,6 +4344,7 @@ do_source(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
sctx_T save_current_sctx; sctx_T save_current_sctx;
static scid_T last_current_SID = 0; static scid_T last_current_SID = 0;
static int last_current_SID_seq = 0;
funccal_entry_T funccalp_entry; funccal_entry_T funccalp_entry;
int save_debug_break_level = debug_break_level; int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL; scriptitem_T *si = NULL;
@@ -4508,11 +4509,11 @@ do_source(
* Also starts profiling timer for nested script. */ * Also starts profiling timer for nested script. */
save_funccal(&funccalp_entry); save_funccal(&funccalp_entry);
/* // Check if this script was sourced before to finds its SID.
* Check if this script was sourced before to finds its SID. // If it's new, generate a new SID.
* If it's new, generate a new SID. // Always use a new sequence number.
*/
save_current_sctx = current_sctx; save_current_sctx = current_sctx;
current_sctx.sc_seq = ++last_current_SID_seq;
current_sctx.sc_lnum = 0; current_sctx.sc_lnum = 0;
# ifdef UNIX # ifdef UNIX
stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);

View File

@@ -326,7 +326,7 @@ EXTERN int want_garbage_collect INIT(= FALSE);
EXTERN int garbage_collect_at_exit INIT(= FALSE); EXTERN int garbage_collect_at_exit INIT(= FALSE);
// Script CTX being sourced or was sourced to define the current function. // 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 #endif
EXTERN int did_source_packages INIT(= FALSE); EXTERN int did_source_packages INIT(= FALSE);

View File

@@ -2953,6 +2953,7 @@ exe_commands(mparm_T *parmp)
sourcing_name = (char_u *)"command line"; sourcing_name = (char_u *)"command line";
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
current_sctx.sc_sid = SID_CARG; current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
#endif #endif
for (i = 0; i < parmp->n_commands; ++i) for (i = 0; i < parmp->n_commands; ++i)
{ {
@@ -3183,6 +3184,7 @@ process_env(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
save_current_sctx = current_sctx; save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV; current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0; current_sctx.sc_lnum = 0;
#endif #endif
do_cmdline_cmd(initstr); do_cmdline_cmd(initstr);

View File

@@ -415,7 +415,7 @@ struct vimoption
char_u *def_val[2]; // default values for variable (vi and vim) char_u *def_val[2]; // default values for variable (vi and vim)
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
sctx_T script_ctx; // script context where the option was last set sctx_T script_ctx; // script context where the option was last set
# define SCTX_INIT , {0, 0} # define SCTX_INIT , {0, 0, 0}
#else #else
# define SCTX_INIT # define SCTX_INIT
#endif #endif
@@ -5959,6 +5959,7 @@ set_string_option_direct(
else else
{ {
script_ctx.sc_sid = set_sid; script_ctx.sc_sid = set_sid;
script_ctx.sc_seq = 0;
script_ctx.sc_lnum = 0; script_ctx.sc_lnum = 0;
} }
set_option_sctx_idx(idx, opt_flags, script_ctx); set_option_sctx_idx(idx, opt_flags, script_ctx);

View File

@@ -84,6 +84,7 @@ typedef struct VimMenu vimmenu_T;
*/ */
typedef struct { typedef struct {
scid_T sc_sid; // script ID scid_T sc_sid; // script ID
int sc_seq; // sourcing sequence number
linenr_T sc_lnum; // line number linenr_T sc_lnum; // line number
} sctx_T; } sctx_T;

View File

@@ -1138,3 +1138,30 @@ func Test_func_range_with_edit()
call delete('Xfuncrange2') call delete('Xfuncrange2')
bwipe! bwipe!
endfunc 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

View File

@@ -2330,14 +2330,19 @@ ex_function(exarg_T *eap)
fp = find_func(name); fp = find_func(name);
if (fp != NULL) 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); emsg_funcname(e_funcexts, name);
goto erret; goto erret;
} }
if (fp->uf_calls > 0) 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); name);
goto erret; goto erret;
} }

View File

@@ -792,6 +792,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
515,
/**/ /**/
514, 514,
/**/ /**/