summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-11-10 17:33:29 +0100
committerBram Moolenaar <Bram@vim.org>2018-11-10 17:33:29 +0100
commitded5f1bed7ff2d138b3ee0f9610d17290b62692d (patch)
tree54727bd70279df4fadc4b8f4701577ac33282ef5
parent1bbb61948342b5cf6e363629f145c65eb455c388 (diff)
patch 8.1.0515: reloading a script gives errors for existing functionsv8.1.0515
Problem: Reloading a script gives errors for existing functions. Solution: Allow redefining a function once when reloading a script.
-rw-r--r--runtime/doc/eval.txt10
-rw-r--r--src/buffer.c1
-rw-r--r--src/ex_cmds2.c9
-rw-r--r--src/globals.h2
-rw-r--r--src/main.c2
-rw-r--r--src/option.c3
-rw-r--r--src/structs.h1
-rw-r--r--src/testdir/test_functions.vim27
-rw-r--r--src/userfunc.c9
-rw-r--r--src/version.c2
10 files changed, 55 insertions, 11 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 2027c2eeb9..b261d58222 100644
--- 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
index cbccc7026c..8e892dadf8 100644
--- 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
index 3a4bce8a26..d6b6f5bf89 100644
--- 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
index 511479d862..131c13be95 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -326,7 +326,7 @@ EXTERN int want_garbage_collect INIT(= FALSE);
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
index d4ff370550..d24eafa949 100644
--- 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
index 3c54ab6422..a4a9c9711d 100644
--- 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
index a0c06b5f25..cd9b5a596f 100644
--- 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
index ac8a27f75a..d942d07687 100644
--- 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
index f79e7f1755..8bfebec64c 100644
--- 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
index b611fe7301..3d728548b2 100644
--- 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,