mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
vim-patch:9.0.1856: issues with formatting positional arguments (#25013)
Problem: issues with formatting positional arguments
Solution: fix them, add tests and documentation
closes: vim/vim#12140
closes: vim/vim#12985
Tentatively fix message_test. Check NULL ptr.
aa90d4f031
Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
This commit is contained in:
parent
c50951a4d0
commit
c431d820e7
9
runtime/doc/builtin.txt
generated
9
runtime/doc/builtin.txt
generated
@ -5132,8 +5132,13 @@ printf({fmt}, {expr1} ...) *printf()*
|
||||
than the field width, the field is expanded to contain
|
||||
the conversion result.
|
||||
The 'h' modifier indicates the argument is 16 bits.
|
||||
The 'l' modifier indicates the argument is 32 bits.
|
||||
The 'L' modifier indicates the argument is 64 bits.
|
||||
The 'l' modifier indicates the argument is a long
|
||||
integer. The size will be 32 bits or 64 bits
|
||||
depending on your platform.
|
||||
The "ll" modifier indicates the argument is 64 bits.
|
||||
The b and B conversion specifiers never take a width
|
||||
modifier and always assume their argument is a 64 bit
|
||||
integer.
|
||||
Generally, these modifiers are not useful. They are
|
||||
ignored when type is known from the argument.
|
||||
|
||||
|
9
runtime/lua/vim/_meta/vimfn.lua
generated
9
runtime/lua/vim/_meta/vimfn.lua
generated
@ -6115,8 +6115,13 @@ function vim.fn.prevnonblank(lnum) end
|
||||
--- than the field width, the field is expanded to contain
|
||||
--- the conversion result.
|
||||
--- The 'h' modifier indicates the argument is 16 bits.
|
||||
--- The 'l' modifier indicates the argument is 32 bits.
|
||||
--- The 'L' modifier indicates the argument is 64 bits.
|
||||
--- The 'l' modifier indicates the argument is a long
|
||||
--- integer. The size will be 32 bits or 64 bits
|
||||
--- depending on your platform.
|
||||
--- The "ll" modifier indicates the argument is 64 bits.
|
||||
--- The b and B conversion specifiers never take a width
|
||||
--- modifier and always assume their argument is a 64 bit
|
||||
--- integer.
|
||||
--- Generally, these modifiers are not useful. They are
|
||||
--- ignored when type is known from the argument.
|
||||
---
|
||||
|
@ -7393,8 +7393,13 @@ M.funcs = {
|
||||
than the field width, the field is expanded to contain
|
||||
the conversion result.
|
||||
The 'h' modifier indicates the argument is 16 bits.
|
||||
The 'l' modifier indicates the argument is 32 bits.
|
||||
The 'L' modifier indicates the argument is 64 bits.
|
||||
The 'l' modifier indicates the argument is a long
|
||||
integer. The size will be 32 bits or 64 bits
|
||||
depending on your platform.
|
||||
The "ll" modifier indicates the argument is 64 bits.
|
||||
The b and B conversion specifiers never take a width
|
||||
modifier and always assume their argument is a 64 bit
|
||||
integer.
|
||||
Generally, these modifiers are not useful. They are
|
||||
ignored when type is known from the argument.
|
||||
|
||||
|
@ -32,33 +32,35 @@
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/vim.h"
|
||||
|
||||
static char e_cannot_mix_positional_and_non_positional_str[]
|
||||
static const char e_cannot_mix_positional_and_non_positional_str[]
|
||||
= N_("E1400: Cannot mix positional and non-positional arguments: %s");
|
||||
static char e_fmt_arg_nr_unused_str[]
|
||||
static const char e_fmt_arg_nr_unused_str[]
|
||||
= N_("E1401: format argument %d unused in $-style format: %s");
|
||||
static char e_positional_num_field_spec_reused_str_str[]
|
||||
static const char e_positional_num_field_spec_reused_str_str[]
|
||||
= N_("E1402: Positional argument %d used as field width reused as different type: %s/%s");
|
||||
static char e_positional_nr_out_of_bounds_str[]
|
||||
static const char e_positional_nr_out_of_bounds_str[]
|
||||
= N_("E1403: Positional argument %d out of bounds: %s");
|
||||
static char e_positional_arg_num_type_inconsistent_str_str[]
|
||||
static const char e_positional_arg_num_type_inconsistent_str_str[]
|
||||
= N_("E1404: Positional argument %d type used inconsistently: %s/%s");
|
||||
static char e_invalid_format_specifier_str[]
|
||||
static const char e_invalid_format_specifier_str[]
|
||||
= N_("E1405: Invalid format specifier: %s");
|
||||
static const char e_aptypes_is_null_str_nr[]
|
||||
= "E1408: Internal error: ap_types or ap_types[idx] is NULL: %s: %d";
|
||||
|
||||
static char typename_unknown[] = N_("unknown");
|
||||
static char typename_int[] = N_("int");
|
||||
static char typename_longint[] = N_("long int");
|
||||
static char typename_longlongint[] = N_("long long int");
|
||||
static char typename_signedsizet[] = N_("signed size_t");
|
||||
static char typename_unsignedint[] = N_("unsigned int");
|
||||
static char typename_unsignedlongint[] = N_("unsigned long int");
|
||||
static char typename_unsignedlonglongint[] = N_("unsigned long long int");
|
||||
static char typename_sizet[] = N_("size_t");
|
||||
static char typename_pointer[] = N_("pointer");
|
||||
static char typename_percent[] = N_("percent");
|
||||
static char typename_char[] = N_("char");
|
||||
static char typename_string[] = N_("string");
|
||||
static char typename_float[] = N_("float");
|
||||
static const char typename_unknown[] = N_("unknown");
|
||||
static const char typename_int[] = N_("int");
|
||||
static const char typename_longint[] = N_("long int");
|
||||
static const char typename_longlongint[] = N_("long long int");
|
||||
static const char typename_signedsizet[] = N_("signed size_t");
|
||||
static const char typename_unsignedint[] = N_("unsigned int");
|
||||
static const char typename_unsignedlongint[] = N_("unsigned long int");
|
||||
static const char typename_unsignedlonglongint[] = N_("unsigned long long int");
|
||||
static const char typename_sizet[] = N_("size_t");
|
||||
static const char typename_pointer[] = N_("pointer");
|
||||
static const char typename_percent[] = N_("percent");
|
||||
static const char typename_char[] = N_("char");
|
||||
static const char typename_string[] = N_("string");
|
||||
static const char typename_float[] = N_("float");
|
||||
|
||||
/// Copy up to `len` bytes of `string` into newly allocated memory and
|
||||
/// terminate with a NUL. The allocated memory always has size `len + 1`, even
|
||||
@ -763,7 +765,7 @@ enum {
|
||||
};
|
||||
|
||||
/// Types that can be used in a format string
|
||||
static int format_typeof(const char *type, bool usetvs)
|
||||
static int format_typeof(const char *type)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// allowed values: \0, h, l, L
|
||||
@ -800,19 +802,6 @@ static int format_typeof(const char *type, bool usetvs)
|
||||
break;
|
||||
}
|
||||
|
||||
if (usetvs) {
|
||||
switch (fmt_spec) {
|
||||
case 'd':
|
||||
case 'u':
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (length_modifier == '\0') {
|
||||
length_modifier = 'L';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get parameter value, do initial processing
|
||||
switch (fmt_spec) {
|
||||
// '%' and 'c' behave similar to 's' regarding flags and field
|
||||
@ -847,7 +836,7 @@ static int format_typeof(const char *type, bool usetvs)
|
||||
if (fmt_spec == 'p') {
|
||||
return TYPE_POINTER;
|
||||
} else if (fmt_spec == 'b' || fmt_spec == 'B') {
|
||||
return TYPE_UNSIGNEDINT;
|
||||
return TYPE_UNSIGNEDLONGLONGINT;
|
||||
} else if (fmt_spec == 'd') {
|
||||
// signed
|
||||
switch (length_modifier) {
|
||||
@ -893,7 +882,7 @@ static int format_typeof(const char *type, bool usetvs)
|
||||
static char *format_typename(const char *type)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
switch (format_typeof(type, false)) {
|
||||
switch (format_typeof(type)) {
|
||||
case TYPE_INT:
|
||||
return _(typename_int);
|
||||
case TYPE_LONGINT:
|
||||
@ -960,7 +949,7 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (format_typeof(type, false) != format_typeof((*ap_types)[arg - 1], false)) {
|
||||
if (format_typeof(type) != format_typeof((*ap_types)[arg - 1])) {
|
||||
semsg(_(e_positional_arg_num_type_inconsistent_str_str), arg,
|
||||
format_typename(type), format_typename((*ap_types)[arg - 1]));
|
||||
return FAIL;
|
||||
@ -1239,7 +1228,7 @@ error:
|
||||
}
|
||||
|
||||
static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, int *arg_idx,
|
||||
int *arg_cur)
|
||||
int *arg_cur, const char *fmt)
|
||||
FUNC_ATTR_NONNULL_ARG(3, 4, 5)
|
||||
{
|
||||
int arg_min = 0;
|
||||
@ -1260,10 +1249,14 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in
|
||||
}
|
||||
|
||||
for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) {
|
||||
assert(ap_types != NULL);
|
||||
if (ap_types == NULL || ap_types[*arg_cur] == NULL) {
|
||||
semsg(e_aptypes_is_null_str_nr, fmt, *arg_cur);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *p = ap_types[*arg_cur];
|
||||
|
||||
int fmt_type = format_typeof(p, true);
|
||||
int fmt_type = format_typeof(p);
|
||||
|
||||
// get parameter value, do initial processing
|
||||
switch (fmt_type) {
|
||||
@ -1477,7 +1470,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
|
||||
const int j = (tvs
|
||||
? (int)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
|
||||
if (j >= 0) {
|
||||
@ -1528,7 +1522,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
|
||||
const int j = (tvs
|
||||
? (int)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
|
||||
if (j >= 0) {
|
||||
@ -1600,7 +1595,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
case 'c': {
|
||||
const int j = (tvs
|
||||
? (int)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
|
||||
// standard demands unsigned char
|
||||
@ -1613,7 +1609,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
case 'S':
|
||||
str_arg = (tvs
|
||||
? tv_str(tvs, &arg_idx, &tofree)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, const char *)));
|
||||
|
||||
if (!str_arg) {
|
||||
@ -1684,7 +1681,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
if (fmt_spec == 'p') {
|
||||
ptr_arg = (tvs
|
||||
? tv_ptr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, void *)));
|
||||
|
||||
if (ptr_arg) {
|
||||
@ -1696,7 +1694,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
case '\0':
|
||||
arg = (tvs
|
||||
? (int)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
break;
|
||||
case 'h':
|
||||
@ -1704,25 +1703,29 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
arg = (int16_t)
|
||||
(tvs
|
||||
? (int)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
break;
|
||||
case 'l':
|
||||
arg = (tvs
|
||||
? (long)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, long)));
|
||||
break;
|
||||
case 'L':
|
||||
arg = (tvs
|
||||
? (long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, long long))); // NOLINT(runtime/int)
|
||||
break;
|
||||
case 'z': // implementation-defined, usually ptrdiff_t
|
||||
arg = (tvs
|
||||
? (ptrdiff_t)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, ptrdiff_t)));
|
||||
break;
|
||||
}
|
||||
@ -1737,32 +1740,37 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
case '\0':
|
||||
uarg = (tvs
|
||||
? (unsigned)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, unsigned)));
|
||||
break;
|
||||
case 'h':
|
||||
uarg = (uint16_t)
|
||||
(tvs
|
||||
? (unsigned)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, unsigned)));
|
||||
break;
|
||||
case 'l':
|
||||
uarg = (tvs
|
||||
? (unsigned long)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, unsigned long)));
|
||||
break;
|
||||
case 'L':
|
||||
uarg = (tvs
|
||||
? (unsigned long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, unsigned long long))); // NOLINT(runtime/int)
|
||||
break;
|
||||
case 'z':
|
||||
uarg = (tvs
|
||||
? (size_t)tv_nr(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, size_t)));
|
||||
break;
|
||||
}
|
||||
@ -1900,7 +1908,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
|
||||
double f = (tvs
|
||||
? tv_float(tvs, &arg_idx)
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, double)));
|
||||
|
||||
double abs_f = f < 0 ? -f : f;
|
||||
|
@ -208,6 +208,7 @@ describe('vim_snprintf()', function()
|
||||
a('three one two', buf, bsize, '%3$s %1$s %2$s', 'one', 'two', 'three')
|
||||
a('1234567', buf, bsize, '%1$d', i(1234567))
|
||||
a('deadbeef', buf, bsize, '%1$x', u(0xdeadbeef))
|
||||
a('001100', buf, bsize, '%2$0*1$b', i(6), u(12))
|
||||
a('001100', buf, bsize, '%1$0.*2$b', u(12), i(6))
|
||||
a('one two', buf, bsize, '%1$s %2$s', 'one', 'two')
|
||||
a('001100', buf, bsize, '%06b', u(12))
|
||||
|
Loading…
Reference in New Issue
Block a user