patch 9.1.2106: Vim9: class, enum and type alias can be used as value

Problem:  Vim9: class, enum and type alias can be used as value in an
          expression (kennypete)
Solution: Abort expression evaluation if class, enum or type alias is
          used in an expression (Yegappan Lakshmanan)

related: #19173
closes:  #19238

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2026-01-23 19:17:29 +00:00
committed by Christian Brabandt
parent 2b6bdbc697
commit 7d22f84f0b
4 changed files with 208 additions and 0 deletions
+16
View File
@@ -3646,7 +3646,15 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int error = FALSE;
if (op_falsy)
{
// Is this typeval supported with the falsy operator?
if (check_typval_is_value(rettv) == FAIL)
{
clear_tv(rettv);
return FAIL;
}
result = tv2bool(rettv);
}
else if (vim9script)
result = tv_get_bool_chk(rettv, &error);
else if (tv_get_number_chk(rettv, &error) != 0)
@@ -5376,7 +5384,15 @@ eval9_leader(
while (VIM_ISWHITE(end_leader[-1]))
--end_leader;
if (vim9script && end_leader[-1] == '!')
{
// Is this typeval supported with the ! operator?
if (check_typval_is_value(rettv) == FAIL)
{
clear_tv(rettv);
return FAIL;
}
val = tv2bool(rettv);
}
else
val = tv_get_number_chk(rettv, &error);
}
+184
View File
@@ -280,6 +280,69 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1405: Class "B" cannot be used as a value')
# Expression evaluation should stop after using a class with the falsy
# operator
lines =<< trim END
vim9script
class C
endclass
var output: string = 'pass'
try
echo C ?? 'falsy' !! execute("output = 'fail'")
catch /E1405:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a class with the falsy operator, expression evaluation should
# be aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
class C
endclass
g:falsy_output = 'pass'
def Fn()
echo C ?? 'falsy' !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
# When using a class with the "!" operator, expression evaluation should be
# aborted
lines =<< trim END
vim9script
class C
endclass
var output: string = 'pass'
try
echo !C !! execute("output = 'fail'")
catch /E1405:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a class with the "!" operator, expression evaluation should be
# aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
class C
endclass
def Fn()
echo !C !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
lines =<< trim END
vim9script
echo null_class ?? 'falsy'
@@ -309,6 +372,68 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E2" cannot be used as a value')
# Expression evaluation should stop after using an enum with the falsy
# operator
lines =<< trim END
vim9script
enum E
endenum
var output: string = 'pass'
try
echo E ?? 'falsy' !! execute("output = 'fail'")
catch /E1421:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a enum with the falsy operator, expression evaluation should
# be aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
enum E3
endenum
g:falsy_output = 'pass'
def Fn()
echo E3 ?? 'falsy' !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E3" cannot be used as a value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
# Expression evaluation should stop after using an enum with the ! operator
lines =<< trim END
vim9script
enum E
endenum
var output: string = 'pass'
try
echo !E !! execute("output = 'fail'")
catch /E1421:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a enum with the "!" operator, expression evaluation should be
# aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
enum E4
endenum
def Fn()
echo !E4 !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E4" cannot be used as a value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
# typealias cannot be used with the falsy operator
lines =<< trim END
vim9script
@@ -324,6 +449,65 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1403: Type alias "T2" cannot be used as a value')
# Expression evaluation should stop after using a typealias with the falsy
# operator
lines =<< trim END
vim9script
type T3 = dict<string>
var output: string = 'pass'
try
echo T3 ?? 'falsy' !! execute("output = 'fail'")
catch /E1403:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a typealias with the falsy operator, expression evaluation
# should be aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
type T3 = dict<job>
g:falsy_output = 'pass'
def Fn()
echo T3 ?? 'falsy' !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
# Expression evaluation should stop after using a typealias with the !
# operator
lines =<< trim END
vim9script
type T3 = dict<string>
var output: string = 'pass'
try
echo !T3 !! execute("output = 'fail'")
catch /E1403:/
endtry
assert_equal('pass', output)
END
v9.CheckSourceScriptSuccess(lines)
# When using a typealias with the "!" operator, expression evaluation should
# be aborted in a function
g:falsy_output = 'pass'
lines =<< trim END
vim9script
type T4 = list<number>
def Fn()
echo !T4 !! execute('g:falsy_output = "fail"')
enddef
Fn()
END
v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
assert_equal('pass', g:falsy_output)
unlet g:falsy_output
var msg = "White space required before and after '??'"
call v9.CheckDefAndScriptFailure(["var x = 1?? 'one' : 'two'"], msg, 1)
call v9.CheckDefAndScriptFailure(["var x = 1 ??'one' : 'two'"], msg, 1)
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2106,
/**/
2105,
/**/
+6
View File
@@ -2430,6 +2430,8 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
invert = !invert;
--p;
}
if (check_type_is_value(get_type_on_stack(cctx, 0)) == FAIL)
return FAIL;
if (generate_2BOOL(cctx, invert, -1) == FAIL)
return FAIL;
}
@@ -3948,7 +3950,11 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
generate_JUMP(cctx, op_falsy
? JUMP_AND_KEEP_IF_TRUE : JUMP_IF_FALSE, 0);
if (op_falsy)
{
type1 = get_type_on_stack(cctx, -1);
if (check_type_is_value(type1) == FAIL)
return FAIL;
}
}
// evaluate the second expression; any type is accepted