diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index f05e6ffa6b..8f52e14b46 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -258,8 +258,9 @@ assert_equal({expected}, {actual} [, {msg}]) *assert_equal()* always matters. Example: >vim call assert_equal('foo', 'bar', 'baz') -< Will add the following to |v:errors|: - test.vim line 12: baz: Expected 'foo' but got 'bar' ~ +< Will add the following to |v:errors|: > + test.vim line 12: baz: Expected 'foo' but got 'bar' +< Parameters: ~ • {expected} (`any`) @@ -401,8 +402,9 @@ assert_match({pattern}, {actual} [, {msg}]) *assert_match()* Example: >vim call assert_match('^f.*o$', 'foobar') -< Will result in a string to be added to |v:errors|: - test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~ +< Will result in a string to be added to |v:errors|: > + test.vim line 12: Pattern '^f.*o$' does not match 'foobar' +< Parameters: ~ • {pattern} (`string`) @@ -7406,54 +7408,54 @@ printf({fmt}, {expr1} ...) *printf()* *E1500* You cannot mix positional and non-positional arguments: >vim echo printf("%s%1$s", "One", "Two") -< E1500: Cannot mix positional and non-positional arguments: - %s%1$s - + " E1500: Cannot mix positional and non-positional arguments: + " %s%1$s +< *E1501* You cannot skip a positional argument in a format string: >vim echo printf("%3$s%1$s", "One", "Two", "Three") -< E1501: format argument 2 unused in $-style format: - %3$s%1$s - + " E1501: format argument 2 unused in $-style format: + " %3$s%1$s +< *E1502* You can re-use a [field-width] (or [precision]) argument: >vim - echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) -< 1 at width 2 is: 01 - + echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) + " 1 at width 2 is: 01 +< However, you can't use it as a different type: >vim - echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) -< E1502: Positional argument 2 used as field width reused as - different type: long int/int - + echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) + " E1502: Positional argument 2 used as field width reused as + " different type: long int/int +< *E1503* When a positional argument is used, but not the correct number or arguments is given, an error is raised: >vim - echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) -< E1503: Positional argument 3 out of bounds: %1$d at width - %2$d is: %01$*2$.*3$d - + echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$d at width + " %2$d is: %1$0*2$.*3$d +< Only the first error is reported: >vim - echo printf("%01$*2$.*3$d %4$d", 1, 2) -< E1503: Positional argument 3 out of bounds: %01$*2$.*3$d - %4$d - + echo printf("%1$0*2$.*3$d %4$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d + " %4$d +< *E1504* A positional argument can be used more than once: >vim echo printf("%1$s %2$s %1$s", "One", "Two") -< One Two One - + " One Two One +< However, you can't use a different type the second time: >vim echo printf("%1$s %2$s %1$d", "One", "Two") -< E1504: Positional argument 1 type used inconsistently: - int/string - + " E1504: Positional argument 1 type used inconsistently: + " int/string +< *E1505* Various other errors that lead to a format string being wrongly formatted lead to: >vim echo printf("%1$d at width %2$d is: %01$*2$.3$d", 1, 2) -< E1505: Invalid format specifier: %1$d at width %2$d is: - %01$*2$.3$d - + " E1505: Invalid format specifier: %1$d at width %2$d is: + " %01$*2$.3$d +< *E1507* This internal error indicates that the logic to parse a positional format argument ran into a problem that couldn't be diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index d3e48a89f8..1a13885ceb 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -212,8 +212,9 @@ function vim.fn.assert_beeps(cmd) end --- always matters. --- Example: >vim --- call assert_equal('foo', 'bar', 'baz') ---- +--- test.vim line 12: baz: Expected 'foo' but got 'bar' +--- < --- --- @param expected any --- @param actual any @@ -336,8 +337,9 @@ function vim.fn.assert_inrange(lower, upper, actual, msg) end --- --- Example: >vim --- call assert_match('^f.*o$', 'foobar') ---- +--- test.vim line 12: Pattern '^f.*o$' does not match 'foobar' +--- < --- --- @param pattern string --- @param actual string @@ -6731,54 +6733,54 @@ function vim.fn.prevnonblank(lnum) end --- *E1500* --- You cannot mix positional and non-positional arguments: >vim --- echo printf("%s%1$s", "One", "Two") ---- < E1500: Cannot mix positional and non-positional arguments: ---- %s%1$s ---- +--- " E1500: Cannot mix positional and non-positional arguments: +--- " %s%1$s +--- < --- *E1501* --- You cannot skip a positional argument in a format string: >vim --- echo printf("%3$s%1$s", "One", "Two", "Three") ---- < E1501: format argument 2 unused in $-style format: ---- %3$s%1$s ---- +--- " E1501: format argument 2 unused in $-style format: +--- " %3$s%1$s +--- < --- *E1502* --- You can re-use a [field-width] (or [precision]) argument: >vim ---- echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) ---- < 1 at width 2 is: 01 ---- +--- echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) +--- " 1 at width 2 is: 01 +--- < --- However, you can't use it as a different type: >vim ---- echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) ---- < E1502: Positional argument 2 used as field width reused as ---- different type: long int/int ---- +--- echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) +--- " E1502: Positional argument 2 used as field width reused as +--- " different type: long int/int +--- < --- *E1503* --- When a positional argument is used, but not the correct number --- or arguments is given, an error is raised: >vim ---- echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) ---- < E1503: Positional argument 3 out of bounds: %1$d at width ---- %2$d is: %01$*2$.*3$d ---- +--- echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) +--- " E1503: Positional argument 3 out of bounds: %1$d at width +--- " %2$d is: %1$0*2$.*3$d +--- < --- Only the first error is reported: >vim ---- echo printf("%01$*2$.*3$d %4$d", 1, 2) ---- < E1503: Positional argument 3 out of bounds: %01$*2$.*3$d ---- %4$d ---- +--- echo printf("%1$0*2$.*3$d %4$d", 1, 2) +--- " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d +--- " %4$d +--- < --- *E1504* --- A positional argument can be used more than once: >vim --- echo printf("%1$s %2$s %1$s", "One", "Two") ---- < One Two One ---- +--- " One Two One +--- < --- However, you can't use a different type the second time: >vim --- echo printf("%1$s %2$s %1$d", "One", "Two") ---- < E1504: Positional argument 1 type used inconsistently: ---- int/string ---- +--- " E1504: Positional argument 1 type used inconsistently: +--- " int/string +--- < --- *E1505* --- Various other errors that lead to a format string being --- wrongly formatted lead to: >vim --- echo printf("%1$d at width %2$d is: %01$*2$.3$d", 1, 2) ---- < E1505: Invalid format specifier: %1$d at width %2$d is: ---- %01$*2$.3$d ---- +--- " E1505: Invalid format specifier: %1$d at width %2$d is: +--- " %01$*2$.3$d +--- < --- *E1507* --- This internal error indicates that the logic to parse a --- positional format argument ran into a problem that couldn't be diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 2f949327f4..73367ee86b 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -314,9 +314,9 @@ M.funcs = { always matters. Example: >vim call assert_equal('foo', 'bar', 'baz') - + test.vim line 12: baz: Expected 'foo' but got 'bar' + < ]=], name = 'assert_equal', params = { { 'expected', 'any' }, { 'actual', 'any' }, { 'msg', 'any' } }, @@ -470,9 +470,9 @@ M.funcs = { Example: >vim call assert_match('^f.*o$', 'foobar') - + test.vim line 12: Pattern '^f.*o$' does not match 'foobar' + < ]=], name = 'assert_match', params = { { 'pattern', 'string' }, { 'actual', 'string' }, { 'msg', 'string' } }, @@ -8180,54 +8180,54 @@ M.funcs = { *E1500* You cannot mix positional and non-positional arguments: >vim echo printf("%s%1$s", "One", "Two") - < E1500: Cannot mix positional and non-positional arguments: - %s%1$s - + " E1500: Cannot mix positional and non-positional arguments: + " %s%1$s + < *E1501* You cannot skip a positional argument in a format string: >vim echo printf("%3$s%1$s", "One", "Two", "Three") - < E1501: format argument 2 unused in $-style format: - %3$s%1$s - + " E1501: format argument 2 unused in $-style format: + " %3$s%1$s + < *E1502* You can re-use a [field-width] (or [precision]) argument: >vim - echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) - < 1 at width 2 is: 01 - + echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) + " 1 at width 2 is: 01 + < However, you can't use it as a different type: >vim - echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) - < E1502: Positional argument 2 used as field width reused as - different type: long int/int - + echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) + " E1502: Positional argument 2 used as field width reused as + " different type: long int/int + < *E1503* When a positional argument is used, but not the correct number or arguments is given, an error is raised: >vim - echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) - < E1503: Positional argument 3 out of bounds: %1$d at width - %2$d is: %01$*2$.*3$d - + echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$d at width + " %2$d is: %1$0*2$.*3$d + < Only the first error is reported: >vim - echo printf("%01$*2$.*3$d %4$d", 1, 2) - < E1503: Positional argument 3 out of bounds: %01$*2$.*3$d - %4$d - + echo printf("%1$0*2$.*3$d %4$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d + " %4$d + < *E1504* A positional argument can be used more than once: >vim echo printf("%1$s %2$s %1$s", "One", "Two") - < One Two One - + " One Two One + < However, you can't use a different type the second time: >vim echo printf("%1$s %2$s %1$d", "One", "Two") - < E1504: Positional argument 1 type used inconsistently: - int/string - + " E1504: Positional argument 1 type used inconsistently: + " int/string + < *E1505* Various other errors that lead to a format string being wrongly formatted lead to: >vim echo printf("%1$d at width %2$d is: %01$*2$.3$d", 1, 2) - < E1505: Invalid format specifier: %1$d at width %2$d is: - %01$*2$.3$d - + " E1505: Invalid format specifier: %1$d at width %2$d is: + " %01$*2$.3$d + < *E1507* This internal error indicates that the logic to parse a positional format argument ran into a problem that couldn't be diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0538ee2b22..31d4a2ecfd 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -968,6 +968,11 @@ static char *format_typename(const char *type) static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const char *type) FUNC_ATTR_NONNULL_ALL { + if (arg <= 0) { + semsg(_(e_invalid_format_specifier_str), type); + return FAIL; + } + if (*ap_types == NULL || *num_posarg < arg) { const char **new_types = *ap_types == NULL ? xcalloc((size_t)arg, sizeof(const char *)) diff --git a/test/old/testdir/test_format.vim b/test/old/testdir/test_format.vim index c2126f583c..585f152029 100644 --- a/test/old/testdir/test_format.vim +++ b/test/old/testdir/test_format.vim @@ -300,6 +300,8 @@ func Test_printf_pos_errors() call CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*0$s')"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%*0$s')"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%.123456789$d', 5)"], "E1510:") call CheckLegacyAndVim9Failure(["call printf('%.123456789d', 5)"], "E1510:")