1
0
forked from aniani/vim

patch 8.2.1308: Vim9: accidentally using "x" causes Vim to exit

Problem:    Vim9: accidentally using "x" causes Vim to exit.
Solution:   Disallow using ":x" or "xit" in Vim9 script. (closes #6399)
This commit is contained in:
Bram Moolenaar 2020-07-28 20:07:27 +02:00
parent 0aac67a431
commit ae616494d7
8 changed files with 78 additions and 26 deletions

View File

@ -71,16 +71,17 @@ comments start with #. >
The reason is that a double quote can also be the start of a string. In many The reason is that a double quote can also be the start of a string. In many
places, especially halfway an expression with a line break, it's hard to tell places, especially halfway an expression with a line break, it's hard to tell
what the meaning is. To avoid confusion only # comments are recognized. what the meaning is, since both a string and a comment can be followed by
This is the same as in shell scripts and Python programs. arbitrary text. To avoid confusion only # comments are recognized. This is
the same as in shell scripts and Python programs.
In Vi # is a command to list text with numbers. In Vim9 script you can use In Vi # is a command to list text with numbers. In Vim9 script you can use
`:number` for that. > `:number` for that. >
101number 101 number
To improve readability there must be a space between a command and the # To improve readability there must be a space between a command and the #
that starts a comment. Note that #{ is the start of a dictionary, therefore that starts a comment. Note that #{ is the start of a dictionary, therefore
it cannot start a comment. it does not start a comment.
Vim9 functions ~ Vim9 functions ~
@ -93,7 +94,7 @@ The syntax is strict, to enforce code that is easy to read and understand.
Compilation is done when the function is first called, or when the Compilation is done when the function is first called, or when the
`:defcompile` command is encountered in the script where the function was `:defcompile` command is encountered in the script where the function was
defined. defined. `:disassemble` also compiles the function.
`:def` has no options like `:function` does: "range", "abort", "dict" or `:def` has no options like `:function` does: "range", "abort", "dict" or
"closure". A `:def` function always aborts on an error, does not get a range "closure". A `:def` function always aborts on an error, does not get a range
@ -104,7 +105,7 @@ be used, type checking will then be done at runtime, like with legacy
functions. functions.
Arguments are accessed by name, without "a:". There is no "a:" dictionary or Arguments are accessed by name, without "a:". There is no "a:" dictionary or
"a:000" list. "a:000" list. Just like any other language.
Variable arguments are defined as the last argument, with a name and have a Variable arguments are defined as the last argument, with a name and have a
list type, similar to Typescript. For example, a list of numbers: > list type, similar to Typescript. For example, a list of numbers: >
@ -216,29 +217,29 @@ Functions can be called without `:call`: >
Using `:call` is still possible, but this is discouraged. Using `:call` is still possible, but this is discouraged.
A method call without `eval` is possible, so long as the start is an A method call without `eval` is possible, so long as the start is an
identifier or can't be an Ex command. It does NOT work for string constants: > identifier or can't be an Ex command. Examples: >
myList->add(123) # works myList->add(123)
g:myList->add(123) # works g:myList->add(123)
[1, 2, 3]->Process() # works [1, 2, 3]->Process()
#{a: 1, b: 2}->Process() # works #{a: 1, b: 2}->Process()
{'a': 1, 'b': 2}->Process() # works {'a': 1, 'b': 2}->Process()
"foobar"->Process() # does NOT work "foobar"->Process()
("foobar")->Process() # works ("foobar")->Process()
'foobar'->Process() # does NOT work 'foobar'->Process()
('foobar')->Process() # works ('foobar')->Process()
In case there is ambiguity between a function name and an Ex command, use ":" In rare case there is ambiguity between a function name and an Ex command, use
to make clear you want to use the Ex command. For example, there is both the ":" to make clear you want to use the Ex command. For example, there is both
`:substitute` command and the `substitute()` function. When the line starts the `:substitute` command and the `substitute()` function. When the line
with `substitute(` this will use the function, prepend a colon to use the starts with `substitute(` this will use the function. Prepend a colon to use
command instead: > the command instead: >
:substitute(pattern (replacement ( :substitute(pattern (replacement (
Note that while variables need to be defined before they can be used, Note that while variables need to be defined before they can be used,
functions can be called before being defined. This is required to be able functions can be called before being defined. This is required to be able
have cyclic dependencies between functions. It is slightly less efficient, have cyclic dependencies between functions. It is slightly less efficient,
since the function has to be looked up by name. And a typo in the function since the function has to be looked up by name. And a typo in the function
name will only be found when the call is executed. name will only be found when the function is called.
Omitting function() ~ Omitting function() ~
@ -347,9 +348,10 @@ No curly braces expansion ~
|curly-braces-names| cannot be used. |curly-braces-names| cannot be used.
No :append, :change or :insert ~ No :xit, :append, :change or :insert ~
These commands are too quickly confused with local variable names. These commands are too easily confused with local variable names. Instead of
`:x` or `:xit` you can use `:exit`.
Comparators ~ Comparators ~

View File

@ -3176,6 +3176,9 @@ ex_append(exarg_T *eap)
int vcol; int vcol;
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
if (not_in_vim9(eap) == FAIL)
return;
// the ! flag toggles autoindent // the ! flag toggles autoindent
if (eap->forceit) if (eap->forceit)
curbuf->b_p_ai = !curbuf->b_p_ai; curbuf->b_p_ai = !curbuf->b_p_ai;
@ -3317,6 +3320,9 @@ ex_change(exarg_T *eap)
{ {
linenr_T lnum; linenr_T lnum;
if (not_in_vim9(eap) == FAIL)
return;
if (eap->line2 >= eap->line1 if (eap->line2 >= eap->line1
&& u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
return; return;

View File

@ -5686,6 +5686,8 @@ ex_stop(exarg_T *eap)
static void static void
ex_exit(exarg_T *eap) ex_exit(exarg_T *eap)
{ {
if (not_in_vim9(eap) == FAIL)
return;
#ifdef FEAT_CMDWIN #ifdef FEAT_CMDWIN
if (cmdwin_type != 0) if (cmdwin_type != 0)
{ {

View File

@ -1,6 +1,7 @@
/* vim9script.c */ /* vim9script.c */
int in_vim9script(void); int in_vim9script(void);
void ex_vim9script(exarg_T *eap); void ex_vim9script(exarg_T *eap);
int not_in_vim9(exarg_T *eap);
void ex_export(exarg_T *eap); void ex_export(exarg_T *eap);
void free_imports(int sid); void free_imports(int sid);
void ex_import(exarg_T *eap); void ex_import(exarg_T *eap);

View File

@ -1515,6 +1515,21 @@ def Test_fixed_size_list()
assert_equal([2, 99, 3, 4, 5], l) assert_equal([2, 99, 3, 4, 5], l)
enddef enddef
def Test_no_insert_xit()
call CheckDefExecFailure(['x = 1'], 'E1100:')
call CheckDefExecFailure(['a = 1'], 'E1100:')
call CheckDefExecFailure(['i = 1'], 'E1100:')
call CheckDefExecFailure(['c = 1'], 'E1100:')
CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:')
CheckScriptFailure(['vim9script', 'a = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'a'], 'E1100:')
CheckScriptFailure(['vim9script', 'i = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'i'], 'E1100:')
CheckScriptFailure(['vim9script', 'c = 1'], 'E488:')
CheckScriptFailure(['vim9script', 'c'], 'E1100:')
enddef
def IfElse(what: number): string def IfElse(what: number): string
let res = '' let res = ''
if what == 1 if what == 1

View File

@ -754,6 +754,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 */
/**/
1308,
/**/ /**/
1307, 1307,
/**/ /**/

View File

@ -7409,6 +7409,13 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
// TODO: other commands with an expression argument // TODO: other commands with an expression argument
case CMD_append:
case CMD_change:
case CMD_insert:
case CMD_xit:
not_in_vim9(&ea);
goto erret;
case CMD_SIZE: case CMD_SIZE:
semsg(_("E476: Invalid command: %s"), ea.cmd); semsg(_("E476: Invalid command: %s"), ea.cmd);
goto erret; goto erret;

View File

@ -58,13 +58,30 @@ ex_vim9script(exarg_T *eap)
} }
} }
/*
* When in Vim9 script give an error and return FAIL.
*/
int
not_in_vim9(exarg_T *eap)
{
switch (eap->cmdidx)
{
case CMD_insert:
case CMD_append:
case CMD_change:
case CMD_xit:
semsg(_("E1100: Missing :let: %s"), eap->cmd);
return FAIL;
default: break;
}
return OK;
}
/* /*
* ":export let Name: type" * ":export let Name: type"
* ":export const Name: type" * ":export const Name: type"
* ":export def Name(..." * ":export def Name(..."
* ":export class Name ..." * ":export class Name ..."
*
* ":export {Name, ...}"
*/ */
void void
ex_export(exarg_T *eap) ex_export(exarg_T *eap)