mirror of
https://github.com/vim/vim.git
synced 2025-10-08 06:04:08 -04:00
patch 8.2.3314: behavior of exists() in a :def function is unpredictable
Problem: Behavior of exists() in a :def function is unpredictable. Solution: Add exists_compiled().
This commit is contained in:
@@ -2473,14 +2473,14 @@ browse({save}, {title}, {initdir}, {default})
|
|||||||
String put up a file requester
|
String put up a file requester
|
||||||
browsedir({title}, {initdir}) String put up a directory requester
|
browsedir({title}, {initdir}) String put up a directory requester
|
||||||
bufadd({name}) Number add a buffer to the buffer list
|
bufadd({name}) Number add a buffer to the buffer list
|
||||||
bufexists({expr}) Number |TRUE| if buffer {expr} exists
|
bufexists({buf}) Number |TRUE| if buffer {buf} exists
|
||||||
buflisted({expr}) Number |TRUE| if buffer {expr} is listed
|
buflisted({buf}) Number |TRUE| if buffer {buf} is listed
|
||||||
bufload({expr}) Number load buffer {expr} if not loaded yet
|
bufload({buf}) Number load buffer {buf} if not loaded yet
|
||||||
bufloaded({expr}) Number |TRUE| if buffer {expr} is loaded
|
bufloaded({buf}) Number |TRUE| if buffer {buf} is loaded
|
||||||
bufname([{expr}]) String Name of the buffer {expr}
|
bufname([{buf}]) String Name of the buffer {buf}
|
||||||
bufnr([{expr} [, {create}]]) Number Number of the buffer {expr}
|
bufnr([{buf} [, {create}]]) Number Number of the buffer {buf}
|
||||||
bufwinid({expr}) Number window ID of buffer {expr}
|
bufwinid({buf}) Number window ID of buffer {buf}
|
||||||
bufwinnr({expr}) Number window number of buffer {expr}
|
bufwinnr({buf}) Number window number of buffer {buf}
|
||||||
byte2line({byte}) Number line number at byte count {byte}
|
byte2line({byte}) Number line number at byte count {byte}
|
||||||
byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr}
|
byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr}
|
||||||
byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr}
|
byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr}
|
||||||
@@ -2562,6 +2562,7 @@ executable({expr}) Number 1 if executable {expr} exists
|
|||||||
execute({command}) String execute {command} and get the output
|
execute({command}) String execute {command} and get the output
|
||||||
exepath({expr}) String full path of the command {expr}
|
exepath({expr}) String full path of the command {expr}
|
||||||
exists({expr}) Number |TRUE| if {expr} exists
|
exists({expr}) Number |TRUE| if {expr} exists
|
||||||
|
exists_compiled({expr}) Number |TRUE| if {expr} exists at compile time
|
||||||
exp({expr}) Float exponential of {expr}
|
exp({expr}) Float exponential of {expr}
|
||||||
expand({expr} [, {nosuf} [, {list}]])
|
expand({expr} [, {nosuf} [, {list}]])
|
||||||
any expand special keywords in {expr}
|
any expand special keywords in {expr}
|
||||||
@@ -4442,8 +4443,10 @@ exepath({expr}) *exepath()*
|
|||||||
*exists()*
|
*exists()*
|
||||||
exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined,
|
exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined,
|
||||||
zero otherwise.
|
zero otherwise.
|
||||||
Note: In a compiled |:def| function local variables and
|
|
||||||
arguments are not visible to `exists()`.
|
Note: In a compiled |:def| function the evaluation is done at
|
||||||
|
runtime. Use `exists_compiled()` to evaluate the expression
|
||||||
|
at compile time.
|
||||||
|
|
||||||
For checking for a supported feature use |has()|.
|
For checking for a supported feature use |has()|.
|
||||||
For checking if a file exists use |filereadable()|.
|
For checking if a file exists use |filereadable()|.
|
||||||
@@ -4534,8 +4537,23 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined,
|
|||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
Varname()->exists()
|
Varname()->exists()
|
||||||
|
<
|
||||||
|
|
||||||
exp({expr}) *exp()*
|
exists_compiled({expr}) *exists()*
|
||||||
|
Like `exists()` but evaluated at compile time. This is useful
|
||||||
|
to skip a block where a function is used that would otherwise
|
||||||
|
give an error: >
|
||||||
|
if exists_compiled('*ThatFunction')
|
||||||
|
ThatFunction('works')
|
||||||
|
endif
|
||||||
|
< If `exists()` were used then a compilation error would be
|
||||||
|
given if ThatFunction() is not defined.
|
||||||
|
|
||||||
|
{expr} must be a literal string. *E1232*
|
||||||
|
Can only be used in a |:def| function. *E1233*
|
||||||
|
|
||||||
|
|
||||||
|
exp({expr}) *exp()*
|
||||||
Return the exponential of {expr} as a |Float| in the range
|
Return the exponential of {expr} as a |Float| in the range
|
||||||
[0, inf].
|
[0, inf].
|
||||||
{expr} must evaluate to a |Float| or a |Number|.
|
{expr} must evaluate to a |Float| or a |Number|.
|
||||||
@@ -6434,7 +6452,7 @@ has({feature} [, {check}])
|
|||||||
features that have been abandoned will not be known by the
|
features that have been abandoned will not be known by the
|
||||||
current Vim version.
|
current Vim version.
|
||||||
|
|
||||||
Also see |exists()|.
|
Also see |exists()| and |exists_compiled()|.
|
||||||
|
|
||||||
Note that to skip code that has a syntax error when the
|
Note that to skip code that has a syntax error when the
|
||||||
feature is not available, Vim may skip the rest of the line
|
feature is not available, Vim may skip the rest of the line
|
||||||
|
@@ -1182,6 +1182,7 @@ Various: *various-functions*
|
|||||||
state() get current busy state
|
state() get current busy state
|
||||||
visualmode() last visual mode used
|
visualmode() last visual mode used
|
||||||
exists() check if a variable, function, etc. exists
|
exists() check if a variable, function, etc. exists
|
||||||
|
exists_compiled() like exists() but check at compile time
|
||||||
has() check if a feature is supported in Vim
|
has() check if a feature is supported in Vim
|
||||||
changenr() return number of most recent change
|
changenr() return number of most recent change
|
||||||
cscope_connection() check if a cscope connection exists
|
cscope_connection() check if a cscope connection exists
|
||||||
|
@@ -646,3 +646,7 @@ EXTERN char e_encryption_sodium_mlock_failed[]
|
|||||||
INIT(= N_("E1230: Encryption: sodium_mlock() failed"));
|
INIT(= N_("E1230: Encryption: sodium_mlock() failed"));
|
||||||
EXTERN char e_cannot_use_bar_to_separate_commands_here_str[]
|
EXTERN char e_cannot_use_bar_to_separate_commands_here_str[]
|
||||||
INIT(= N_("E1231: Cannot use a bar to separate commands here: %s"));
|
INIT(= N_("E1231: Cannot use a bar to separate commands here: %s"));
|
||||||
|
EXTERN char e_argument_of_exists_compiled_must_be_literal_string[]
|
||||||
|
INIT(= N_("E1232: Argument of exists_compiled() must be a literal string"));
|
||||||
|
EXTERN char e_exists_compiled_can_only_be_used_in_def_function[]
|
||||||
|
INIT(= N_("E1233: exists_compiled() can only be used in a :def function"));
|
||||||
|
@@ -49,6 +49,7 @@ static void f_escape(typval_T *argvars, typval_T *rettv);
|
|||||||
static void f_eval(typval_T *argvars, typval_T *rettv);
|
static void f_eval(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_eventhandler(typval_T *argvars, typval_T *rettv);
|
static void f_eventhandler(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_execute(typval_T *argvars, typval_T *rettv);
|
static void f_execute(typval_T *argvars, typval_T *rettv);
|
||||||
|
static void f_exists_compiled(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_expand(typval_T *argvars, typval_T *rettv);
|
static void f_expand(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_expandcmd(typval_T *argvars, typval_T *rettv);
|
static void f_expandcmd(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_feedkeys(typval_T *argvars, typval_T *rettv);
|
static void f_feedkeys(typval_T *argvars, typval_T *rettv);
|
||||||
@@ -1329,6 +1330,8 @@ static funcentry_T global_functions[] =
|
|||||||
ret_string, f_exepath},
|
ret_string, f_exepath},
|
||||||
{"exists", 1, 1, FEARG_1, arg1_string,
|
{"exists", 1, 1, FEARG_1, arg1_string,
|
||||||
ret_number_bool, f_exists},
|
ret_number_bool, f_exists},
|
||||||
|
{"exists_compiled", 1, 1, FEARG_1, arg1_string,
|
||||||
|
ret_number_bool, f_exists_compiled},
|
||||||
{"exp", 1, 1, FEARG_1, arg1_float_or_nr,
|
{"exp", 1, 1, FEARG_1, arg1_float_or_nr,
|
||||||
ret_float, FLOAT_FUNC(f_exp)},
|
ret_float, FLOAT_FUNC(f_exp)},
|
||||||
{"expand", 1, 3, FEARG_1, arg3_string_bool_bool,
|
{"expand", 1, 3, FEARG_1, arg3_string_bool_bool,
|
||||||
@@ -3626,6 +3629,12 @@ f_exists(typval_T *argvars, typval_T *rettv)
|
|||||||
rettv->vval.v_number = n;
|
rettv->vval.v_number = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||||
|
{
|
||||||
|
emsg(_(e_exists_compiled_can_only_be_used_in_def_function));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "expand()" function
|
* "expand()" function
|
||||||
*/
|
*/
|
||||||
|
@@ -793,42 +793,57 @@ def Test_exists()
|
|||||||
CheckDefAndScriptFailure2(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1')
|
CheckDefAndScriptFailure2(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1')
|
||||||
call assert_equal(1, exists('&tabstop'))
|
call assert_equal(1, exists('&tabstop'))
|
||||||
|
|
||||||
if exists('+newoption')
|
var lines =<< trim END
|
||||||
|
if exists('+newoption')
|
||||||
|
if &newoption == 'ok'
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
END
|
||||||
|
CheckDefFailure(lines, 'E113:')
|
||||||
|
CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_exists_compiled()
|
||||||
|
call assert_equal(1, exists_compiled('&tabstop'))
|
||||||
|
CheckDefAndScriptFailure2(['exists_compiled(10)'], 'E1232:', 'E1233:')
|
||||||
|
CheckDefAndScriptFailure2(['exists_compiled(v:progname)'], 'E1232:', 'E1233:')
|
||||||
|
|
||||||
|
if exists_compiled('+newoption')
|
||||||
if &newoption == 'ok'
|
if &newoption == 'ok'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
if exists('&newoption')
|
if exists_compiled('&newoption')
|
||||||
if &newoption == 'ok'
|
if &newoption == 'ok'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
if exists('+tabstop')
|
if exists_compiled('+tabstop')
|
||||||
assert_equal(8, &tabstop)
|
assert_equal(8, &tabstop)
|
||||||
else
|
else
|
||||||
assert_report('tabstop option not existing?')
|
assert_report('tabstop option not existing?')
|
||||||
endif
|
endif
|
||||||
if exists('&tabstop')
|
if exists_compiled('&tabstop')
|
||||||
assert_equal(8, &tabstop)
|
assert_equal(8, &tabstop)
|
||||||
else
|
else
|
||||||
assert_report('tabstop option not existing?')
|
assert_report('tabstop option not existing?')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists(':DoSomeCommand') >= 2
|
if exists_compiled(':DoSomeCommand') >= 2
|
||||||
DoSomeCommand
|
DoSomeCommand
|
||||||
endif
|
endif
|
||||||
assert_equal(4, g:didSomeCommand)
|
assert_equal(4, g:didSomeCommand)
|
||||||
if exists(':NoSuchCommand') >= 2
|
if exists_compiled(':NoSuchCommand') >= 2
|
||||||
NoSuchCommand
|
NoSuchCommand
|
||||||
endif
|
endif
|
||||||
|
|
||||||
var found = false
|
var found = false
|
||||||
if exists('*CheckScriptSuccess')
|
if exists_compiled('*CheckScriptSuccess')
|
||||||
found = true
|
found = true
|
||||||
endif
|
endif
|
||||||
assert_true(found)
|
assert_true(found)
|
||||||
if exists('*NoSuchFunction')
|
if exists_compiled('*NoSuchFunction')
|
||||||
NoSuchFunction()
|
NoSuchFunction()
|
||||||
endif
|
endif
|
||||||
if exists('*no_such_function')
|
if exists_compiled('*no_such_function')
|
||||||
no_such_function()
|
no_such_function()
|
||||||
endif
|
endif
|
||||||
enddef
|
enddef
|
||||||
|
@@ -755,6 +755,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 */
|
||||||
|
/**/
|
||||||
|
3314,
|
||||||
/**/
|
/**/
|
||||||
3313,
|
3313,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -3415,9 +3415,9 @@ compile_call(
|
|||||||
int is_searchpair;
|
int is_searchpair;
|
||||||
|
|
||||||
// We can evaluate "has('name')" at compile time.
|
// We can evaluate "has('name')" at compile time.
|
||||||
// We can evaluate some "exists()" values at compile time.
|
// We always evaluate "exists_compiled()" at compile time.
|
||||||
if ((varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
|
if ((varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
|
||||||
|| (varlen == 6 && STRNCMP(*arg, "exists", 6) == 0))
|
|| (varlen == 15 && STRNCMP(*arg, "exists_compiled", 6) == 0))
|
||||||
{
|
{
|
||||||
char_u *s = skipwhite(*arg + varlen + 1);
|
char_u *s = skipwhite(*arg + varlen + 1);
|
||||||
typval_T argvars[2];
|
typval_T argvars[2];
|
||||||
@@ -3431,8 +3431,7 @@ compile_call(
|
|||||||
s = skipwhite(s);
|
s = skipwhite(s);
|
||||||
if (*s == ')' && argvars[0].v_type == VAR_STRING
|
if (*s == ')' && argvars[0].v_type == VAR_STRING
|
||||||
&& ((is_has && !dynamic_feature(argvars[0].vval.v_string))
|
&& ((is_has && !dynamic_feature(argvars[0].vval.v_string))
|
||||||
|| (!is_has && vim_strchr((char_u *)"+&:*",
|
|| !is_has))
|
||||||
*argvars[0].vval.v_string))))
|
|
||||||
{
|
{
|
||||||
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
|
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
|
||||||
|
|
||||||
@@ -3449,6 +3448,11 @@ compile_call(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
clear_tv(&argvars[0]);
|
clear_tv(&argvars[0]);
|
||||||
|
if (!is_has)
|
||||||
|
{
|
||||||
|
emsg(_(e_argument_of_exists_compiled_must_be_literal_string));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|
Reference in New Issue
Block a user