To: vim_dev@googlegroups.com Subject: Patch 9.0.1631 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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", "" or "" 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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///