[Preprocessor] Prevent expansion of y in x ## y when x is empty

When x is empty, x ## is suppressed, and when y gets expanded, the fact that it follows ## is not
available in the macro expansion result. The macro definition can be checked instead, the ## will
be available there regardless of what x expands to.

Fixes http://llvm.org/PR12767

Patch by Harald van Dijk!

llvm-svn: 182699
This commit is contained in:
Argyrios Kyrtzidis 2013-05-25 01:35:18 +00:00
parent d6111d3c6d
commit 977026c5f5
2 changed files with 20 additions and 10 deletions

View File

@ -244,9 +244,11 @@ void TokenLexer::ExpandFunctionArguments() {
// Otherwise, this is a use of the argument. Find out if there is a paste // Otherwise, this is a use of the argument. Find out if there is a paste
// (##) operator before or after the argument. // (##) operator before or after the argument.
bool PasteBefore = bool NonEmptyPasteBefore =
!ResultToks.empty() && ResultToks.back().is(tok::hashhash); !ResultToks.empty() && ResultToks.back().is(tok::hashhash);
bool PasteBefore = i != 0 && Tokens[i-1].is(tok::hashhash);
bool PasteAfter = i+1 != e && Tokens[i+1].is(tok::hashhash); bool PasteAfter = i+1 != e && Tokens[i+1].is(tok::hashhash);
assert(!NonEmptyPasteBefore || PasteBefore);
// In Microsoft mode, remove the comma before __VA_ARGS__ to ensure there // In Microsoft mode, remove the comma before __VA_ARGS__ to ensure there
// are no trailing commas if __VA_ARGS__ is empty. // are no trailing commas if __VA_ARGS__ is empty.
@ -314,7 +316,7 @@ void TokenLexer::ExpandFunctionArguments() {
// that __VA_ARGS__ expands to multiple tokens, avoid a pasting error when // that __VA_ARGS__ expands to multiple tokens, avoid a pasting error when
// the expander trys to paste ',' with the first token of the __VA_ARGS__ // the expander trys to paste ',' with the first token of the __VA_ARGS__
// expansion. // expansion.
if (PasteBefore && ResultToks.size() >= 2 && if (NonEmptyPasteBefore && ResultToks.size() >= 2 &&
ResultToks[ResultToks.size()-2].is(tok::comma) && ResultToks[ResultToks.size()-2].is(tok::comma) &&
(unsigned)ArgNo == Macro->getNumArgs()-1 && (unsigned)ArgNo == Macro->getNumArgs()-1 &&
Macro->isVariadic()) { Macro->isVariadic()) {
@ -350,7 +352,7 @@ void TokenLexer::ExpandFunctionArguments() {
// case, we do not want the extra whitespace to be added. For example, // case, we do not want the extra whitespace to be added. For example,
// we want ". ## foo" -> ".foo" not ". foo". // we want ". ## foo" -> ".foo" not ". foo".
if ((CurTok.hasLeadingSpace() || NextTokGetsSpace) && if ((CurTok.hasLeadingSpace() || NextTokGetsSpace) &&
!PasteBefore) !NonEmptyPasteBefore)
ResultToks[ResultToks.size()-NumToks].setFlag(Token::LeadingSpace); ResultToks[ResultToks.size()-NumToks].setFlag(Token::LeadingSpace);
NextTokGetsSpace = false; NextTokGetsSpace = false;
@ -371,10 +373,14 @@ void TokenLexer::ExpandFunctionArguments() {
} }
// If this is on the RHS of a paste operator, we've already copied the // If this is on the RHS of a paste operator, we've already copied the
// paste operator to the ResultToks list. Remove it. // paste operator to the ResultToks list, unless the LHS was empty too.
assert(PasteBefore && ResultToks.back().is(tok::hashhash)); // Remove it.
NextTokGetsSpace |= ResultToks.back().hasLeadingSpace(); assert(PasteBefore);
ResultToks.pop_back(); if (NonEmptyPasteBefore) {
assert(ResultToks.back().is(tok::hashhash));
NextTokGetsSpace |= ResultToks.back().hasLeadingSpace();
ResultToks.pop_back();
}
// If this is the __VA_ARGS__ token, and if the argument wasn't provided, // 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 // and if the macro had at least one real argument, and if the token before

View File

@ -1,13 +1,17 @@
// RUN: %clang_cc1 -E %s | grep 'a:Y' // RUN: %clang_cc1 -E %s | FileCheck --strict-whitespace %s
// RUN: %clang_cc1 -E %s | grep 'b:Y'
// RUN: %clang_cc1 -E %s | grep 'c:YY'
#define FOO(X) X ## Y #define FOO(X) X ## Y
a:FOO() a:FOO()
// CHECK: a:Y
#define FOO2(X) Y ## X #define FOO2(X) Y ## X
b:FOO2() b:FOO2()
// CHECK: b:Y
#define FOO3(X) X ## Y ## X ## Y ## X ## X #define FOO3(X) X ## Y ## X ## Y ## X ## X
c:FOO3() c:FOO3()
// CHECK: c:YY
#define FOO4(X, Y) X ## Y
d:FOO4(,FOO4(,))
// CHECK: d:FOO4