mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -04:00 
			
		
		
		
	patch 8.1.1820: using expr->FuncRef() does not work
Problem: Using expr->FuncRef() does not work. Solution: Make FuncRef work as a method.
This commit is contained in:
		
							
								
								
									
										163
									
								
								src/eval.c
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								src/eval.c
									
									
									
									
									
								
							| @@ -3654,6 +3654,74 @@ pattern_match(char_u *pat, char_u *text, int ic) | |||||||
|     return matches; |     return matches; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Handle a name followed by "(".  Both for just "name(arg)" and for | ||||||
|  |  * "expr->name(arg)". | ||||||
|  |  * Returns OK or FAIL. | ||||||
|  |  */ | ||||||
|  |     static int | ||||||
|  | eval_func( | ||||||
|  | 	char_u	    **arg,	// points to "(", will be advanced | ||||||
|  | 	char_u	    *name, | ||||||
|  | 	int	    name_len, | ||||||
|  | 	typval_T    *rettv, | ||||||
|  | 	int	    evaluate, | ||||||
|  | 	typval_T    *basetv)	// "expr" for "expr->name(arg)" | ||||||
|  | { | ||||||
|  |     char_u	*s = name; | ||||||
|  |     int		len = name_len; | ||||||
|  |     partial_T	*partial; | ||||||
|  |     int		ret = OK; | ||||||
|  |  | ||||||
|  |     if (!evaluate) | ||||||
|  | 	check_vars(s, len); | ||||||
|  |  | ||||||
|  |     /* If "s" is the name of a variable of type VAR_FUNC | ||||||
|  |      * use its contents. */ | ||||||
|  |     s = deref_func_name(s, &len, &partial, !evaluate); | ||||||
|  |  | ||||||
|  |     /* Need to make a copy, in case evaluating the arguments makes | ||||||
|  |      * the name invalid. */ | ||||||
|  |     s = vim_strsave(s); | ||||||
|  |     if (s == NULL) | ||||||
|  | 	ret = FAIL; | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  | 	funcexe_T funcexe; | ||||||
|  |  | ||||||
|  | 	// Invoke the function. | ||||||
|  | 	vim_memset(&funcexe, 0, sizeof(funcexe)); | ||||||
|  | 	funcexe.firstline = curwin->w_cursor.lnum; | ||||||
|  | 	funcexe.lastline = curwin->w_cursor.lnum; | ||||||
|  | 	funcexe.doesrange = &len; | ||||||
|  | 	funcexe.evaluate = evaluate; | ||||||
|  | 	funcexe.partial = partial; | ||||||
|  | 	funcexe.basetv = basetv; | ||||||
|  | 	ret = get_func_tv(s, len, rettv, arg, &funcexe); | ||||||
|  |     } | ||||||
|  |     vim_free(s); | ||||||
|  |  | ||||||
|  |     /* If evaluate is FALSE rettv->v_type was not set in | ||||||
|  |      * get_func_tv, but it's needed in handle_subscript() to parse | ||||||
|  |      * what follows. So set it here. */ | ||||||
|  |     if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') | ||||||
|  |     { | ||||||
|  | 	rettv->vval.v_string = NULL; | ||||||
|  | 	rettv->v_type = VAR_FUNC; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Stop the expression evaluation when immediately | ||||||
|  |      * aborting on error, or when an interrupt occurred or | ||||||
|  |      * an exception was thrown but not caught. */ | ||||||
|  |     if (evaluate && aborting()) | ||||||
|  |     { | ||||||
|  | 	if (ret == OK) | ||||||
|  | 	    clear_tv(rettv); | ||||||
|  | 	ret = FAIL; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * The "evaluate" argument: When FALSE, the argument is only parsed but not |  * The "evaluate" argument: When FALSE, the argument is only parsed but not | ||||||
|  * executed.  The function may return OK, but the rettv will be of type |  * executed.  The function may return OK, but the rettv will be of type | ||||||
| @@ -4671,55 +4739,7 @@ eval7( | |||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 	    if (**arg == '(')		/* recursive! */ | 	    if (**arg == '(')		/* recursive! */ | ||||||
| 	    { | 		ret = eval_func(arg, s, len, rettv, evaluate, NULL); | ||||||
| 		partial_T *partial; |  | ||||||
|  |  | ||||||
| 		if (!evaluate) |  | ||||||
| 		    check_vars(s, len); |  | ||||||
|  |  | ||||||
| 		/* If "s" is the name of a variable of type VAR_FUNC |  | ||||||
| 		 * use its contents. */ |  | ||||||
| 		s = deref_func_name(s, &len, &partial, !evaluate); |  | ||||||
|  |  | ||||||
| 		/* Need to make a copy, in case evaluating the arguments makes |  | ||||||
| 		 * the name invalid. */ |  | ||||||
| 		s = vim_strsave(s); |  | ||||||
| 		if (s == NULL) |  | ||||||
| 		    ret = FAIL; |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 		    funcexe_T funcexe; |  | ||||||
|  |  | ||||||
| 		    // Invoke the function. |  | ||||||
| 		    vim_memset(&funcexe, 0, sizeof(funcexe)); |  | ||||||
| 		    funcexe.firstline = curwin->w_cursor.lnum; |  | ||||||
| 		    funcexe.lastline = curwin->w_cursor.lnum; |  | ||||||
| 		    funcexe.doesrange = &len; |  | ||||||
| 		    funcexe.evaluate = evaluate; |  | ||||||
| 		    funcexe.partial = partial; |  | ||||||
| 		    ret = get_func_tv(s, len, rettv, arg, &funcexe); |  | ||||||
| 		} |  | ||||||
| 		vim_free(s); |  | ||||||
|  |  | ||||||
| 		/* If evaluate is FALSE rettv->v_type was not set in |  | ||||||
| 		 * get_func_tv, but it's needed in handle_subscript() to parse |  | ||||||
| 		 * what follows. So set it here. */ |  | ||||||
| 		if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') |  | ||||||
| 		{ |  | ||||||
| 		    rettv->vval.v_string = NULL; |  | ||||||
| 		    rettv->v_type = VAR_FUNC; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		/* Stop the expression evaluation when immediately |  | ||||||
| 		 * aborting on error, or when an interrupt occurred or |  | ||||||
| 		 * an exception was thrown but not caught. */ |  | ||||||
| 		if (evaluate && aborting()) |  | ||||||
| 		{ |  | ||||||
| 		    if (ret == OK) |  | ||||||
| 			clear_tv(rettv); |  | ||||||
| 		    ret = FAIL; |  | ||||||
| 		} |  | ||||||
| 	    } |  | ||||||
| 	    else if (evaluate) | 	    else if (evaluate) | ||||||
| 		ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); | 		ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); | ||||||
| 	    else | 	    else | ||||||
| @@ -4815,55 +4835,42 @@ eval_method( | |||||||
| { | { | ||||||
|     char_u	*name; |     char_u	*name; | ||||||
|     long	len; |     long	len; | ||||||
|     funcexe_T	funcexe; |     char_u	*alias; | ||||||
|     int		ret = OK; |  | ||||||
|     typval_T	base = *rettv; |     typval_T	base = *rettv; | ||||||
|  |     int		ret; | ||||||
|  |  | ||||||
|     // Skip over the ->. |     // Skip over the ->. | ||||||
|     *arg += 2; |     *arg += 2; | ||||||
|  |     rettv->v_type = VAR_UNKNOWN; | ||||||
|  |  | ||||||
|     // Locate the method name. |  | ||||||
|     name = *arg; |     name = *arg; | ||||||
|     for (len = 0; eval_isnamec(name[len]); ++len) |     len = get_name_len(arg, &alias, evaluate, TRUE); | ||||||
| 	; |     if (alias != NULL) | ||||||
|     if (len == 0) | 	name = alias; | ||||||
|  |  | ||||||
|  |     if (len <= 0) | ||||||
|     { |     { | ||||||
| 	if (verbose) | 	if (verbose) | ||||||
| 	    emsg(_("E260: Missing name after ->")); | 	    emsg(_("E260: Missing name after ->")); | ||||||
| 	return FAIL; | 	ret = FAIL; | ||||||
|     } |     } | ||||||
|  |     else | ||||||
|     // Check for the "(".  Skip over white space after it. |     { | ||||||
|     if (name[len] != '(') | 	if (**arg != '(') | ||||||
| 	{ | 	{ | ||||||
| 	    if (verbose) | 	    if (verbose) | ||||||
| 		semsg(_(e_missingparen), name); | 		semsg(_(e_missingparen), name); | ||||||
| 	return FAIL; | 	    ret = FAIL; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	    ret = eval_func(arg, name, len, rettv, evaluate, &base); | ||||||
|     } |     } | ||||||
|     *arg += len; |  | ||||||
|  |  | ||||||
|     // TODO: if "name" is a function reference, resolve it. |  | ||||||
|  |  | ||||||
|     vim_memset(&funcexe, 0, sizeof(funcexe)); |  | ||||||
|     funcexe.evaluate = evaluate; |  | ||||||
|     funcexe.basetv = &base; |  | ||||||
|     rettv->v_type = VAR_UNKNOWN; |  | ||||||
|     ret = get_func_tv(name, len, rettv, arg, &funcexe); |  | ||||||
|  |  | ||||||
|     /* Clear the funcref afterwards, so that deleting it while |     /* Clear the funcref afterwards, so that deleting it while | ||||||
|      * evaluating the arguments is possible (see test55). */ |      * evaluating the arguments is possible (see test55). */ | ||||||
|     if (evaluate) |     if (evaluate) | ||||||
| 	clear_tv(&base); | 	clear_tv(&base); | ||||||
|  |  | ||||||
|     /* Stop the expression evaluation when immediately aborting on |  | ||||||
|      * error, or when an interrupt occurred or an exception was thrown |  | ||||||
|      * but not caught. */ |  | ||||||
|     if (aborting()) |  | ||||||
|     { |  | ||||||
| 	if (ret == OK) |  | ||||||
| 	    clear_tv(rettv); |  | ||||||
| 	ret = FAIL; |  | ||||||
|     } |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| " Tests for ->method() | " Tests for ->method() | ||||||
|  |  | ||||||
| func Test_list() | func Test_list_method() | ||||||
|   let l = [1, 2, 3] |   let l = [1, 2, 3] | ||||||
|   call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) |   call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) | ||||||
|   eval l->assert_equal(l) |   eval l->assert_equal(l) | ||||||
| @@ -34,7 +34,7 @@ func Test_list() | |||||||
|   call assert_fails('eval l->values()', 'E715:') |   call assert_fails('eval l->values()', 'E715:') | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| func Test_dict() | func Test_dict_method() | ||||||
|   let d = #{one: 1, two: 2, three: 3} |   let d = #{one: 1, two: 2, three: 3} | ||||||
|  |  | ||||||
|   call assert_equal(d, d->copy()) |   call assert_equal(d, d->copy()) | ||||||
| @@ -66,7 +66,7 @@ func Test_dict() | |||||||
|   call assert_equal([1, 2, 3], d->values()) |   call assert_equal([1, 2, 3], d->values()) | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| func Test_string() | func Test_string_method() | ||||||
|   call assert_equal(['1', '2', '3'], '1 2 3'->split()) |   call assert_equal(['1', '2', '3'], '1 2 3'->split()) | ||||||
|   call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) |   call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) | ||||||
|   call assert_equal([65, 66, 67], 'ABC'->str2list()) |   call assert_equal([65, 66, 67], 'ABC'->str2list()) | ||||||
| @@ -76,7 +76,7 @@ func Test_string() | |||||||
|   call assert_equal('axc', 'abc'->substitute('b', 'x', '')) |   call assert_equal('axc', 'abc'->substitute('b', 'x', '')) | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| func Test_append() | func Test_method_append() | ||||||
|   new |   new | ||||||
|   eval ['one', 'two', 'three']->append(1) |   eval ['one', 'two', 'three']->append(1) | ||||||
|   call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) |   call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) | ||||||
| @@ -89,3 +89,16 @@ func Test_append() | |||||||
|  |  | ||||||
|   exe 'bwipe! ' .. bnr |   exe 'bwipe! ' .. bnr | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
|  | func Test_method_funcref() | ||||||
|  |   func Concat(one, two, three) | ||||||
|  |     return a:one .. a:two .. a:three | ||||||
|  |   endfunc | ||||||
|  |   let FuncRef = function('Concat') | ||||||
|  |   eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') | ||||||
|  |  | ||||||
|  |   let Partial = function('Concat', ['two']) | ||||||
|  |   eval 'one'->Partial('three')->assert_equal('onetwothree') | ||||||
|  |  | ||||||
|  |   delfunc Concat | ||||||
|  | endfunc | ||||||
|   | |||||||
| @@ -1498,6 +1498,7 @@ call_func( | |||||||
|     typval_T	argv[MAX_FUNC_ARGS + 1]; // used when "partial" or |     typval_T	argv[MAX_FUNC_ARGS + 1]; // used when "partial" or | ||||||
| 					 // "funcexe->basetv" is not NULL | 					 // "funcexe->basetv" is not NULL | ||||||
|     int		argv_clear = 0; |     int		argv_clear = 0; | ||||||
|  |     int		argv_base = 0; | ||||||
|     partial_T	*partial = funcexe->partial; |     partial_T	*partial = funcexe->partial; | ||||||
|  |  | ||||||
|     // Make a copy of the name, if it comes from a funcref variable it could |     // Make a copy of the name, if it comes from a funcref variable it could | ||||||
| @@ -1590,9 +1591,9 @@ call_func( | |||||||
| 		    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); | 		    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); | ||||||
| 		    argv[0] = *funcexe->basetv; | 		    argv[0] = *funcexe->basetv; | ||||||
| 		    argcount++; | 		    argcount++; | ||||||
|  | 		    argvars = argv; | ||||||
|  | 		    argv_base = 1; | ||||||
| 		} | 		} | ||||||
| 		else |  | ||||||
| 		    memcpy(argv, argvars, sizeof(typval_T) * argcount); |  | ||||||
|  |  | ||||||
| 		if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) | 		if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) | ||||||
| 		    *funcexe->doesrange = TRUE; | 		    *funcexe->doesrange = TRUE; | ||||||
| @@ -1621,7 +1622,7 @@ call_func( | |||||||
| 			did_save_redo = TRUE; | 			did_save_redo = TRUE; | ||||||
| 		    } | 		    } | ||||||
| 		    ++fp->uf_calls; | 		    ++fp->uf_calls; | ||||||
| 		    call_user_func(fp, argcount, argv, rettv, | 		    call_user_func(fp, argcount, argvars, rettv, | ||||||
| 					 funcexe->firstline, funcexe->lastline, | 					 funcexe->firstline, funcexe->lastline, | ||||||
| 				  (fp->uf_flags & FC_DICT) ? selfdict : NULL); | 				  (fp->uf_flags & FC_DICT) ? selfdict : NULL); | ||||||
| 		    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) | 		    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) | ||||||
| @@ -1698,8 +1699,10 @@ call_func( | |||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // clear the copies made from the partial | ||||||
|     while (argv_clear > 0) |     while (argv_clear > 0) | ||||||
| 	clear_tv(&argv[--argv_clear]); | 	clear_tv(&argv[--argv_clear + argv_base]); | ||||||
|  |  | ||||||
|     vim_free(tofree); |     vim_free(tofree); | ||||||
|     vim_free(name); |     vim_free(name); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -773,6 +773,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 */ | ||||||
|  | /**/ | ||||||
|  |     1820, | ||||||
| /**/ | /**/ | ||||||
|     1819, |     1819, | ||||||
| /**/ | /**/ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user