From 7d22f84f0b8107fb2eb7edb5665f08daa5dae12a Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Fri, 23 Jan 2026 19:17:29 +0000 Subject: [PATCH] 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 Signed-off-by: Christian Brabandt --- src/eval.c | 16 +++ src/testdir/test_vim9_expr.vim | 184 +++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9expr.c | 6 ++ 4 files changed, 208 insertions(+) diff --git a/src/eval.c b/src/eval.c index 4a9f5821ba..9678ea7f67 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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); } diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index b663728849..4551ea5eb9 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -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 + 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 + 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 + 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 + 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) diff --git a/src/version.c b/src/version.c index 1852977ac8..5017e1d7b9 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2106, /**/ 2105, /**/ diff --git a/src/vim9expr.c b/src/vim9expr.c index c8353ab981..ebab9ec136 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -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 -- 2.47.3