[clang] [clang] Suggest using __VA_OPT__(,) instead of GNU zero variadic macro argument (PR #188624)

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 26 05:56:15 PDT 2026


https://github.com/serge-sans-paille updated https://github.com/llvm/llvm-project/pull/188624

>From 174b065d76f254b59ffc6ae5b0080592d42c457f Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Wed, 25 Mar 2026 22:52:31 +0100
Subject: [PATCH 1/5] [clang] Suggest using __VA_OPT__(,) instead of GNU zero
 variadic macro argument

Also provide an appropriate fixit.
---
 clang/include/clang/Basic/DiagnosticLexKinds.td       |  3 ++-
 clang/lib/Lex/TokenLexer.cpp                          | 11 ++++++++++-
 .../Lexer/gnu-zero-variadic-macro-argument-fixit.c    |  9 +++++++++
 3 files changed, 21 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 5eceeced311f2..99a3109baa01c 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -769,7 +769,8 @@ def err_paste_at_start : Error<
   "'##' cannot appear at start of macro expansion">;
 def err_paste_at_end : Error<"'##' cannot appear at end of macro expansion">;
 def ext_paste_comma : Extension<
-  "token pasting of ',' and __VA_ARGS__ is a GNU extension">, InGroup<GNUZeroVariadicMacroArguments>;
+  "token pasting of ',' and __VA_ARGS__ is a GNU extension.%select{| Consider using __VA_OPT__(,) instead}0">,
+  InGroup<GNUZeroVariadicMacroArguments>;
 def err_unterm_macro_invoc : Error<
   "unterminated function-like macro invocation">;
 def err_too_many_args_in_macro_invoc : Error<
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index db4313f766812..699b3a6a6c7d8 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -523,7 +523,16 @@ void TokenLexer::ExpandFunctionArguments() {
           Macro->isVariadic()) {
         VaArgsPseudoPaste = true;
         // Remove the paste operator, report use of the extension.
-        PP.Diag(ResultToks.pop_back_val().getLocation(), diag::ext_paste_comma);
+        const bool hint = PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus20;
+        auto diag = PP.Diag(ResultToks.pop_back_val().getLocation(),
+                            diag::ext_paste_comma)
+                    << hint;
+        if (hint) {
+          diag << FixItHint::CreateReplacement(
+              SourceRange(ResultToks[ResultToks.size() - 1].getLocation(),
+                          CurTok.getLocation()),
+              " __VA_OPT__(,) __VA_ARGS__");
+        }
       }
 
       ResultToks.append(ArgToks, ArgToks+NumToks);
diff --git a/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
new file mode 100644
index 0000000000000..e796ed001c0be
--- /dev/null
+++ b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fdiagnostics-parseable-fixits %s -Wgnu-zero-variadic-macro-arguments -std=c23
+
+void foo(const char* fmt, ...);
+// expected-warning at +1 {{token pasting of ',' and __VA_ARGS__ is a GNU extension. Consider using __VA_OPT__(,) instead}}
+#define FOO(format, ...) foo(format, ##__VA_ARGS__)
+
+void bar(void) {
+  FOO("", 0);
+}

>From 7a94b696fab7e349cc93dcaa70f3ef35ec869a2a Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Wed, 25 Mar 2026 23:19:47 +0100
Subject: [PATCH 2/5] fixup! [clang] Suggest using __VA_OPT__(,) instead of GNU
 zero variadic macro argument

---
 clang/lib/Lex/TokenLexer.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 699b3a6a6c7d8..9550c7cb722d6 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -523,12 +523,13 @@ void TokenLexer::ExpandFunctionArguments() {
           Macro->isVariadic()) {
         VaArgsPseudoPaste = true;
         // Remove the paste operator, report use of the extension.
-        const bool hint = PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus20;
-        auto diag = PP.Diag(ResultToks.pop_back_val().getLocation(),
+        const unsigned hint =
+            (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus20) ? 1u : 0u;
+        auto Diag = PP.Diag(ResultToks.pop_back_val().getLocation(),
                             diag::ext_paste_comma)
                     << hint;
         if (hint) {
-          diag << FixItHint::CreateReplacement(
+          Diag << FixItHint::CreateReplacement(
               SourceRange(ResultToks[ResultToks.size() - 1].getLocation(),
                           CurTok.getLocation()),
               " __VA_OPT__(,) __VA_ARGS__");

>From 79a3f97666871c764bc3ce5914df6710b6c4569c Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Thu, 26 Mar 2026 01:11:15 +0100
Subject: [PATCH 3/5] fixup! fixup! [clang] Suggest using __VA_OPT__(,) instead
 of GNU zero variadic macro argument

---
 clang/include/clang/Basic/DiagnosticLexKinds.td           | 2 +-
 clang/lib/Lex/TokenLexer.cpp                              | 6 +++---
 clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c | 6 ++++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 99a3109baa01c..8ff5780651e64 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -769,7 +769,7 @@ def err_paste_at_start : Error<
   "'##' cannot appear at start of macro expansion">;
 def err_paste_at_end : Error<"'##' cannot appear at end of macro expansion">;
 def ext_paste_comma : Extension<
-  "token pasting of ',' and __VA_ARGS__ is a GNU extension.%select{| Consider using __VA_OPT__(,) instead}0">,
+  "token pasting of ',' and __VA_ARGS__ is a GNU extension%select{|; consider using __VA_OPT__(,) instead}0">,
   InGroup<GNUZeroVariadicMacroArguments>;
 def err_unterm_macro_invoc : Error<
   "unterminated function-like macro invocation">;
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 9550c7cb722d6..42bc08758651f 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -523,12 +523,12 @@ void TokenLexer::ExpandFunctionArguments() {
           Macro->isVariadic()) {
         VaArgsPseudoPaste = true;
         // Remove the paste operator, report use of the extension.
-        const unsigned hint =
+        const unsigned Hint =
             (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus20) ? 1u : 0u;
         auto Diag = PP.Diag(ResultToks.pop_back_val().getLocation(),
                             diag::ext_paste_comma)
-                    << hint;
-        if (hint) {
+                    << Hint;
+        if (Hint) {
           Diag << FixItHint::CreateReplacement(
               SourceRange(ResultToks[ResultToks.size() - 1].getLocation(),
                           CurTok.getLocation()),
diff --git a/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
index e796ed001c0be..8a24ba737409a 100644
--- a/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
+++ b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
@@ -1,7 +1,9 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -fdiagnostics-parseable-fixits %s -Wgnu-zero-variadic-macro-arguments -std=c23
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wgnu-zero-variadic-macro-arguments -std=c23
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s -Wgnu-zero-variadic-macro-arguments -std=c23 2>&1 | FileCheck %s
 
 void foo(const char* fmt, ...);
-// expected-warning at +1 {{token pasting of ',' and __VA_ARGS__ is a GNU extension. Consider using __VA_OPT__(,) instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:36-[[@LINE+2]]:51}:" __VA_OPT__(,) __VA_ARGS__"
+// expected-warning at +1 {{token pasting of ',' and __VA_ARGS__ is a GNU extension; consider using __VA_OPT__(,) instead}}
 #define FOO(format, ...) foo(format, ##__VA_ARGS__)
 
 void bar(void) {

>From 472e0350e0d478fd68f57f47f3623490e9f778c7 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Thu, 26 Mar 2026 01:43:12 +0100
Subject: [PATCH 4/5] fixup! fixup! fixup! [clang] Suggest using __VA_OPT__(,)
 instead of GNU zero variadic macro argument

---
 clang/docs/ReleaseNotes.rst                               | 3 +++
 clang/lib/Lex/TokenLexer.cpp                              | 2 +-
 clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c | 2 ++
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0dbe667e4f07a..52cbdd5c6dca9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -323,6 +323,9 @@ Improvements to Clang's diagnostics
   ``-Wunused-private-field`` no longer emits a warning for annotated private
   fields.
 
+- Improved ``-Wgnu-zero-variadic-macro-arguments`` to suggest using
+  ``__VA_OPT__`` if the current language version supports it(#GH188624)
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 42bc08758651f..ade97bbbe74c2 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -530,7 +530,7 @@ void TokenLexer::ExpandFunctionArguments() {
                     << Hint;
         if (Hint) {
           Diag << FixItHint::CreateReplacement(
-              SourceRange(ResultToks[ResultToks.size() - 1].getLocation(),
+              SourceRange(ResultToks.back().getLocation(),
                           CurTok.getLocation()),
               " __VA_OPT__(,) __VA_ARGS__");
         }
diff --git a/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
index 8a24ba737409a..60e0774f0629e 100644
--- a/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
+++ b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -Wgnu-zero-variadic-macro-arguments -std=c23
 // RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s -Wgnu-zero-variadic-macro-arguments -std=c23 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wgnu-zero-variadic-macro-arguments -xc++ -std=c++20
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s -Wgnu-zero-variadic-macro-arguments -xc++ -std=c++20 2>&1 | FileCheck %s
 
 void foo(const char* fmt, ...);
 // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:36-[[@LINE+2]]:51}:" __VA_OPT__(,) __VA_ARGS__"

>From 6422bef072620e85b39e27a186439b04d69f8893 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Thu, 26 Mar 2026 13:55:32 +0100
Subject: [PATCH 5/5] fixup! fixup! fixup! fixup! [clang] Suggest using
 __VA_OPT__(,) instead of GNU zero variadic macro argument

---
 clang/lib/Lex/TokenLexer.cpp | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index ade97bbbe74c2..9acc674f1faf3 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -137,6 +137,10 @@ void TokenLexer::destroy() {
   if (ActualArgs) ActualArgs->destroy(PP);
 }
 
+static bool hasVaOptSupport(const LangOptions &LangOpts) {
+  return LangOpts.C23 || LangOpts.CPlusPlus20;
+}
+
 bool TokenLexer::MaybeRemoveCommaBeforeVaArgs(
     SmallVectorImpl<Token> &ResultToks, bool HasPasteOperator, MacroInfo *Macro,
     unsigned MacroArgNo, Preprocessor &PP) {
@@ -164,8 +168,11 @@ bool TokenLexer::MaybeRemoveCommaBeforeVaArgs(
     return false;
 
   // Issue an extension diagnostic for the paste operator.
-  if (HasPasteOperator)
-    PP.Diag(ResultToks.back().getLocation(), diag::ext_paste_comma);
+  if (HasPasteOperator) {
+    const bool VaOptSupport = hasVaOptSupport(PP.getLangOpts());
+    PP.Diag(ResultToks.back().getLocation(), diag::ext_paste_comma)
+        << VaOptSupport;
+  }
 
   // Remove the comma.
   ResultToks.pop_back();
@@ -523,12 +530,11 @@ void TokenLexer::ExpandFunctionArguments() {
           Macro->isVariadic()) {
         VaArgsPseudoPaste = true;
         // Remove the paste operator, report use of the extension.
-        const unsigned Hint =
-            (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus20) ? 1u : 0u;
+        const bool VaOptSupport = hasVaOptSupport(PP.getLangOpts());
         auto Diag = PP.Diag(ResultToks.pop_back_val().getLocation(),
                             diag::ext_paste_comma)
-                    << Hint;
-        if (Hint) {
+                    << VaOptSupport;
+        if (VaOptSupport) {
           Diag << FixItHint::CreateReplacement(
               SourceRange(ResultToks.back().getLocation(),
                           CurTok.getLocation()),



More information about the cfe-commits mailing list