mirror of https://github.com/microsoft/clang.git
Improved support for removing the comma preceding __VA_ARGS__ where __VA_ARGS__
is empty in a variadic macro expansion. This fixes a divergence in support for the ", ## __VA_ARGS__" GCC extension which differed in behaviour when in strict C99 mode (note: there is no change in behaviour has been made in the gnu99 mode that clang uses by default). In addition, there is improved support for the Microsoft alternative extension ", __VA_ARGS__". git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167613 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
1a52a4d36c
commit
02f0022dfc
|
@ -119,6 +119,55 @@ void TokenLexer::destroy() {
|
|||
if (ActualArgs) ActualArgs->destroy(PP);
|
||||
}
|
||||
|
||||
/// Remove comma ahead of __VA_ARGS__, if present, according to compiler dialect
|
||||
/// settings. Returns true if the comma is removed.
|
||||
static bool MaybeRemoveCommaBeforeVaArgs(SmallVector<Token, 128> &ResultToks,
|
||||
bool &NextTokGetsSpace,
|
||||
bool HasPasteOperator,
|
||||
MacroInfo *Macro, unsigned MacroArgNo,
|
||||
Preprocessor &PP) {
|
||||
// Is the macro argument __VA_ARGS__?
|
||||
if (!Macro->isVariadic() || MacroArgNo != Macro->getNumArgs()-1)
|
||||
return false;
|
||||
|
||||
// In Microsoft-compatibility mode, a comma is removed in the expansion
|
||||
// of " ... , __VA_ARGS__ " if __VA_ARGS__ is empty. This extension is
|
||||
// not supported by gcc.
|
||||
if (!HasPasteOperator && !PP.getLangOpts().MicrosoftMode)
|
||||
return false;
|
||||
|
||||
// GCC removes the comma in the expansion of " ... , ## __VA_ARGS__ " if
|
||||
// __VA_ARGS__ is empty, but not in strict C99 mode where there are no
|
||||
// named arguments, where it remains. In all other modes, including C99
|
||||
// with GNU extensions, it is removed regardless of named arguments.
|
||||
// Microsoft also appears to support this extension, unofficially.
|
||||
if (PP.getLangOpts().C99 && !PP.getLangOpts().GNUMode
|
||||
&& Macro->getNumArgs() < 2)
|
||||
return false;
|
||||
|
||||
// Is a comma available to be removed?
|
||||
if (ResultToks.empty() || !ResultToks.back().is(tok::comma))
|
||||
return false;
|
||||
|
||||
// Issue an extension diagnostic for the paste operator.
|
||||
if (HasPasteOperator)
|
||||
PP.Diag(ResultToks.back().getLocation(), diag::ext_paste_comma);
|
||||
|
||||
// Remove the comma.
|
||||
ResultToks.pop_back();
|
||||
|
||||
// If the comma was right after another paste (e.g. "X##,##__VA_ARGS__"),
|
||||
// then removal of the comma should produce a placemarker token (in C99
|
||||
// terms) which we model by popping off the previous ##, giving us a plain
|
||||
// "X" when __VA_ARGS__ is empty.
|
||||
if (!ResultToks.empty() && ResultToks.back().is(tok::hashhash))
|
||||
ResultToks.pop_back();
|
||||
|
||||
// Never add a space, even if the comma, ##, or arg had a space.
|
||||
NextTokGetsSpace = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Expand the arguments of a function-like macro so that we can quickly
|
||||
/// return preexpanded tokens from Tokens.
|
||||
void TokenLexer::ExpandFunctionArguments() {
|
||||
|
@ -199,6 +248,14 @@ void TokenLexer::ExpandFunctionArguments() {
|
|||
!ResultToks.empty() && ResultToks.back().is(tok::hashhash);
|
||||
bool PasteAfter = i+1 != e && Tokens[i+1].is(tok::hashhash);
|
||||
|
||||
// In Microsoft mode, remove the comma before __VA_ARGS__ to ensure there
|
||||
// are no trailing commas if __VA_ARGS__ is empty.
|
||||
if (!PasteBefore && ActualArgs->isVarargsElidedUse() &&
|
||||
MaybeRemoveCommaBeforeVaArgs(ResultToks, NextTokGetsSpace,
|
||||
/*HasPasteOperator=*/false,
|
||||
Macro, ArgNo, PP))
|
||||
continue;
|
||||
|
||||
// If it is not the LHS/RHS of a ## operator, we must pre-expand the
|
||||
// argument and substitute the expanded tokens into the result. This is
|
||||
// C99 6.10.3.1p1.
|
||||
|
@ -321,23 +378,13 @@ void TokenLexer::ExpandFunctionArguments() {
|
|||
|
||||
// If this is the __VA_ARGS__ token, and if the argument wasn't provided,
|
||||
// and if the macro had at least one real argument, and if the token before
|
||||
// the ## was a comma, remove the comma.
|
||||
if ((unsigned)ArgNo == Macro->getNumArgs()-1 && // is __VA_ARGS__
|
||||
ActualArgs->isVarargsElidedUse() && // Argument elided.
|
||||
!ResultToks.empty() && ResultToks.back().is(tok::comma)) {
|
||||
// Never add a space, even if the comma, ##, or arg had a space.
|
||||
NextTokGetsSpace = false;
|
||||
// Remove the paste operator, report use of the extension.
|
||||
PP.Diag(ResultToks.back().getLocation(), diag::ext_paste_comma);
|
||||
ResultToks.pop_back();
|
||||
|
||||
// If the comma was right after another paste (e.g. "X##,##__VA_ARGS__"),
|
||||
// then removal of the comma should produce a placemarker token (in C99
|
||||
// terms) which we model by popping off the previous ##, giving us a plain
|
||||
// "X" when __VA_ARGS__ is empty.
|
||||
if (!ResultToks.empty() && ResultToks.back().is(tok::hashhash))
|
||||
ResultToks.pop_back();
|
||||
}
|
||||
// the ## was a comma, remove the comma. This is a GCC extension which is
|
||||
// disabled when using -std=c99.
|
||||
if (ActualArgs->isVarargsElidedUse())
|
||||
MaybeRemoveCommaBeforeVaArgs(ResultToks, NextTokGetsSpace,
|
||||
/*HasPasteOperator=*/true,
|
||||
Macro, ArgNo, PP);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// Test the __VA_ARGS__ comma swallowing extensions of various compiler dialects.
|
||||
|
||||
// RUN: %clang_cc1 -E %s | FileCheck -check-prefix=GCC -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -std=c99 %s | FileCheck -check-prefix=C99 -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -std=c11 %s | FileCheck -check-prefix=C99 -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -x c++ %s | FileCheck -check-prefix=GCC -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -std=gnu99 %s | FileCheck -check-prefix=GCC -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -fms-compatibility %s | FileCheck -check-prefix=MS -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -DNAMED %s | FileCheck -check-prefix=GCC -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -E -std=c99 -DNAMED %s | FileCheck -check-prefix=C99 -strict-whitespace %s
|
||||
|
||||
|
||||
#ifndef NAMED
|
||||
# define A(...) [ __VA_ARGS__ ]
|
||||
# define B(...) [ , __VA_ARGS__ ]
|
||||
# define C(...) [ , ## __VA_ARGS__ ]
|
||||
# define D(A,...) [ A , ## __VA_ARGS__ ]
|
||||
# define E(A,...) [ __VA_ARGS__ ## A ]
|
||||
#else
|
||||
// These are the GCC named argument versions of the C99-style variadic macros.
|
||||
// Note that __VA_ARGS__ *may* be used as the name, this is not prohibited!
|
||||
# define A(__VA_ARGS__...) [ __VA_ARGS__ ]
|
||||
# define B(__VA_ARGS__...) [ , __VA_ARGS__ ]
|
||||
# define C(__VA_ARGS__...) [ , ## __VA_ARGS__ ]
|
||||
# define D(A,__VA_ARGS__...) [ A , ## __VA_ARGS__ ]
|
||||
# define E(A,__VA_ARGS__...) [ __VA_ARGS__ ## A ]
|
||||
#endif
|
||||
|
||||
|
||||
1: A() B() C() D() E()
|
||||
2: A(a) B(a) C(a) D(a) E(a)
|
||||
3: A(,) B(,) C(,) D(,) E(,)
|
||||
4: A(a,b,c) B(a,b,c) C(a,b,c) D(a,b,c) E(a,b,c)
|
||||
5: A(a,b,) B(a,b,) C(a,b,) D(a,b,)
|
||||
|
||||
// The GCC ", ## __VA_ARGS__" extension swallows the comma when followed by
|
||||
// empty __VA_ARGS__. This extension does not apply in -std=c99 mode, but
|
||||
// does apply in C++.
|
||||
//
|
||||
// GCC: 1: [ ] [ , ] [ ] [ ] [ ]
|
||||
// GCC: 2: [ a ] [ , a ] [ ,a ] [ a ] [ a ]
|
||||
// GCC: 3: [ , ] [ , , ] [ ,, ] [ , ] [ ]
|
||||
// GCC: 4: [ a,b,c ] [ , a,b,c ] [ ,a,b,c ] [ a ,b,c ] [ b,ca ]
|
||||
// GCC: 5: [ a,b, ] [ , a,b, ] [ ,a,b, ] [ a ,b, ]
|
||||
|
||||
// Under C99 standard mode, the GCC ", ## __VA_ARGS__" extension *does not*
|
||||
// swallow the comma when followed by empty __VA_ARGS__.
|
||||
//
|
||||
// C99: 1: [ ] [ , ] [ , ] [ ] [ ]
|
||||
// C99: 2: [ a ] [ , a ] [ ,a ] [ a ] [ a ]
|
||||
// C99: 3: [ , ] [ , , ] [ ,, ] [ , ] [ ]
|
||||
// C99: 4: [ a,b,c ] [ , a,b,c ] [ ,a,b,c ] [ a ,b,c ] [ b,ca ]
|
||||
// C99: 5: [ a,b, ] [ , a,b, ] [ ,a,b, ] [ a ,b, ]
|
||||
|
||||
// Microsoft's extension is on ", __VA_ARGS__" (without explicit ##) where
|
||||
// the comma is swallowed when followed by empty __VA_ARGS__.
|
||||
//
|
||||
// MS: 1: [ ] [ ] [ ] [ ] [ ]
|
||||
// MS: 2: [ a ] [ , a ] [ ,a ] [ a ] [ a ]
|
||||
// MS: 3: [ , ] [ , , ] [ ,, ] [ , ] [ ]
|
||||
// MS: 4: [ a,b,c ] [ , a,b,c ] [ ,a,b,c ] [ a ,b,c ] [ b,ca ]
|
||||
// MS: 5: [ a,b, ] [ , a,b, ] [ ,a,b, ] [ a ,b, ]
|
||||
|
||||
// FIXME: Item 3(d) in MS output should be [ ] not [ , ]
|
Loading…
Reference in New Issue