diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cd7f280fbb6d..98bd0d9fa807 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -350,6 +350,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/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 5eceeced311f..bea0aafac98c 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -769,7 +769,9 @@ 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; + "token pasting of ',' and '__VA_ARGS__' is a GNU extension%select{|; " + "consider using '__VA_OPT__(,)' instead}0">, + InGroup; 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 db4313f76681..9b18c31e19eb 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 &ResultToks, bool HasPasteOperator, MacroInfo *Macro, unsigned MacroArgNo, Preprocessor &PP) { @@ -164,8 +168,10 @@ 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) { + PP.Diag(ResultToks.back().getLocation(), diag::ext_paste_comma) + << hasVaOptSupport(PP.getLangOpts()); + } // Remove the comma. ResultToks.pop_back(); @@ -523,7 +529,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); + bool VaOptSupport = hasVaOptSupport(PP.getLangOpts()); + auto Diag = PP.Diag(ResultToks.pop_back_val().getLocation(), + diag::ext_paste_comma) + << VaOptSupport; + if (VaOptSupport) { + Diag << FixItHint::CreateReplacement( + SourceRange(ResultToks.back().getLocation(), + CurTok.getLocation()), + " __VA_OPT__(,) __VA_ARGS__"); + } } ResultToks.append(ArgToks, ArgToks+NumToks); diff --git a/clang/test/Lexer/gnu-flags.c b/clang/test/Lexer/gnu-flags.c index 30cfcf710f34..ad9646bb568e 100644 --- a/clang/test/Lexer/gnu-flags.c +++ b/clang/test/Lexer/gnu-flags.c @@ -18,7 +18,7 @@ #if ALL || ZEROARGS // expected-warning@+9 {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} // expected-note@+4 {{macro 'efoo' defined here}} -// expected-warning@+3 {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} +// expected-warning@+3 {{token pasting of ',' and '__VA_ARGS__' is a GNU extension}} #endif #define efoo(format, args...) foo(format , ##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 new file mode 100644 index 000000000000..32d1fd379709 --- /dev/null +++ b/clang/test/Lexer/gnu-zero-variadic-macro-argument-fixit.c @@ -0,0 +1,13 @@ +// 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__" +// expected-warning@+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); +} diff --git a/clang/test/Preprocessor/macro_fn.c b/clang/test/Preprocessor/macro_fn.c index 2e72bd272084..48fd26daebfa 100644 --- a/clang/test/Preprocessor/macro_fn.c +++ b/clang/test/Preprocessor/macro_fn.c @@ -60,7 +60,7 @@ one_dot() /* empty first argument, elided ... */ SomeComplicatedStuff((desc), ##__VA_ARGS__) #ifndef VARIADIC_MACRO_ARGS_REMOVE_COMMA -/* expected-warning@-3 {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} */ +/* expected-warning@-3 {{token pasting of ',' and '__VA_ARGS__' is a GNU extension}} */ #endif NSAssert(somecond, somedesc)