vim-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.
ded5f1bed7
This commit is contained in:
erw7
2019-08-29 13:07:03 +09:00
parent e4a4786241
commit 9db60b06a1
9 changed files with 51 additions and 7 deletions

View File

@@ -9238,9 +9238,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

@@ -5155,6 +5155,7 @@ chk_modeline(
const int secure_save = secure; const int secure_save = secure;
const sctx_T save_current_sctx = current_sctx; const sctx_T 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;
// Make sure no risky things are executed as a side effect. // Make sure no risky things are executed as a side effect.
secure = 1; secure = 1;

View File

@@ -21504,7 +21504,11 @@ void 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;
} }

View File

@@ -256,6 +256,7 @@ typedef int scid_T;
// the line number in the script "sc_sid". // the line number in the script "sc_sid".
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

@@ -3034,6 +3034,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
char_u *firstline = NULL; char_u *firstline = NULL;
int retval = FAIL; int retval = FAIL;
static scid_T last_current_SID = 0; static scid_T last_current_SID = 0;
static int last_current_SID_seq = 0;
void *save_funccalp; void *save_funccalp;
int save_debug_break_level = debug_break_level; int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL; scriptitem_T *si = NULL;
@@ -3160,7 +3161,9 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
// 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.
const sctx_T save_current_sctx = current_sctx; const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_seq = ++last_current_SID_seq;
current_sctx.sc_lnum = 0; current_sctx.sc_lnum = 0;
FileID file_id; FileID file_id;
bool file_id_ok = os_fileid((char *)fname_exp, &file_id); bool file_id_ok = os_fileid((char *)fname_exp, &file_id);

View File

@@ -333,7 +333,7 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_API_CLIENT -8 // for API clients #define SID_API_CLIENT -8 // for API clients
// 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 });
// ID of the current channel making a client API call // ID of the current channel making a client API call
EXTERN uint64_t current_channel_id INIT(= 0); EXTERN uint64_t current_channel_id INIT(= 0);

View File

@@ -1665,6 +1665,7 @@ static void exe_commands(mparm_T *parmp)
curwin->w_cursor.lnum = 0; curwin->w_cursor.lnum = 0;
sourcing_name = (char_u *)"command line"; sourcing_name = (char_u *)"command line";
current_sctx.sc_sid = SID_CARG; current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) { for (i = 0; i < parmp->n_commands; i++) {
do_cmdline_cmd(parmp->commands[i]); do_cmdline_cmd(parmp->commands[i]);
if (parmp->cmds_tofree[i]) if (parmp->cmds_tofree[i])
@@ -1862,6 +1863,7 @@ static int execute_env(char *env)
sourcing_lnum = 0; sourcing_lnum = 0;
const sctx_T save_current_sctx = current_sctx; const sctx_T 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;
do_cmdline_cmd((char *)initstr); do_cmdline_cmd((char *)initstr);
sourcing_name = save_sourcing_name; sourcing_name = save_sourcing_name;

View File

@@ -200,7 +200,7 @@ typedef struct vimoption {
// local option: indirect option index // local option: indirect option index
char_u *def_val[2]; // default values for variable (vi and vim) char_u *def_val[2]; // default values for variable (vi and vim)
LastSet last_set; // script in which the option was last set LastSet last_set; // script in which the option was last set
# define SCTX_INIT , { 0, 0 } # define SCTX_INIT , { 0, 0, 0 }
} vimoption_T; } vimoption_T;
#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value #define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value
@@ -2423,6 +2423,7 @@ set_string_option_direct(
script_ctx = current_sctx; script_ctx = current_sctx;
} 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);
@@ -3798,7 +3799,8 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int indir = (int)options[opt_idx].indir; int indir = (int)options[opt_idx].indir;
const LastSet last_set = { .script_ctx = const LastSet last_set = { .script_ctx =
{ script_ctx.sc_sid, script_ctx.sc_lnum + sourcing_lnum }, { script_ctx.sc_sid, script_ctx.sc_seq,
script_ctx.sc_lnum + sourcing_lnum },
current_channel_id }; current_channel_id };
// Remember where the option was set. For local options need to do that // Remember where the option was set. For local options need to do that

View File

@@ -1067,6 +1067,33 @@ func Test_func_range_with_edit()
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
sandbox function Fsandbox() sandbox function Fsandbox()
normal ix normal ix
endfunc endfunc