diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/ascii.h | 8 | ||||
-rw-r--r-- | src/nvim/charset.c | 75 | ||||
-rw-r--r-- | src/nvim/charset.h | 11 | ||||
-rw-r--r-- | src/nvim/eval.c | 35 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 6 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 17 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 3 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/keymap.c | 12 | ||||
-rw-r--r-- | src/nvim/ops.c | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_eval_stuff.vim | 36 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr.vim | 11 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 12 | ||||
-rw-r--r-- | src/nvim/viml/parser/expressions.c | 4 |
17 files changed, 181 insertions, 67 deletions
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index f41068ea70..7b5e82cd3f 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -169,6 +169,14 @@ static inline bool ascii_isbdigit(int c) return (c == '0' || c == '1'); } +/// Checks if `c` is an octal digit, that is, 0-7. +/// +/// @see {ascii_isdigit} +static inline bool ascii_isodigit(int c) +{ + return (c >= '0' && c <= '7'); +} + /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4725c0d08f..ab4e4ad4bd 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1385,6 +1385,8 @@ bool vim_isblankline(char_u *lbuf) /// If "prep" is not NULL, returns a flag to indicate the type of the number: /// 0 decimal /// '0' octal +/// 'O' octal +/// 'o' octal /// 'B' bin /// 'b' bin /// 'X' hex @@ -1396,20 +1398,25 @@ bool vim_isblankline(char_u *lbuf) /// If "what" contains STR2NR_OCT recognize octal numbers. /// If "what" contains STR2NR_HEX recognize hex numbers. /// If "what" contains STR2NR_FORCE always assume bin/oct/hex. +/// If "what" contains STR2NR_QUOTE ignore embedded single quotes /// If maxlen > 0, check at a maximum maxlen chars. +/// If strict is true, check the number strictly. return *len = 0 if fail. /// /// @param start /// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is -/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using -/// STR2NR_FORCE is always zero. +/// hexadecimal, '0', 'o' or 'O' is octal, 'b' or 'B' is binary. +/// When using STR2NR_FORCE is always zero. /// @param len Returns the detected length of number. /// @param what Recognizes what number passed, @see ChStr2NrFlags. /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. +/// @param strict If true, fail if the number has unexpected trailing +/// alpha-numeric chars: *len is set to 0 and nothing else is +/// returned. void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, - uvarnumber_T *const unptr, const int maxlen) + uvarnumber_T *const unptr, const int maxlen, const bool strict) FUNC_ATTR_NONNULL_ARG(1) { const char *ptr = (const char *)start; @@ -1419,14 +1426,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, const bool negative = (ptr[0] == '-'); uvarnumber_T un = 0; + if (len != NULL) { + *len = 0; + } + if (negative) { ptr++; } if (what & STR2NR_FORCE) { - // When forcing main consideration is skipping the prefix. Octal and decimal - // numbers have no prefixes to skip. pre is not set. - switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) { + // When forcing main consideration is skipping the prefix. Decimal numbers + // have no prefixes to skip. pre is not set. + switch (what & ~(STR2NR_FORCE | STR2NR_QUOTE)) { case STR2NR_HEX: { if (!STRING_ENDED(ptr + 2) && ptr[0] == '0' @@ -1445,7 +1456,16 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } goto vim_str2nr_bin; } - case STR2NR_OCT: { + // Make STR2NR_OOCT work the same as STR2NR_OCT when forcing. + case STR2NR_OCT: + case STR2NR_OOCT: + case STR2NR_OCT | STR2NR_OOCT: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'o' || ptr[1] == 'O') + && ascii_isodigit(ptr[2])) { + ptr += 2; + } goto vim_str2nr_oct; } case 0: { @@ -1455,9 +1475,9 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, abort(); } } - } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && !STRING_ENDED(ptr + 1) - && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { + } else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN)) + && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' + && ptr[1] != '9') { pre = ptr[1]; // Detect hexadecimal: 0x or 0X followed by hex digit. if ((what & STR2NR_HEX) @@ -1475,10 +1495,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr += 2; goto vim_str2nr_bin; } - // Detect octal number: zero followed by octal digits without '8' or '9'. + // Detect octal: 0o or 0O followed by octal digits (without '8' or '9'). + if ((what & STR2NR_OOCT) + && !STRING_ENDED(ptr + 2) + && (pre == 'O' || pre == 'o') + && ascii_isodigit(ptr[2])) { + ptr += 2; + goto vim_str2nr_oct; + } + // Detect old octal format: 0 followed by octal digits. pre = 0; if (!(what & STR2NR_OCT) - || !('0' <= ptr[1] && ptr[1] <= '7')) { + || !ascii_isodigit(ptr[1])) { goto vim_str2nr_dec; } for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { @@ -1492,11 +1520,22 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } - // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. + // Do the conversion manually to avoid sscanf() quirks. abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ - while (!STRING_ENDED(ptr) && (cond)) { \ + const char *const after_prefix = ptr; \ + while (!STRING_ENDED(ptr)) { \ + if ((what & STR2NR_QUOTE) && ptr > after_prefix && *ptr == '\'') { \ + ptr++; \ + if (!STRING_ENDED(ptr) && (cond)) { \ + continue; \ + } \ + ptr--; \ + } \ + if (!(cond)) { \ + break; \ + } \ const uvarnumber_T digit = (uvarnumber_T)(conv); \ /* avoid ubsan error for overflow */ \ if (un < UVARNUMBER_MAX / base \ @@ -1513,7 +1552,7 @@ vim_str2nr_bin: PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_oct: - PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); + PARSE_NUMBER(8, (ascii_isodigit(*ptr)), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_dec: PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); @@ -1524,6 +1563,12 @@ vim_str2nr_hex: #undef PARSE_NUMBER vim_str2nr_proceed: + // Check for an alpha-numeric character immediately following, that is + // most likely a typo. + if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) { + return; + } + if (prep != NULL) { *prep = pre; } diff --git a/src/nvim/charset.h b/src/nvim/charset.h index e657ce19b6..2fef4d78a2 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -23,13 +23,20 @@ typedef enum { STR2NR_BIN = (1 << 0), ///< Allow binary numbers. STR2NR_OCT = (1 << 1), ///< Allow octal numbers. STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers. + STR2NR_OOCT = (1 << 3), ///< Octal with prefix "0o": 0o777 /// Force one of the above variants. /// /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero /// as flags, but still present for completeness. - STR2NR_FORCE = (1 << 3), + /// + /// STR2NR_FORCE|STR2NR_OCT|STR2NR_OOCT is the same as STR2NR_FORCE|STR2NR_OCT + /// or STR2NR_FORCE|STR2NR_OOCT. + STR2NR_FORCE = (1 << 7), /// Recognize all formats vim_str2nr() can recognize. - STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX, + STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX | STR2NR_OOCT, + /// Disallow octals numbers without the 0o prefix. + STR2NR_NO_OCT = STR2NR_BIN | STR2NR_HEX | STR2NR_OOCT, + STR2NR_QUOTE = (1 << 4), ///< Ignore embedded single quotes. } ChStr2NrFlags; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5603fbb082..dfbb187b5b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1456,21 +1456,18 @@ static void ex_let_const(exarg_T *eap, const bool is_const) /* * Assign the typevalue "tv" to the variable or variables at "arg_start". * Handles both "var" with any type and "[var, var; var]" with a list type. - * When "nextchars" is not NULL it points to a string with characters that + * When "op" is not NULL it points to a string with characters that * must appear after the variable(s). Use "+", "-" or "." for add, subtract * or concatenate. * Returns OK or FAIL; */ -static int -ex_let_vars( - char_u *arg_start, - typval_T *tv, - int copy, // copy values from "tv", don't move - int semicolon, // from skip_var_list() - int var_count, // from skip_var_list() - int is_const, // lock variables for :const - char_u *nextchars -) +static int ex_let_vars(char_u *arg_start, + typval_T *tv, + int copy, // copy values from "tv", don't move + int semicolon, // from skip_var_list() + int var_count, // from skip_var_list() + int is_const, // lock variables for :const + char_u *op) { char_u *arg = arg_start; typval_T ltv; @@ -1479,7 +1476,7 @@ ex_let_vars( /* * ":let var = expr" or ":for var in list" */ - if (ex_let_one(arg, tv, copy, is_const, nextchars, nextchars) == NULL) { + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { return FAIL; } return OK; @@ -1510,7 +1507,7 @@ ex_let_vars( while (*arg != ']') { arg = skipwhite(arg + 1); arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, - (const char_u *)",;]", nextchars); + (const char_u *)",;]", op); if (arg == NULL) { return FAIL; } @@ -1532,8 +1529,8 @@ ex_let_vars( ltv.vval.v_list = rest_list; tv_list_ref(rest_list); - arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, - (char_u *)"]", nextchars); + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, (char_u *)"]", + op); tv_clear(<v); if (arg == NULL) { return FAIL; @@ -3950,7 +3947,12 @@ static int eval7( rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + EMSG2(_(e_invexpr2), *arg); + ret = FAIL; + break; + } *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -8670,6 +8672,7 @@ handle_subscript( } } + // "." is ".name" lookup when we found a dict. while (ret == OK && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index faff29b268..a7242ba73a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -344,7 +344,7 @@ return { stdpath={args=1}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, - str2nr={args={1, 2}}, + str2nr={args={1, 3}}, strcharpart={args={2, 3}}, strchars={args={1,2}}, strdisplaywidth={args={1, 2}}, diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index bd4dc87d31..89e1f04bfd 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -437,7 +437,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, t += 4; uvarnumber_T ch; vim_str2nr((char_u *)ubuf, NULL, NULL, - STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); + STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true); if (ch == 0) { hasnul = true; } @@ -611,8 +611,8 @@ parse_json_number_check: // Convert integer varnumber_T nr; int num_len; - vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); - if ((int) exp_num_len != num_len) { + vim_str2nr((char_u *)s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true); + if ((int)exp_num_len != num_len) { emsgf(_("E685: internal error: while converting number \"%.*s\" " "to integer vim_str2nr consumed %i bytes in place of %zu"), (int) exp_num_len, s, num_len, exp_num_len); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 99f9f17e0a..801b0f9d1c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9998,7 +9998,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; varnumber_T n; - int what; + int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { base = tv_get_number(&argvars[1]); @@ -10006,6 +10006,9 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) { + what |= STR2NR_QUOTE; + } } char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); @@ -10015,22 +10018,20 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } switch (base) { case 2: { - what = STR2NR_BIN | STR2NR_FORCE; + what |= STR2NR_BIN | STR2NR_FORCE; break; } case 8: { - what = STR2NR_OCT | STR2NR_FORCE; + what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE; break; } case 16: { - what = STR2NR_HEX | STR2NR_FORCE; + what |= STR2NR_HEX | STR2NR_FORCE; break; } - default: { - what = 0; - } } - vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false); + // Text after the number is silently ignored. if (isneg) { rettv->vval.v_number = -n; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 5cb0058ec6..22b3bf026b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2990,7 +2990,8 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; if (tv->vval.v_string != NULL) { - vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, + false); } return n; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index bd8d3973d5..2ea16f0d50 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -609,7 +609,7 @@ void ex_sort(exarg_T *eap) } else { nrs[lnum - eap->line1].st_u.num.is_number = true; vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].st_u.num.value, NULL, 0); + &nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false); } } else { s = skipwhite(p); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 0f98c9cd34..b90773ce83 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6056,7 +6056,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); *str += len; *num1 = (int)num; first = true; @@ -6064,7 +6064,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == ',') { // parse "to" part of range *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 277b9ade89..c6966ff9fa 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -628,7 +628,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { bp += 3; // skip t_xx, xx may be '-' or '>' } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } bp += l + 5; break; } @@ -654,7 +658,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { // <Char-123> or <Char-033> or <Char-0x33> - vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); + vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } key = (int)n; } else { int off = 1; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3f0d85dc88..77432149ce 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5017,7 +5017,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) 0 + (do_bin ? STR2NR_BIN : 0) + (do_oct ? STR2NR_OCT : 0) + (do_hex ? STR2NR_HEX : 0), - NULL, &n, maxlen); + NULL, &n, maxlen, false); // ignore leading '-' for hex, octal and bin numbers if (pre && negative) { diff --git a/src/nvim/option.c b/src/nvim/option.c index fdfb409c5e..23b488ea68 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1276,9 +1276,9 @@ int do_set( } } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative, octal and hex numbers. - vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); - if (arg[i] != NUL && !ascii_iswhite(arg[i])) { - errmsg = e_invarg; + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true); + if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) { + errmsg = (char_u *)N_("E521: Number required after ="); goto skip; } } else { @@ -7486,7 +7486,7 @@ unsigned int get_bkc_value(buf_T *buf) /// /// @param win If not NULL, the window to get the local option from; global /// otherwise. -char_u *get_showbreak_value(win_T *const win FUNC_ATTR_UNUSED) +char_u *get_showbreak_value(win_T *const win) FUNC_ATTR_WARN_UNUSED_RESULT { if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) { diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 4870b9a60a..084c856ba0 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -33,6 +33,24 @@ func Test_for_invalid() redraw endfunc +func Test_readfile_binary() + new + call setline(1, ['one', 'two', 'three']) + setlocal ff=dos + silent write XReadfile + let lines = readfile('XReadfile') + call assert_equal(['one', 'two', 'three'], lines) + let lines = readfile('XReadfile', '', 2) + call assert_equal(['one', 'two'], lines) + let lines = readfile('XReadfile', 'b') + call assert_equal(["one\r", "two\r", "three\r", ""], lines) + let lines = readfile('XReadfile', 'b', 2) + call assert_equal(["one\r", "two\r"], lines) + + bwipe! + call delete('XReadfile') +endfunc + func Test_mkdir_p() call mkdir('Xmkdir/nested', 'p') call assert_true(isdirectory('Xmkdir/nested')) @@ -90,6 +108,15 @@ func Test_string_concatenation() call assert_equal('ab', a) endfunc +" Test fix for issue #4507 +func Test_skip_after_throw() + try + throw 'something' + let x = wincol() || &ts + catch /something/ + endtry +endfunc + func Test_nocatch_restore_silent_emsg() silent! try throw 1 @@ -111,15 +138,6 @@ func Test_let_errmsg() let v:errmsg = '' endfunc -" Test fix for issue #4507 -func Test_skip_after_throw() - try - throw 'something' - let x = wincol() || &ts - catch /something/ - endtry -endfunc - func Test_number_max_min_size() " This will fail on systems without 64 bit number support or when not " configured correctly. diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 0b41a1127a..c49285621a 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -502,6 +502,17 @@ func Test_empty_concatenate() call assert_equal('b', 'b' . 'a'[4:0]) endfunc +func Test_broken_number() + let X = 'bad' + call assert_fails('echo 1X', 'E15:') + call assert_fails('echo 0b1X', 'E15:') + call assert_fails('echo 0b12', 'E15:') + call assert_fails('echo 0x1X', 'E15:') + call assert_fails('echo 011X', 'E15:') + call assert_equal(2, str2nr('2a')) + call assert_fails('inoremap <Char-0b1z> b', 'E474:') +endfunc + func Test_eval_after_if() let s:val = '' func SetVal(x) diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6cb3e24201..ed46730cbb 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -152,6 +152,10 @@ func Test_str2nr() call assert_equal(65, str2nr('0101', 8)) call assert_equal(-65, str2nr('-101', 8)) call assert_equal(-65, str2nr('-0101', 8)) + call assert_equal(65, str2nr('0o101', 8)) + call assert_equal(65, str2nr('0O0101', 8)) + call assert_equal(-65, str2nr('-0O101', 8)) + call assert_equal(-65, str2nr('-0o0101', 8)) call assert_equal(11259375, str2nr('abcdef', 16)) call assert_equal(11259375, str2nr('ABCDEF', 16)) @@ -161,8 +165,16 @@ func Test_str2nr() call assert_equal(11259375, str2nr('0XABCDEF', 16)) call assert_equal(-11259375, str2nr('-0xABCDEF', 16)) + call assert_equal(1, str2nr("1'000'000", 10, 0)) + call assert_equal(256, str2nr("1'0000'0000", 2, 1)) + call assert_equal(262144, str2nr("1'000'000", 8, 1)) + call assert_equal(1000000, str2nr("1'000'000", 10, 1)) + call assert_equal(1000, str2nr("1'000''000", 10, 1)) + call assert_equal(65536, str2nr("1'00'00", 16, 1)) + call assert_equal(0, str2nr('0x10')) call assert_equal(0, str2nr('0b10')) + call assert_equal(0, str2nr('0o10')) call assert_equal(1, str2nr('12', 2)) call assert_equal(1, str2nr('18', 8)) call assert_equal(1, str2nr('1g', 16)) diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index e9d82ca87d..f5bd5479c4 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -351,7 +351,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } if (exp_start) { vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, - (int)(ret.len - exp_start)); + (int)(ret.len - exp_start), false); } if (exp_negative) { exp_part += frac_size; @@ -369,7 +369,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) int len; int prep; vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, - &ret.data.num.val.integer, (int)pline.size); + &ret.data.num.val.integer, (int)pline.size, false); ret.len = (size_t)len; const uint8_t bases[] = { [0] = 10, |