]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1546: Vim9: error with has() and short circuit evaluation v9.1.1546
authorYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 15 Jul 2025 18:26:52 +0000 (20:26 +0200)
committerChristian Brabandt <cb@256bit.org>
Tue, 15 Jul 2025 18:30:52 +0000 (20:30 +0200)
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>
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9expr.c

index 4a86a9b567b14be6f7960530b34a2e6da9a2336e..04b5e1118a4c39fbf3db112a63be4e6b820e573d 100644 (file)
@@ -3875,4 +3875,131 @@ def Test_disassemble_assign_tuple_set_type()
   unlet g:instr
 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
index d4043d1db64b3c30ef3220069e0ea86c160c8d55..2e70a3ba9ddea25a12a6288af14e3cac150bc099 100644 (file)
@@ -5265,6 +5265,102 @@ def Test_method_call_with_list_arg()
   v9.CheckSourceSuccess(lines)
 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.
 def Test_substitute_cmd()
   new
index e4084d585380e5ad6ee3fdb75941dbe9ab51a600..16b71bb687495a96a9dea9069bc4347b1caf6a1d 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1546,
 /**/
     1545,
 /**/
index d169ed75ab6ea665b31662bf27fea7d36b4f2501..33c0dafb7704694f91643d0143d538682a80b950 100644 (file)
@@ -1233,20 +1233,25 @@ compile_call(
               && ((is_has && !dynamic_feature(argvars[0].vval.v_string))
                    || !is_has))
        {
-           typval_T    *tv = &ppconst->pp_tv[ppconst->pp_used];
 
            *arg = s + 1;
-           argvars[1].v_type = VAR_UNKNOWN;
-           tv->v_type = VAR_NUMBER;
-           tv->vval.v_number = 0;
-           if (is_has)
-               f_has(argvars, tv);
-           else if (is_len)
-               f_len(argvars, tv);
-           else
-               f_exists(argvars, tv);
+
+           if (cctx->ctx_skip != SKIP_YES)
+           {
+               typval_T        *tv = &ppconst->pp_tv[ppconst->pp_used];
+
+               argvars[1].v_type = VAR_UNKNOWN;
+               tv->v_type = VAR_NUMBER;
+               tv->vval.v_number = 0;
+               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]);
-           ++ppconst->pp_used;
            return OK;
        }
        clear_tv(&argvars[0]);