changeset 5604:b43363a7b4c7 v7.4.149

updated for version 7.4.149 Problem: Get E685 error when assigning a function to an autoload variable. (Yukihiro Nakadaira) Solution: Instead of having a global no_autoload variable, pass an autoload flag down to where it is used. (ZyX)
author Bram Moolenaar <bram@vim.org>
date Tue, 14 Jan 2014 15:24:39 +0100
parents 0908a52f3bd9
children 339a410f525a
files src/eval.c src/testdir/sautest/autoload/footest.vim src/testdir/test55.in src/testdir/test55.ok src/testdir/test60.in src/testdir/test60.ok src/version.c
diffstat 7 files changed, 97 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -125,9 +125,6 @@ static dictitem_T	globvars_var;		/* vari
  */
 static hashtab_T	compat_hashtab;
 
-/* When using exists() don't auto-load a script. */
-static int		no_autoload = FALSE;
-
 /*
  * When recursively copying lists and dicts we need to remember which ones we
  * have done to avoid endless recursiveness.  This unique ID is used for that.
@@ -156,6 +153,11 @@ static int echo_attr = 0;   /* attribute
 /* Values for trans_function_name() argument: */
 #define TFN_INT		1	/* internal function name OK */
 #define TFN_QUIET	2	/* no error messages */
+#define TFN_NO_AUTOLOAD	4	/* do not use script autoloading */
+
+/* Values for get_lval() flags argument: */
+#define GLV_QUIET	TFN_QUIET	/* no error messages */
+#define GLV_NO_AUTOLOAD	TFN_NO_AUTOLOAD	/* do not use script autoloading */
 
 /*
  * Structure to hold info for a user function.
@@ -390,7 +392,7 @@ static void list_func_vars __ARGS((int *
 static char_u *list_arg_vars __ARGS((exarg_T *eap, char_u *arg, int *first));
 static char_u *ex_let_one __ARGS((char_u *arg, typval_T *tv, int copy, char_u *endchars, char_u *op));
 static int check_changedtick __ARGS((char_u *arg));
-static char_u *get_lval __ARGS((char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int quiet, int fne_flags));
+static char_u *get_lval __ARGS((char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags));
 static void clear_lval __ARGS((lval_T *lp));
 static void set_var_lval __ARGS((lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op));
 static int tv_op __ARGS((typval_T *tv1, typval_T *tv2, char_u  *op));
@@ -770,7 +772,7 @@ static char_u *find_name_end __ARGS((cha
 static char_u * make_expanded_name __ARGS((char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end));
 static int eval_isnamec __ARGS((int c));
 static int eval_isnamec1 __ARGS((int c));
-static int get_var_tv __ARGS((char_u *name, int len, typval_T *rettv, int verbose));
+static int get_var_tv __ARGS((char_u *name, int len, typval_T *rettv, int verbose, int no_autoload));
 static int handle_subscript __ARGS((char_u **arg, typval_T *rettv, int evaluate, int verbose));
 static typval_T *alloc_tv __ARGS((void));
 static typval_T *alloc_string_tv __ARGS((char_u *string));
@@ -781,8 +783,8 @@ static linenr_T get_tv_lnum_buf __ARGS((
 static char_u *get_tv_string __ARGS((typval_T *varp));
 static char_u *get_tv_string_buf __ARGS((typval_T *varp, char_u *buf));
 static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
-static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp));
-static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, int htname, char_u *varname, int writing));
+static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp, int no_autoload));
+static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, int htname, char_u *varname, int no_autoload));
 static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
 static void vars_clear_ext __ARGS((hashtab_T *ht, int free_val));
 static void delete_var __ARGS((hashtab_T *ht, hashitem_T *hi));
@@ -1059,7 +1061,7 @@ var_redir_start(name, append)
     ga_init2(&redir_ga, (int)sizeof(char), 500);
 
     /* Parse the variable name (can be a dict or list entry). */
-    redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, FALSE,
+    redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0,
 							     FNE_CHECK_START);
     if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL)
     {
@@ -1150,7 +1152,7 @@ var_redir_stop()
 	    /* Call get_lval() again, if it's inside a Dict or List it may
 	     * have changed. */
 	    redir_endp = get_lval(redir_varname, NULL, redir_lval,
-					FALSE, FALSE, FALSE, FNE_CHECK_START);
+					FALSE, FALSE, 0, FNE_CHECK_START);
 	    if (redir_endp != NULL && redir_lval->ll_name != NULL)
 		set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)".");
 	    clear_lval(redir_lval);
@@ -2239,7 +2241,7 @@ list_arg_vars(eap, arg, first)
 	    {
 		if (tofree != NULL)
 		    name = tofree;
-		if (get_var_tv(name, len, &tv, TRUE) == FAIL)
+		if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL)
 		    error = TRUE;
 		else
 		{
@@ -2474,7 +2476,7 @@ ex_let_one(arg, tv, copy, endchars, op)
     {
 	lval_T	lv;
 
-	p = get_lval(arg, tv, &lv, FALSE, FALSE, FALSE, FNE_CHECK_START);
+	p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
 	if (p != NULL && lv.ll_name != NULL)
 	{
 	    if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
@@ -2519,18 +2521,22 @@ check_changedtick(arg)
  * "unlet" is TRUE for ":unlet": slightly different behavior when something is
  * wrong; must end in space or cmd separator.
  *
+ * flags:
+ *  GLV_QUIET:       do not give error messages
+ *  GLV_NO_AUTOLOAD: do not use script autoloading
+ *
  * Returns a pointer to just after the name, including indexes.
  * When an evaluation error occurs "lp->ll_name" is NULL;
  * Returns NULL for a parsing error.  Still need to free items in "lp"!
  */
     static char_u *
-get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
+get_lval(name, rettv, lp, unlet, skip, flags, fne_flags)
     char_u	*name;
     typval_T	*rettv;
     lval_T	*lp;
     int		unlet;
     int		skip;
-    int		quiet;	    /* don't give error messages */
+    int		flags;	    /* GLV_ values */
     int		fne_flags;  /* flags for find_name_end() */
 {
     char_u	*p;
@@ -2544,6 +2550,7 @@ get_lval(name, rettv, lp, unlet, skip, q
     char_u	*key = NULL;
     int		len;
     hashtab_T	*ht;
+    int		quiet = flags & GLV_QUIET;
 
     /* Clear everything in "lp". */
     vim_memset(lp, 0, sizeof(lval_T));
@@ -2591,7 +2598,7 @@ get_lval(name, rettv, lp, unlet, skip, q
 
     cc = *p;
     *p = NUL;
-    v = find_var(lp->ll_name, &ht);
+    v = find_var(lp->ll_name, &ht, flags & GLV_NO_AUTOLOAD);
     if (v == NULL && !quiet)
 	EMSG2(_(e_undefvar), lp->ll_name);
     *p = cc;
@@ -2904,7 +2911,7 @@ set_var_lval(lp, endp, rettv, copy, op)
 
 		/* handle +=, -= and .= */
 		if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
-							     &tv, TRUE) == OK)
+						      &tv, TRUE, FALSE) == OK)
 		{
 		    if (tv_op(&tv, rettv, op) == OK)
 			set_var(lp->ll_name, &tv, FALSE);
@@ -3556,7 +3563,7 @@ ex_unletlock(eap, argstart, deep)
     do
     {
 	/* Parse the name and find the end. */
-	name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, FALSE,
+	name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0,
 							     FNE_CHECK_START);
 	if (lv.ll_name == NULL)
 	    error = TRUE;	    /* error but continue parsing */
@@ -3709,7 +3716,7 @@ do_lock_var(lp, name_end, deep, lock)
 	    ret = FAIL;
 	else
 	{
-	    di = find_var(lp->ll_name, NULL);
+	    di = find_var(lp->ll_name, NULL, TRUE);
 	    if (di == NULL)
 		ret = FAIL;
 	    else
@@ -5179,7 +5186,7 @@ eval7(arg, rettv, evaluate, want_string)
 		}
 	    }
 	    else if (evaluate)
-		ret = get_var_tv(s, len, rettv, TRUE);
+		ret = get_var_tv(s, len, rettv, TRUE, FALSE);
 	    else
 		ret = OK;
 	}
@@ -8284,7 +8291,7 @@ deref_func_name(name, lenp)
 
     cc = name[*lenp];
     name[*lenp] = NUL;
-    v = find_var(name, NULL);
+    v = find_var(name, NULL, FALSE);
     name[*lenp] = cc;
     if (v != NULL && v->di_tv.v_type == VAR_FUNC)
     {
@@ -10039,8 +10046,6 @@ f_exists(argvars, rettv)
     int		n = FALSE;
     int		len = 0;
 
-    no_autoload = TRUE;
-
     p = get_tv_string(&argvars[0]);
     if (*p == '$')			/* environment variable */
     {
@@ -10091,7 +10096,7 @@ f_exists(argvars, rettv)
 	{
 	    if (tofree != NULL)
 		name = tofree;
-	    n = (get_var_tv(name, len, &tv, FALSE) == OK);
+	    n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK);
 	    if (n)
 	    {
 		/* handle d.key, l[idx], f(expr) */
@@ -10107,8 +10112,6 @@ f_exists(argvars, rettv)
     }
 
     rettv->vval.v_number = n;
-
-    no_autoload = FALSE;
 }
 
 #ifdef FEAT_FLOAT
@@ -13344,8 +13347,8 @@ f_islocked(argvars, rettv)
     dictitem_T	*di;
 
     rettv->vval.v_number = -1;
-    end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE, FALSE,
-							     FNE_CHECK_START);
+    end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE,
+					GLV_NO_AUTOLOAD, FNE_CHECK_START);
     if (end != NULL && lv.ll_name != NULL)
     {
 	if (*end != NUL)
@@ -13358,7 +13361,7 @@ f_islocked(argvars, rettv)
 		    rettv->vval.v_number = 1;	    /* always locked */
 		else
 		{
-		    di = find_var(lv.ll_name, NULL);
+		    di = find_var(lv.ll_name, NULL, TRUE);
 		    if (di != NULL)
 		    {
 			/* Consider a variable locked when:
@@ -19774,11 +19777,12 @@ set_cmdarg(eap, oldarg)
  * Return OK or FAIL.
  */
     static int
-get_var_tv(name, len, rettv, verbose)
+get_var_tv(name, len, rettv, verbose, no_autoload)
     char_u	*name;
     int		len;		/* length of "name" */
     typval_T	*rettv;		/* NULL when only checking existence */
     int		verbose;	/* may give error message */
+    int		no_autoload;	/* do not use script autoloading */
 {
     int		ret = OK;
     typval_T	*tv = NULL;
@@ -19805,7 +19809,7 @@ get_var_tv(name, len, rettv, verbose)
      */
     else
     {
-	v = find_var(name, NULL);
+	v = find_var(name, NULL, no_autoload);
 	if (v != NULL)
 	    tv = &v->di_tv;
     }
@@ -20207,9 +20211,10 @@ get_tv_string_buf_chk(varp, buf)
  * hashtab_T used.
  */
     static dictitem_T *
-find_var(name, htp)
+find_var(name, htp, no_autoload)
     char_u	*name;
     hashtab_T	**htp;
+    int		no_autoload;
 {
     char_u	*varname;
     hashtab_T	*ht;
@@ -20219,7 +20224,7 @@ find_var(name, htp)
 	*htp = ht;
     if (ht == NULL)
 	return NULL;
-    return find_var_in_ht(ht, *name, varname, htp != NULL);
+    return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
 }
 
 /*
@@ -20227,11 +20232,11 @@ find_var(name, htp)
  * Returns NULL if not found.
  */
     static dictitem_T *
-find_var_in_ht(ht, htname, varname, writing)
+find_var_in_ht(ht, htname, varname, no_autoload)
     hashtab_T	*ht;
     int		htname;
     char_u	*varname;
-    int		writing;
+    int		no_autoload;
 {
     hashitem_T	*hi;
 
@@ -20263,7 +20268,7 @@ find_var_in_ht(ht, htname, varname, writ
 	 * worked find the variable again.  Don't auto-load a script if it was
 	 * loaded already, otherwise it would be loaded every time when
 	 * checking if a function name is a Funcref variable. */
-	if (ht == &globvarht && !writing)
+	if (ht == &globvarht && !no_autoload)
 	{
 	    /* Note: script_autoload() may make "hi" invalid. It must either
 	     * be obtained again or not used. */
@@ -20343,7 +20348,7 @@ get_var_value(name)
 {
     dictitem_T	*v;
 
-    v = find_var(name, NULL);
+    v = find_var(name, NULL, FALSE);
     if (v == NULL)
 	return NULL;
     return get_tv_string(&v->di_tv);
@@ -21672,7 +21677,7 @@ ex_function(eap)
      */
     if (fudi.fd_dict == NULL)
     {
-	v = find_var(name, &ht);
+	v = find_var(name, &ht, FALSE);
 	if (v != NULL && v->di_tv.v_type == VAR_FUNC)
 	{
 	    emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
@@ -21830,8 +21835,9 @@ ret_free:
  * Also handles a Funcref in a List or Dictionary.
  * Returns the function name in allocated memory, or NULL for failure.
  * flags:
- * TFN_INT:   internal function name OK
- * TFN_QUIET: be quiet
+ * TFN_INT:         internal function name OK
+ * TFN_QUIET:       be quiet
+ * TFN_NO_AUTOLOAD: do not use script autoloading
  * Advances "pp" to just after the function name (if no error).
  */
     static char_u *
@@ -21869,7 +21875,8 @@ trans_function_name(pp, skip, flags, fdp
     if (lead > 2)
 	start += lead;
 
-    end = get_lval(start, NULL, &lv, FALSE, skip, flags & TFN_QUIET,
+    /* Note that TFN_ flags use the same values as GLV_ flags. */
+    end = get_lval(start, NULL, &lv, FALSE, skip, flags,
 					      lead > 2 ? 0 : FNE_CHECK_START);
     if (end == start)
     {
@@ -22146,7 +22153,8 @@ function_exists(name)
     char_u  *p;
     int	    n = FALSE;
 
-    p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL);
+    p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD,
+			    NULL);
     nm = skipwhite(nm);
 
     /* Only accept "funcname", "funcname ", "funcname (..." and
@@ -22393,10 +22401,6 @@ script_autoload(name, reload)
     int		ret = FALSE;
     int		i;
 
-    /* Return quickly when autoload disabled. */
-    if (no_autoload)
-	return FALSE;
-
     /* If there is no '#' after name[0] there is no package name. */
     p = vim_strchr(name, AUTOLOAD_CHAR);
     if (p == NULL || p == name)
new file mode 100644
--- /dev/null
+++ b/src/testdir/sautest/autoload/footest.vim
@@ -0,0 +1,5 @@
+" Autoload script used by test55 and test60
+let footest#x = 1
+func footest#F()
+  return 0
+endfunc
--- a/src/testdir/test55.in
+++ b/src/testdir/test55.in
@@ -282,6 +282,13 @@ let l = [0, 1, 2, 3]
 :    $put =ps
 :  endfor
 :endfor
+:" :lockvar/islocked() triggering script autoloading
+:set rtp+=./sautest
+:lockvar g:footest#x
+:unlockvar g:footest#x
+:$put ='locked g:footest#x:'.islocked('g:footest#x')
+:$put ='exists g:footest#x:'.exists('g:footest#x')
+:$put ='g:footest#x: '.g:footest#x
 :"
 :" a:000 function argument
 :" first the tests that should fail
--- a/src/testdir/test55.ok
+++ b/src/testdir/test55.ok
@@ -86,6 +86,9 @@ 0011-011
 FFpFFpp
 0000-000
 ppppppp
+locked g:footest#x:-1
+exists g:footest#x:0
+g:footest#x: 1
 caught a:000
 caught a:000[0]
 caught a:000[2]
--- a/src/testdir/test60.in
+++ b/src/testdir/test60.in
@@ -1,4 +1,4 @@
-Tests for the exists() function.  vim: set ft=vim :
+Tests for the exists() function.  vim: set ft=vim ts=8 :
 
 STARTTEST
 :so small.vim
@@ -11,8 +11,10 @@ STARTTEST
 endfunction
 :function! TestExists()
     augroup myagroup
-	autocmd! BufEnter *.my echo 'myfile edited'
+	autocmd! BufEnter       *.my     echo "myfile edited"
+	autocmd! FuncUndefined  UndefFun exec "fu UndefFun()\nendfu"
     augroup END
+    set rtp+=./sautest
 
     let test_cases = []
 
@@ -95,10 +97,15 @@ endfunction
     " Non-existing user defined function
     let test_cases += [['*MyxyzFunc', 0]]
 
+    " Function that may be created by FuncUndefined event
+    let test_cases += [['*UndefFun', 0]]
+    " Function that may be created by script autoloading
+    let test_cases += [['*footest#F', 0]]
+
     redir! > test.out
 
     for [test_case, result] in test_cases
-      	echo test_case . ": " . result
+        echo test_case . ": " . result
         call RunTest(test_case, result)
     endfor
 
@@ -207,6 +214,14 @@ endfunction
 	echo "FAILED"
     endif
 
+    " Non-existing autoload variable that may be autoloaded
+    echo 'footest#x: 0'
+    if !exists('footest#x')
+	echo "OK"
+    else
+	echo "FAILED"
+    endif
+
     " Valid local list
     let local_list = ["blue", "orange"]
     echo 'local_list: 1'
@@ -566,6 +581,10 @@ endfunction
 
     call TestFuncArg("arg1", "arg2")
 
+    echo ' g:footest#x =' g:footest#x
+    echo '   footest#F()' footest#F()
+    echo 'UndefFun()' UndefFun()
+
     redir END
 endfunction
 :call TestExists()
@@ -576,5 +595,6 @@ endfunction
 :set ff=unix
 :w
 :qa!
+:while getchar(1) | call getchar() | endwhile
 ENDTEST
 
--- a/src/testdir/test60.ok
+++ b/src/testdir/test60.ok
@@ -71,6 +71,10 @@ OK
 OK
 *MyxyzFunc: 0
 OK
+*UndefFun: 0
+OK
+*footest#F: 0
+OK
 :edit: 2
 OK
 :edit/a: 0
@@ -95,6 +99,8 @@ local_var%n: 0
 OK
 local_var: 0
 OK
+footest#x: 0
+OK
 local_list: 1
 OK
 local_list[1]: 1
@@ -195,3 +201,6 @@ a:1: 1
 OK
 a:2: 0
 OK
+ g:footest#x = 1
+   footest#F() 0
+UndefFun() 0
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    149,
+/**/
     148,
 /**/
     147,