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
>
>