Index: include/clang/Lex/Token.h =================================================================== --- include/clang/Lex/Token.h +++ include/clang/Lex/Token.h @@ -73,7 +73,7 @@ enum TokenFlags { StartOfLine = 0x01, // At start of line or only after whitespace // (considering the line after macro expansion). - LeadingSpace = 0x02, // Whitespace exists before this token (considering + LeadingSpace = 0x02, // Whitespace exists before this token (considering // whitespace after macro expansion). DisableExpand = 0x04, // This identifier may never be macro expanded. NeedsCleaning = 0x08, // Contained an escaped newline or trigraph. @@ -84,6 +84,13 @@ StringifiedInMacro = 0x100, // This string or character literal is formed by // macro stringizing or charizing operator. CommaAfterElided = 0x200, // The comma following this token was elided (MS). + CommaBeforeElided = 0x400, // The comma before this token was elided, used + // in conjunction with below (MS). + CommaBeforeNeedsElision = 0x800, // A comma in a Macro before a __VA_ARGS__ + // expansion shouldn't be emitted, EVEN if + // it is hidden in in a function like macro. + // This marks the token if that condition + // has yet to be satisified(MS). }; tok::TokenKind getKind() const { return Kind; } Index: include/clang/Lex/TokenLexer.h =================================================================== --- include/clang/Lex/TokenLexer.h +++ include/clang/Lex/TokenLexer.h @@ -99,6 +99,13 @@ /// should not be subject to further macro expansion. bool DisableMacroExpansion : 1; + /// UnfulfilledCommaElision - This is true when the TokenLexer has identified + /// an empty __VA_ARGS__ item but has been unable to elide the comma for it or + /// mark a token for comma elision. This likely happens when this TokenLexer + /// represents an empty function macro, like M(...) __VA_ARGS__. This is used + /// later to apply this value to the next available token. + bool UnfulfilledCommaElision : 1; + TokenLexer(const TokenLexer &) = delete; void operator=(const TokenLexer &) = delete; public: @@ -108,7 +115,8 @@ /// identifier for an object-like macro. TokenLexer(Token &Tok, SourceLocation ILEnd, MacroInfo *MI, MacroArgs *ActualArgs, Preprocessor &pp) - : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false) { + : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false), + UnfulfilledCommaElision(false) { Init(Tok, ILEnd, MI, ActualArgs); } @@ -124,7 +132,8 @@ /// the token lexer is empty. TokenLexer(const Token *TokArray, unsigned NumToks, bool DisableExpansion, bool ownsTokens, Preprocessor &pp) - : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false) { + : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false), + UnfulfilledCommaElision(false) { Init(TokArray, NumToks, DisableExpansion, ownsTokens); } Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -753,7 +753,7 @@ bool isVariadic = MI->isVariadic(); // Outer loop, while there are more arguments, keep reading them. - Token Tok; + Token Tok{}; // Read arguments as unexpanded tokens. This avoids issues, e.g., where // an argument value in a macro could expand to ',' or '(' or ')'. Index: lib/Lex/TokenLexer.cpp =================================================================== --- lib/Lex/TokenLexer.cpp +++ lib/Lex/TokenLexer.cpp @@ -41,7 +41,8 @@ Tokens = &*Macro->tokens_begin(); OwnsTokens = false; DisableMacroExpansion = false; - NumTokens = Macro->tokens_end()-Macro->tokens_begin(); + UnfulfilledCommaElision = false; + NumTokens = Macro->tokens_end() - Macro->tokens_begin(); MacroExpansionStart = SourceLocation(); SourceManager &SM = PP.getSourceManager(); @@ -89,6 +90,7 @@ Tokens = TokArray; OwnsTokens = ownsTokens; DisableMacroExpansion = disableMacroExpansion; + UnfulfilledCommaElision = false; NumTokens = NumToks; CurToken = 0; ExpandLocStart = ExpandLocEnd = SourceLocation(); @@ -140,6 +142,13 @@ && Macro->getNumArgs() < 2) return false; + // if we SHOULD have erased a comma, but we don't have any place to do so, + // mark the TokenLexer to consider doing so later. + if (ResultToks.empty() && !HasPasteOperator) { + UnfulfilledCommaElision = true; + return false; + } + // Is a comma available to be removed? if (ResultToks.empty() || !ResultToks.back().is(tok::comma)) return false; @@ -262,7 +271,10 @@ // argument and substitute the expanded tokens into the result. This is // C99 6.10.3.1p1. if (!PasteBefore && !PasteAfter) { - ExpandSingleFunctionArgument(CurTok, ArgNo, ResultToks); + if (!ExpandSingleFunctionArgument(CurTok, ArgNo, ResultToks)) + MaybeRemoveCommaBeforeVaArgs(ResultToks, + /*HasPasteOperator=*/false, Macro, ArgNo, + PP); continue; } @@ -370,6 +382,13 @@ Macro, ArgNo, PP); } + // If we have a leading empty expansion that we were unable to remove a comma, + // mark the 'front' token for removal so that it can be removed later. + if (!ResultToks.empty() && UnfulfilledCommaElision) { + ResultToks.front().setFlag(Token::CommaBeforeNeedsElision); + UnfulfilledCommaElision = false; + } + // If anything changed, install this as the new Tokens list. if (MadeChange) { assert(!OwnsTokens && "This would leak if we already own the token list"); @@ -460,6 +479,12 @@ Tok.setFlagValue(Token::LeadingSpace, HasLeadingSpace || NextTokGetsSpace); if (CurToken == 0) Tok.setFlag(Token::LeadingEmptyMacro); + // If we ended up being empty without a token to attach CommaElision to, + // we set the token being created as potentially needing a comma elision. + if (UnfulfilledCommaElision) { + Tok.setFlag(Token::CommaBeforeNeedsElision); + UnfulfilledCommaElision = false; + } return PP.HandleEndOfTokenLexer(Tok); } @@ -470,7 +495,16 @@ bool isFirstToken = CurToken == 0; // Get the next token to return. + bool CommaBeforeNeedsElision = Tok.getFlag(Token::CommaBeforeNeedsElision); + bool CommaBeforeElided = Tok.getFlag(Token::CommaBeforeElided); Tok = Tokens[CurToken++]; + // Propogate the comma elision to the next token if we haven't already. + // This only happens in teh case where the previous token wasn't emitted, + // in this call to PP::Lex. + if (CommaBeforeNeedsElision) + Tok.setFlag(Token::CommaBeforeNeedsElision); + if (CommaBeforeElided) + Tok.setFlag(Token::CommaBeforeElided); bool TokenIsFromPaste = false; @@ -544,6 +578,21 @@ return PP.HandleIdentifier(Tok); } + // If the current token is a comma, it might be subject to MSVC's comma + // elision, so look ahead and see if this is the case. Don't do this in + // LookAhead mode, since consecutive commas need to result in only a single + // elision. + if (Tok.is(tok::comma) && PP.getLangOpts().MSVCCompat) { + const Token &NextTok = PP.LookAhead(0); + // If we need to ignore this comma don't return successfully, and allow the + // next token to get picked up in lexing. + if (NextTok.getFlag(Token::CommaBeforeNeedsElision) && + !NextTok.getFlag(Token::CommaBeforeElided)) { + Tok.setFlag(Token::CommaBeforeElided); + return false; + } + } + // Otherwise, return a normal token. return true; } Index: test/Preprocessor/microsoft-ext.c =================================================================== --- test/Preprocessor/microsoft-ext.c +++ test/Preprocessor/microsoft-ext.c @@ -90,3 +90,33 @@ // CHECK: TWENTYONE_EMPTY_A: 1 #undef TWO #undef M + +// Comma suppressed before an empty variadic macro expansion. +#define EMPTY +#define PRINT(s, ...) printf(s, __VA_ARGS__) +PRINT("error", EMPTY); +// CHECK: printf("error"); +#undef PRINT +#undef EMPTY + +// Comma suppressed before an empty variadic macro expansion, even through +// function indirections. +#define M(...) __VA_ARGS__ +#define X(...) __VA_ARGS__,b; +#define Y(x,...) X(a, M(__VA_ARGS__)) +#define Z(x,...) a,X(M(__VA_ARGS__)) +#define Q(x,...) a,M(__VA_ARGS__); +int Y(xyz) +// CHECK: int a,b; +int Z(xyz) +// CHECK: int a,b; +int Q(xyz) +// CHECK: int a; +int d,X() +// CHECK: int d,,b; +#undef Q +#undef Z +#undef Y +#undef X +#undef M +