Re: [PATCH] sparse: add support for __VA_OPT__
Chris Li <[email protected]> Tue, 24 Feb 2026 18:39:57 -0800
| Newsgroups | org.kernel.vger.linux-sparse |
|---|---|
| Message-ID | <CACePvbUjNQUZWcTK-59p09RJAjZZs+z8HuYmkfeTXWBsOKBEfw@mail.gmail.com> |
Hi Dan,
Thanks for the patch.
BTW, can you CC my personal email next time? Thanks.
On Tue, Feb 24, 2026 at 3:12=E2=80=AFAM Dan Carpenter <dan.carpenter@linaro=
.org> wrote:
>
> The linux kernel has started using __VA_OPT__ so lets add support for it.
>
> What it does is it adds an optional thing, normally a comma, if the
> __VA_ARGS__ parameter is not empty. So if you have at least one argument
> but possibly more then you could create a macro like:
>
> #define test_args(a, ...) printf(a __VA_OPT__(,) __VA_ARGS__)
>
> If you call test_args("foo\n") it expands to:
>
> printf("foo\n");
>
> but if you pass two arguments test_args("foo %d\n", 2) it expands to:
>
> printf("foo\n" , 2);
>
> I guess if you wanted instead of a comma then you could have an empty
> __VA_OPT__() or you could pass random things like __VA_OPT__(a b c). I
> don't know why you would do that. In this case, "a" is a macro argument
> so that has to be expanded out.
>
> Signed-off-by: Dan Carpenter <[email protected]>
> ---
> ident-list.h | 1 +
> pre-process.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 53 insertions(+)
>
> diff --git a/ident-list.h b/ident-list.h
> index d65668108385..bffc4038a75d 100644
> --- a/ident-list.h
> +++ b/ident-list.h
> @@ -66,6 +66,7 @@ IDENT(c_static_assert);
> __IDENT(pragma_ident, "__pragma__", 0);
> __IDENT(_Pragma_ident, "_Pragma", 0);
> __IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0);
> +__IDENT(__VA_OPT___ident, "__VA_OPT__", 0);
> __IDENT(__func___ident, "__func__", 0);
> __IDENT(__FUNCTION___ident, "__FUNCTION__", 0);
> __IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0);
> diff --git a/pre-process.c b/pre-process.c
> index 05a5a79396a8..94710301ce17 100644
> --- a/pre-process.c
> +++ b/pre-process.c
> @@ -643,11 +643,49 @@ static int handle_kludge(struct token **p, struct a=
rg *args)
> }
> }
>
> +static struct token *get_VA_OPT(struct token **p, struct arg *args)
> +{
> + struct token *t =3D (*p)->next;
> + const char *expected;
> + struct token *ret =3D NULL;
> + struct token *dup, *tail;
> +
> + if (token_type(t) !=3D TOKEN_SPECIAL || t->special !=3D '(') {
> + expected =3D "(";
> + goto error;
> + }
> + while (true) {
> + t =3D t->next;
> + if (eof_token(t)) {
> + expected =3D ")";
> + goto error;
> + }
> + if (token_type(t) =3D=3D TOKEN_SPECIAL &&
> + t->special =3D=3D ')')
> + break;
I think this approach has some limitations. You can use the
collect_arguments() to collect and expand the arguments for
__VA_OPT__().
I did a small test case to show that gcc actually expand the arguments
inside the __VA_OPT__().
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D terminal =3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D
$ cat ~/tmp/va_opt.c
#define A(B) ,
#define foo(fmt, ...) printk(fmt __VA_OPT__(A(x)) __VA_ARGS__)
foo("\n");
foo("\n", "a");
$ gcc -E ~/tmp/va_opt.c
# 0 "/home/chrisl/tmp/va_opt.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "/home/chrisl/tmp/va_opt.c"
printk("\n" );
printk("\n" , "a");
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D terminal =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
That shows that gcc correctly expand the A() macro inside __VA_OPT__(A(x)).
I have not test it on your program yet, reading your patch I assume it
will not expand the A() here.
One idea is to add an expand function expand___VA_OPT__, similar to
expand_has_feature(). Register it in the dynamic expand macro array.
That way you get the collect_arguments() for free and it behaves just
like a builtin macro expansion. Just duplicate the collected arg list
to the current token, if there are extra arguments.
If you go that route, likely don't need the manual __VA_OPT__ident parsing.
I haven't had time to code it myself yet; it's just an idea.
Chris
> + dup =3D dup_token(t, &(*p)->pos);
> + if (!ret)
> + ret =3D dup;
> + else
> + tail->next =3D dup;
> + tail =3D dup;
> + }
> +
> + if (tail)
> + tail->next =3D &eof_token_entry;
> + *p =3D t;
> + return ret;
> +error:
> + sparse_error(t->pos, "__VA_OPT__ error: expected '%s'", expected)=
;
> + return NULL;
> +}
> +
> static struct token **substitute(struct token **list, struct token *body=
, struct arg *args)
> {
> struct position *base_pos =3D &(*list)->pos;
> int *count;
> enum {Normal, Placeholder, Concat} state =3D Normal;
> + struct token *va_opt =3D NULL;
>
> for (; !eof_token(body); body =3D body->next) {
> struct token *added, *arg;
> @@ -697,6 +735,10 @@ static struct token **substitute(struct token **list=
, struct token *body, struct
>
> case TOKEN_MACRO_ARGUMENT:
> arg =3D args[body->argnum].expanded;
> + if (va_opt && !eof_token(arg)) {
> + list =3D substitute(list, va_opt, args);
> + va_opt =3D NULL;
> + }
> count =3D &args[body->argnum].n_normal;
> if (eof_token(arg)) {
> state =3D Normal;
> @@ -716,6 +758,11 @@ static struct token **substitute(struct token **list=
, struct token *body, struct
> continue;
>
> case TOKEN_IDENT:
> + if (body->ident =3D=3D &__VA_OPT___ident) {
> + va_opt =3D get_VA_OPT(&body, args);
> + continue;
> + }
> +
> added =3D dup_token(body, base_pos);
> if (added->ident->tainted)
> added->pos.noexpand =3D 1;
> @@ -1234,6 +1281,8 @@ static struct token *parse_arguments(struct token *=
list)
> while (token_type(arg) =3D=3D TOKEN_IDENT) {
> if (arg->ident =3D=3D &__VA_ARGS___ident)
> goto Eva_args;
> + if (arg->ident =3D=3D &__VA_OPT___ident)
> + goto Eva_opt;
> if (!++count->normal)
> goto Eargs;
> next =3D arg->next;
> @@ -1312,6 +1361,9 @@ Enotclosed:
> Eva_args:
> sparse_error(arg->pos, "__VA_ARGS__ can only appear in the expans=
ion of a C99 variadic macro");
> return NULL;
> +Eva_opt:
> + sparse_error(arg->pos, "__VA_OPT__ can only appear in the expansi=
on of a C99 variadic macro");
> + return NULL;
> Eargs:
> sparse_error(arg->pos, "too many arguments in macro definition");
> return NULL;
> --
> 2.51.0
>
>