Patch 9.0.1631
Problem:    Passing a wrong variable type to an option gives multiple errors.
Solution:   Bail out early on failure. (closes #12504)
Files:      src/evalvars.c, src/option.c, src/proto/option.pro,
            src/testdir/test_let.vim, src/testdir/test_options.vim,
            src/testdir/test_vim9_assign.vim,
            src/testdir/test_vim9_builtin.vim, src/testdir/test_vimscript.vim


*** ../vim-9.0.1630/src/evalvars.c      2023-06-05 19:46:14.436726710 +0100
--- src/evalvars.c      2023-06-14 16:36:18.826102564 +0100
***************
*** 1639,1746 ****
      p = find_option_end(&arg, &scope);
      if (p == NULL || (endchars != NULL
                               && vim_strchr(endchars, *skipwhite(p)) == NULL))
        emsg(_(e_unexpected_characters_in_let));
!     else
      {
!       int         c1;
!       long        n = 0;
!       getoption_T opt_type;
!       long        numval;
!       char_u      *stringval = NULL;
!       char_u      *s = NULL;
!       int         failed = FALSE;
!       int         opt_p_flags;
!       char_u      *tofree = NULL;
!       char_u      numbuf[NUMBUFLEN];
! 
!       c1 = *p;
!       *p = NUL;
! 
!       opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags,
!                                                                       scope);
!       if ((opt_type == gov_bool
!                   || opt_type == gov_number
!                   || opt_type == gov_hidden_bool
!                   || opt_type == gov_hidden_number)
!                        && (tv->v_type != VAR_STRING || !in_vim9script()))
!       {
!           if (opt_type == gov_bool || opt_type == gov_hidden_bool)
!               // bool, possibly hidden
!               n = (long)tv_get_bool(tv);
!           else
!               // number, possibly hidden
!               n = (long)tv_get_number(tv);
!       }
  
!       if ((opt_p_flags & P_FUNC) && (tv->v_type == VAR_PARTIAL
!                                               || tv->v_type == VAR_FUNC))
!       {
!           // If the option can be set to a function reference or a lambda
!           // and the passed value is a function reference, then convert it to
!           // the name (string) of the function reference.
!           s = tv2string(tv, &tofree, numbuf, 0);
!       }
!       // Avoid setting a string option to the text "v:false" or similar.
!       // In Vim9 script also don't convert a number to string.
!       else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL
!                        && (!in_vim9script() || tv->v_type != VAR_NUMBER))
!           s = tv_get_string_chk(tv);
  
!       if (op != NULL && *op != '=')
!       {
!           if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
!                   || (opt_type == gov_string && *op != '.'))
!           {
!               semsg(_(e_wrong_variable_type_for_str_equal), op);
!               failed = TRUE;  // don't set the value
  
!           }
!           else
            {
!               // number, in legacy script also bool
!               if (opt_type == gov_number
!                            || (opt_type == gov_bool && !in_vim9script()))
!               {
!                   switch (*op)
!                   {
!                       case '+': n = numval + n; break;
!                       case '-': n = numval - n; break;
!                       case '*': n = numval * n; break;
!                       case '/': n = (long)num_divide(numval, n,
!                                                              &failed); break;
!                       case '%': n = (long)num_modulus(numval, n,
!                                                              &failed); break;
!                   }
!                   s = NULL;
!               }
!               else if (opt_type == gov_string
!                                        && stringval != NULL && s != NULL)
!               {
!                   // string
!                   s = concat_str(stringval, s);
!                   vim_free(stringval);
!                   stringval = s;
!               }
            }
        }
! 
!       if (!failed)
        {
!           if (opt_type != gov_string || s != NULL)
!           {
!               char *err = set_option_value(arg, n, s, scope);
! 
!               arg_end = p;
!               if (err != NULL)
!                   emsg(_(err));
!           }
!           else
!               emsg(_(e_string_required));
        }
-       *p = c1;
-       vim_free(stringval);
-       vim_free(tofree);
      }
      return arg_end;
  }
  
--- 1639,1754 ----
      p = find_option_end(&arg, &scope);
      if (p == NULL || (endchars != NULL
                               && vim_strchr(endchars, *skipwhite(p)) == NULL))
+     {
        emsg(_(e_unexpected_characters_in_let));
!       return NULL;
!     }
! 
!     int               c1;
!     long      n = 0;
!     getoption_T       opt_type;
!     long      numval;
!     char_u    *stringval = NULL;
!     char_u    *s = NULL;
!     int               failed = FALSE;
!     int               opt_p_flags;
!     char_u    *tofree = NULL;
!     char_u    numbuf[NUMBUFLEN];
! 
!     c1 = *p;
!     *p = NUL;
! 
!     opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, 
scope);
!     if (opt_type == gov_unknown && arg[0] != 't' && arg[1] != '_')
      {
!       semsg(_(e_unknown_option_str_2), arg);
!       goto theend;
!     }
!     if (op != NULL && *op != '='
!       && (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
!           || (opt_type == gov_string && *op != '.')))
!     {
!       semsg(_(e_wrong_variable_type_for_str_equal), op);
!       goto theend;
!     }
  
!     if ((opt_type == gov_bool
!               || opt_type == gov_number
!               || opt_type == gov_hidden_bool
!               || opt_type == gov_hidden_number)
!                    && (tv->v_type != VAR_STRING || !in_vim9script()))
!     {
!       if (opt_type == gov_bool || opt_type == gov_hidden_bool)
!           // bool, possibly hidden
!           n = (long)tv_get_bool_chk(tv, &failed);
!       else
!           // number, possibly hidden
!           n = (long)tv_get_number_chk(tv, &failed);
!       if (failed)
!           goto theend;
!     }
  
!     if ((opt_p_flags & P_FUNC) && (tv->v_type == VAR_PARTIAL
!                                                   || tv->v_type == VAR_FUNC))
!     {
!       // If the option can be set to a function reference or a lambda
!       // and the passed value is a function reference, then convert it to
!       // the name (string) of the function reference.
!       s = tv2string(tv, &tofree, numbuf, 0);
!       if (s == NULL)
!           goto theend;
!     }
!     // Avoid setting a string option to the text "v:false" or similar.
!     // In Vim9 script also don't convert a number to string.
!     else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL
!                    && (!in_vim9script() || tv->v_type != VAR_NUMBER))
!     {
!       s = tv_get_string_chk(tv);
!       if (s == NULL)
!           goto theend;
!     }
!     else if (opt_type == gov_string || opt_type == gov_hidden_string)
!     {
!       emsg(_(e_string_required));
!       goto theend;
!     }
  
!     if (op != NULL && *op != '=')
!     {
!       // number, in legacy script also bool
!       if (opt_type == gov_number
!                                || (opt_type == gov_bool && !in_vim9script()))
!       {
!           switch (*op)
            {
!               case '+': n = numval + n; break;
!               case '-': n = numval - n; break;
!               case '*': n = numval * n; break;
!               case '/': n = (long)num_divide(numval, n, &failed); break;
!               case '%': n = (long)num_modulus(numval, n, &failed); break;
            }
+           s = NULL;
+           if (failed)
+               goto theend;
        }
!       else if (opt_type == gov_string && stringval != NULL && s != NULL)
        {
!           // string
!           s = concat_str(stringval, s);
!           vim_free(stringval);
!           stringval = s;
        }
      }
+ 
+     char *err = set_option_value(arg, n, s, scope);
+     arg_end = p;
+     if (err != NULL)
+       emsg(_(err));
+ 
+ theend:
+     *p = c1;
+     vim_free(stringval);
+     vim_free(tofree);
      return arg_end;
  }
  
***************
*** 4303,4311 ****
      char_u    nbuf[NUMBUFLEN];
      int               error = FALSE;
  
      if (varp->v_type == VAR_BOOL)
      {
!       if (is_string_option(varname))
        {
            emsg(_(e_string_required));
            return;
--- 4311,4327 ----
      char_u    nbuf[NUMBUFLEN];
      int               error = FALSE;
  
+     int               opt_idx = findoption(varname);
+     if (opt_idx < 0)
+     {
+       semsg(_(e_unknown_option_str_2), varname);
+       return;
+     }
+     int               opt_p_flags = get_option_flags(opt_idx);
+ 
      if (varp->v_type == VAR_BOOL)
      {
!       if (opt_p_flags & P_STRING)
        {
            emsg(_(e_string_required));
            return;
***************
*** 4315,4323 ****
      }
      else
      {
!       if (!in_vim9script() || varp->v_type != VAR_STRING)
            numval = (long)tv_get_number_chk(varp, &error);
!       strval = tv_get_string_buf_chk(varp, nbuf);
      }
      if (!error && strval != NULL)
        set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
--- 4331,4341 ----
      }
      else
      {
!       if ((opt_p_flags & (P_NUM|P_BOOL))
!               && (!in_vim9script() || varp->v_type != VAR_STRING))
            numval = (long)tv_get_number_chk(varp, &error);
!       if (!error)
!           strval = tv_get_string_buf_chk(varp, nbuf);
      }
      if (!error && strval != NULL)
        set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
*** ../vim-9.0.1630/src/option.c        2023-05-31 17:12:07.888535653 +0100
--- src/option.c        2023-06-14 16:33:06.794336531 +0100
***************
*** 5462,5481 ****
  }
  #endif
  
- #if defined(FEAT_EVAL) || defined(PROTO)
- /*
-  * Return TRUE if "name" is a string option.
-  * Returns FALSE if option "name" does not exist.
-  */
-     int
- is_string_option(char_u *name)
- {
-     int idx = findoption(name);
- 
-     return idx >= 0 && (options[idx].flags & P_STRING);
- }
- #endif
- 
  /*
   * Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
   * When "has_lt" is true there is a '<' before "*arg_arg".
--- 5462,5467 ----
*** ../vim-9.0.1630/src/proto/option.pro        2023-03-12 21:20:51.441254187 
+0000
--- src/proto/option.pro        2023-06-14 16:33:06.794336531 +0100
***************
*** 105,111 ****
  char_u *get_highlight_default(void);
  char_u *get_encoding_default(void);
  int is_option_allocated(char *name);
- int is_string_option(char_u *name);
  int makeset(FILE *fd, int opt_flags, int local_only);
  int makefoldset(FILE *fd);
  void clear_termoptions(void);
--- 105,110 ----
*** ../vim-9.0.1630/src/testdir/test_let.vim    2023-05-02 16:25:35.630819728 
+0100
--- src/testdir/test_let.vim    2023-06-14 16:33:06.794336531 +0100
***************
*** 266,280 ****
  func Test_let_option_error()
    let _w = &tw
    let &tw = 80
!   call assert_fails('let &tw .= 1', 'E734:')
    call assert_equal(80, &tw)
    let &tw = _w
  
    let _w = &fillchars
    let &fillchars = "vert:|"
!   call assert_fails('let &fillchars += "diff:-"', 'E734:')
    call assert_equal("vert:|", &fillchars)
    let &fillchars = _w
  endfunc
  
  " Errors with the :let statement
--- 266,300 ----
  func Test_let_option_error()
    let _w = &tw
    let &tw = 80
!   call assert_fails('let &tw .= 1', ['E734:', 'E734:'])
!   call assert_fails('let &tw .= []', ['E734:', 'E734:'])
!   call assert_fails('let &tw = []', ['E745:', 'E745:'])
!   call assert_fails('let &tw += []', ['E745:', 'E745:'])
    call assert_equal(80, &tw)
    let &tw = _w
  
+   let _w = &autoread
+   let &autoread = 1
+   call assert_fails('let &autoread .= 1', ['E734:', 'E734:'])
+   call assert_fails('let &autoread .= []', ['E734:', 'E734:'])
+   call assert_fails('let &autoread = []', ['E745:', 'E745:'])
+   call assert_fails('let &autoread += []', ['E745:', 'E745:'])
+   call assert_equal(1, &autoread)
+   let &autoread = _w
+ 
    let _w = &fillchars
    let &fillchars = "vert:|"
!   call assert_fails('let &fillchars += "diff:-"', ['E734:', 'E734:'])
!   call assert_fails('let &fillchars += []', ['E734:', 'E734:'])
!   call assert_fails('let &fillchars = []', ['E730:', 'E730:'])
!   call assert_fails('let &fillchars .= []', ['E730:', 'E730:'])
    call assert_equal("vert:|", &fillchars)
    let &fillchars = _w
+ 
+   call assert_fails('let &nosuchoption = 1', ['E355:', 'E355:'])
+   call assert_fails('let &nosuchoption = ""', ['E355:', 'E355:'])
+   call assert_fails('let &nosuchoption = []', ['E355:', 'E355:'])
+   call assert_fails('let &t_xx = []', ['E730:', 'E730:'])
  endfunc
  
  " Errors with the :let statement
*** ../vim-9.0.1630/src/testdir/test_options.vim        2023-05-06 
22:21:07.247211940 +0100
--- src/testdir/test_options.vim        2023-06-14 16:33:06.794336531 +0100
***************
*** 376,382 ****
    call assert_equal('"set filetype=' .. getcompletion('a*', 
'filetype')->join(), @:)
  endfunc
  
! func Test_set_errors()
    call assert_fails('set scroll=-1', 'E49:')
    call assert_fails('set backupcopy=', 'E474:')
    call assert_fails('set regexpengine=3', 'E474:')
--- 376,382 ----
    call assert_equal('"set filetype=' .. getcompletion('a*', 
'filetype')->join(), @:)
  endfunc
  
! func Test_set_option_errors()
    call assert_fails('set scroll=-1', 'E49:')
    call assert_fails('set backupcopy=', 'E474:')
    call assert_fails('set regexpengine=3', 'E474:')
***************
*** 478,484 ****
    if has('python') || has('python3')
      call assert_fails('set pyxversion=6', 'E474:')
    endif
!   call assert_fails("let &tabstop='ab'", 'E521:')
    call assert_fails('set spellcapcheck=%\\(', 'E54:')
    call assert_fails('set sessionoptions=curdir,sesdir', 'E474:')
    call assert_fails('set foldmarker={{{,', 'E474:')
--- 478,484 ----
    if has('python') || has('python3')
      call assert_fails('set pyxversion=6', 'E474:')
    endif
!   call assert_fails("let &tabstop='ab'", ['E521:', 'E521:'])
    call assert_fails('set spellcapcheck=%\\(', 'E54:')
    call assert_fails('set sessionoptions=curdir,sesdir', 'E474:')
    call assert_fails('set foldmarker={{{,', 'E474:')
***************
*** 502,507 ****
--- 502,513 ----
    call assert_fails('set t_#-&', 'E522:')
    call assert_fails('let &formatoptions = "?"', 'E539:')
    call assert_fails('call setbufvar("", "&formatoptions", "?")', 'E539:')
+   call assert_fails('call setwinvar(0, "&scrolloff", [])', ['E745:', 'E745:'])
+   call assert_fails('call setwinvar(0, "&list", [])', ['E745:', 'E745:'])
+   call assert_fails('call setwinvar(0, "&listchars", [])', ['E730:', 'E730:'])
+   call assert_fails('call setwinvar(0, "&nosuchoption", 0)', ['E355:', 
'E355:'])
+   call assert_fails('call setwinvar(0, "&nosuchoption", "")', ['E355:', 
'E355:'])
+   call assert_fails('call setwinvar(0, "&nosuchoption", [])', ['E355:', 
'E355:'])
  endfunc
  
  func Test_set_encoding()
*** ../vim-9.0.1630/src/testdir/test_vim9_assign.vim    2023-06-10 
19:00:08.457416167 +0100
--- src/testdir/test_vim9_assign.vim    2023-06-14 16:33:06.798336525 +0100
***************
*** 145,150 ****
--- 145,156 ----
    &ts %= 4
    assert_equal(2, &ts)
  
+   assert_fails('&ts /= 0', ['E1154:', 'E1154:'])
+   assert_fails('&ts %= 0', ['E1154:', 'E1154:'])
+   assert_fails('&ts /= []', ['E745:', 'E745:'])
+   assert_fails('&ts %= []', ['E745:', 'E745:'])
+   assert_equal(2, &ts)
+ 
    var f100: float = 100.0
    f100 /= 5
    assert_equal(20.0, f100)
*** ../vim-9.0.1630/src/testdir/test_vim9_builtin.vim   2023-06-08 
17:09:40.192768840 +0100
--- src/testdir/test_vim9_builtin.vim   2023-06-14 16:33:06.798336525 +0100
***************
*** 3944,3950 ****
    v9.CheckDefAndScriptFailure(['setwinvar("a", "b", 1)'], ['E1013: Argument 
1: type mismatch, expected number but got string', 'E1210: Number required for 
argument 1'])
    v9.CheckDefAndScriptFailure(['setwinvar(1, 2, "c")'], ['E1013: Argument 2: 
type mismatch, expected string but got number', 'E1174: String required for 
argument 2'])
    assert_fails('setwinvar(1, "", 10)', 'E461: Illegal variable name')
!   assert_fails('setwinvar(0, "&rulerformat", true)', 'E928:')
  enddef
  
  def Test_sha256()
--- 3944,3950 ----
    v9.CheckDefAndScriptFailure(['setwinvar("a", "b", 1)'], ['E1013: Argument 
1: type mismatch, expected number but got string', 'E1210: Number required for 
argument 1'])
    v9.CheckDefAndScriptFailure(['setwinvar(1, 2, "c")'], ['E1013: Argument 2: 
type mismatch, expected string but got number', 'E1174: String required for 
argument 2'])
    assert_fails('setwinvar(1, "", 10)', 'E461: Illegal variable name')
!   assert_fails('setwinvar(0, "&rulerformat", true)', ['E928:', 'E928:'])
  enddef
  
  def Test_sha256()
*** ../vim-9.0.1630/src/testdir/test_vimscript.vim      2023-05-24 
21:02:20.489162125 +0100
--- src/testdir/test_vimscript.vim      2023-06-14 16:33:06.798336525 +0100
***************
*** 7077,7083 ****
      call assert_equal(6, &scrolljump)
      let &scrolljump %= 5
      call assert_equal(1, &scrolljump)
!     call assert_fails('let &scrolljump .= "j"', 'E734:')
      set scrolljump&vim
  
      let &foldlevelstart = 2
--- 7077,7083 ----
      call assert_equal(6, &scrolljump)
      let &scrolljump %= 5
      call assert_equal(1, &scrolljump)
!     call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:'])
      set scrolljump&vim
  
      let &foldlevelstart = 2
*** ../vim-9.0.1630/src/version.c       2023-06-14 15:09:59.226017801 +0100
--- src/version.c       2023-06-14 16:33:42.638290614 +0100
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1631,
  /**/

-- 
"The question of whether computers can think is just like the question
of whether submarines can swim."      -- Edsger W. Dijkstra

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20230614154029.13C6A1C0CE4%40moolenaar.net.

Raspunde prin e-mail lui