patch 9.1.1546: Vim9: error with has() and short circuit evaluation

Problem:  Vim9: error with has() and short circuit evaluation
Solution: Only eval, if ctx_skip is not SKIP_YES (Yegappan Lakshmanan).

fixes: #17750
closes: #17755

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2025-07-15 20:26:52 +02:00
committed by Christian Brabandt
parent edce68912e
commit 8de753148f
4 changed files with 241 additions and 11 deletions

View File

@ -3875,4 +3875,131 @@ def Test_disassemble_assign_tuple_set_type()
unlet g:instr unlet g:instr
enddef enddef
" Disassemble the code generated for a has() function call
def Test_disassemble_has_shortcircuit()
# true && false condition check
var lines =<< trim END
vim9script
def Fn(): string
if has('jumplist') && has('foobar')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''jumplist'') && has(''foobar'')\_s*' ..
'return ''present''\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'0 PUSHS "missing"\_s*' ..
'1 RETURN', g:instr)
# false && true condition check
lines =<< trim END
vim9script
def Fn(): string
if has('foobar') && has('jumplist')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''foobar'') && has(''jumplist'')\_s*' ..
'return ''present''\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'0 PUSHS "missing"\_s*' ..
'1 RETURN', g:instr)
# false && false condition check
lines =<< trim END
vim9script
def Fn(): string
if has('foobar') && has('foobaz')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''foobar'') && has(''foobaz'')\_s*' ..
'return ''present''\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'0 PUSHS "missing"\_s*' ..
'1 RETURN', g:instr)
# true || false condition check
lines =<< trim END
vim9script
def Fn(): string
if has('jumplist') || has('foobar')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''jumplist'') || has(''foobar'')\_s*' ..
'return ''present''\_s*' ..
'0 PUSHS "present"\_s*' ..
'1 RETURN\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'2 PUSHS "missing"\_s*' ..
'3 RETURN', g:instr)
# false || true condition check
lines =<< trim END
vim9script
def Fn(): string
if has('foobar') || has('jumplist')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''foobar'') || has(''jumplist'')\_s*' ..
'return ''present''\_s*' ..
'0 PUSHS "present"\_s*' ..
'1 RETURN\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'2 PUSHS "missing"\_s*' ..
'3 RETURN', g:instr)
# false || false condition check
lines =<< trim END
vim9script
def Fn(): string
if has('foobar') || has('foobaz')
return 'present'
endif
return 'missing'
enddef
g:instr = execute('disassemble Fn')
END
v9.CheckScriptSuccess(lines)
assert_match('<SNR>\d\+_Fn\_s*' ..
'if has(''foobar'') || has(''foobaz'')\_s*' ..
'return ''present''\_s*' ..
'endif\_s*' ..
'return ''missing''\_s*' ..
'0 PUSHS "missing"\_s*' ..
'1 RETURN', g:instr)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -5265,6 +5265,102 @@ def Test_method_call_with_list_arg()
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
enddef enddef
" Test for using more than one has() check in a compound if condition.
def Test_has_func_shortcircuit()
def Has_And1_Cond(): string
# true && false
if has('jumplist') && has('foobar')
return 'present'
endif
return 'missing'
enddef
assert_equal('missing', Has_And1_Cond())
def Has_And2_Cond(): string
# false && true
if has('foobar') && has('jumplist')
return 'present'
endif
return 'missing'
enddef
assert_equal('missing', Has_And2_Cond())
def Has_And3_Cond(): string
# false && false
if has('foobar') && has('foobaz')
return 'present'
endif
return 'missing'
enddef
assert_equal('missing', Has_And3_Cond())
def Has_Or1_Cond(): string
# true || false
if has('jumplist') || has('foobar')
return 'present'
endif
return 'missing'
enddef
assert_equal('present', Has_Or1_Cond())
def Has_Or2_Cond(): string
# false || true
if has('foobar') || has('jumplist')
return 'present'
endif
return 'missing'
enddef
assert_equal('present', Has_Or2_Cond())
def Has_Or3_Cond(): string
# false || false
if has('foobar') || has('foobaz')
return 'present'
endif
return 'missing'
enddef
assert_equal('missing', Has_Or3_Cond())
enddef
" Test for using more than one len() function in a compound if condition.
def Test_len_func_shortcircuit()
def Len_And1_Cond(): string
# true && false
if len('xxx') == 3 && len('yyy') == 2
return 'match'
endif
return 'nomatch'
enddef
assert_equal('nomatch', Len_And1_Cond())
def Len_And2_Cond(): string
# false && true
if len('xxx') == 2 && len('yyy') == 3
return 'match'
endif
return 'nomatch'
enddef
assert_equal('nomatch', Len_And2_Cond())
def Len_Or1_Cond(): string
# true || false
if len('xxx') == 3 || len('yyy') == 2
return 'match'
endif
return 'nomatch'
enddef
assert_equal('match', Len_Or1_Cond())
def Len_Or2_Cond(): string
# false || true
if len('xxx') == 2 || len('yyy') == 3
return 'match'
endif
return 'nomatch'
enddef
assert_equal('match', Len_Or2_Cond())
enddef
" Keep this last, it messes up highlighting. " Keep this last, it messes up highlighting.
def Test_substitute_cmd() def Test_substitute_cmd()
new new

View File

@ -719,6 +719,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 */
/**/
1546,
/**/ /**/
1545, 1545,
/**/ /**/

View File

@ -1233,20 +1233,25 @@ compile_call(
&& ((is_has && !dynamic_feature(argvars[0].vval.v_string)) && ((is_has && !dynamic_feature(argvars[0].vval.v_string))
|| !is_has)) || !is_has))
{ {
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
*arg = s + 1; *arg = s + 1;
argvars[1].v_type = VAR_UNKNOWN;
tv->v_type = VAR_NUMBER; if (cctx->ctx_skip != SKIP_YES)
tv->vval.v_number = 0; {
if (is_has) typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
f_has(argvars, tv);
else if (is_len) argvars[1].v_type = VAR_UNKNOWN;
f_len(argvars, tv); tv->v_type = VAR_NUMBER;
else tv->vval.v_number = 0;
f_exists(argvars, tv); if (is_has)
f_has(argvars, tv);
else if (is_len)
f_len(argvars, tv);
else
f_exists(argvars, tv);
++ppconst->pp_used;
}
clear_tv(&argvars[0]); clear_tv(&argvars[0]);
++ppconst->pp_used;
return OK; return OK;
} }
clear_tv(&argvars[0]); clear_tv(&argvars[0]);