def ext_pp_comma_expr : Extension<"comma operator in operand of #if">;
def ext_pp_bad_vaargs_use : Extension<
"__VA_ARGS__ can only appear in the expansion of a C99 variadic macro">;
+
+def ext_pp_bad_vaopt_use : Extension<
+ "__VA_OPT__ can only appear in the expansion of a variadic macro">;
+def err_pp_missing_lparen_in_vaopt_use : Error<
+ "missing '(' following __VA_OPT__">;
+def err_pp_vaopt_nested_use : Error<
+ "__VA_OPT__ cannot be nested within its own replacement tokens">;
+
+def err_vaopt_paste_at_start : Error<
+ "'##' cannot appear at start of __VA_OPT__ argument">;
+
+def err_vaopt_paste_at_end
+ : Error<"'##' cannot appear at end of __VA_OPT__ argument">;
+
def ext_pp_macro_redef : ExtWarn<"%0 macro redefined">, InGroup<MacroRedefined>;
def ext_variadic_macro : Extension<"variadic macros are a C99 feature">,
InGroup<VariadicMacros>;
/// argument, this returns false.
bool isVarargsElidedUse() const { return VarargsElided; }
+ /// Returns true if the macro was defined with a variadic (ellipsis) parameter
+ /// AND was invoked with at least one token supplied as a variadic argument.
+ ///
+ /// \code
+ /// #define F(a) a
+ /// #define V(a, ...) __VA_OPT__(a)
+ /// F() <-- returns false on this invocation.
+ /// V(,a) <-- returns true on this invocation.
+ /// V(,) <-- returns false on this invocation.
+ /// \endcode
+ ///
+
+ bool invokedWithVariadicArgument(const MacroInfo *const MI) const;
+
/// StringifyArgument - Implement C99 6.10.3.2p2, converting a sequence of
/// tokens into the literal string token that should be produced by the C #
/// preprocessor operator. If Charify is true, then it should be turned into
/// token expansion, etc.
class Preprocessor {
friend class VariadicMacroScopeGuard;
+ friend class VAOptDefinitionContext;
std::shared_ptr<PreprocessorOptions> PPOpts;
DiagnosticsEngine *Diags;
LangOptions &LangOpts;
IdentifierInfo *Ident_Pragma, *Ident__pragma; // _Pragma, __pragma
IdentifierInfo *Ident__identifier; // __identifier
IdentifierInfo *Ident__VA_ARGS__; // __VA_ARGS__
+ IdentifierInfo *Ident__VA_OPT__; // __VA_OPT__
IdentifierInfo *Ident__has_feature; // __has_feature
IdentifierInfo *Ident__has_extension; // __has_extension
IdentifierInfo *Ident__has_builtin; // __has_builtin
#define LLVM_CLANG_LEX_TOKENLEXER_H
#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/ArrayRef.h"
namespace clang {
class MacroInfo;
class Preprocessor;
class Token;
class MacroArgs;
+ class VAOptExpansionContext;
/// TokenLexer - This implements a lexer that returns tokens from a macro body
/// or token stream instead of lexing from a character buffer. This is used for
/// CurTokenIdx data members.
bool pasteTokens(Token &Tok);
-
+
+ /// Takes the tail sequence of tokens within ReplacementToks that represent
+ /// the just expanded __VA_OPT__ tokens (possibly zero tokens) and transforms
+ /// them into a string. \p VCtx is used to determine which token represents
+ /// the first __VA_OPT__ replacement token.
+ ///
+ /// \param[in,out] ReplacementToks - Contains the current Replacement Tokens
+ /// (prior to rescanning and token pasting), the tail end of which represents
+ /// the tokens just expanded through __VA_OPT__ processing. These (sub)
+ /// sequence of tokens are folded into one stringified token.
+ ///
+ /// \param[in] VCtx - contains information about the
+
+ void stringifyVAOPTContents(SmallVectorImpl<Token> &ReplacementToks,
+ const VAOptExpansionContext &VCtx,
+ SourceLocation VAOPTClosingParenLoc);
+
/// Expand the arguments of a function-like macro so that we can quickly
/// return preexpanded tokens from Tokens.
void ExpandFunctionArguments();
-//===- VariadicMacroSupport.h - scope-guards etc. -*- C++ -*---------------===//
+//===- VariadicMacroSupport.h - state machines and scope guards -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
#define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H
#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/SmallVector.h"
namespace clang {
+ class Preprocessor;
-/// An RAII class that tracks when the Preprocessor starts and stops lexing the
-/// definition of a (ISO C/C++) variadic macro. As an example, this is useful
-/// for unpoisoning and repoisoning certain identifiers (such as __VA_ARGS__)
-/// that are only allowed in this context. Also, being a friend of the
-/// Preprocessor class allows it to access PP's cached identifiers directly (as
-/// opposed to performing a lookup each time).
-class VariadicMacroScopeGuard {
- const Preprocessor &PP;
- IdentifierInfo &Ident__VA_ARGS__;
-
-public:
- VariadicMacroScopeGuard(const Preprocessor &P)
- : PP(P), Ident__VA_ARGS__(*PP.Ident__VA_ARGS__) {
- assert(Ident__VA_ARGS__.isPoisoned() && "__VA_ARGS__ should be poisoned "
- "outside an ISO C/C++ variadic "
- "macro definition!");
- }
-
- /// Client code should call this function just before the Preprocessor is
- /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
- void enterScope() { Ident__VA_ARGS__.setIsPoisoned(false); }
-
- /// Client code should call this function as soon as the Preprocessor has
- /// either completed lexing the macro's definition tokens, or an error occured
- /// and the context is being exited. This function is idempotent (might be
- /// explicitly called, and then reinvoked via the destructor).
- void exitScope() { Ident__VA_ARGS__.setIsPoisoned(true); }
-
- ~VariadicMacroScopeGuard() { exitScope(); }
-};
+ /// An RAII class that tracks when the Preprocessor starts and stops lexing
+ /// the definition of a (ISO C/C++) variadic macro. As an example, this is
+ /// useful for unpoisoning and repoisoning certain identifiers (such as
+ /// __VA_ARGS__) that are only allowed in this context. Also, being a friend
+ /// of the Preprocessor class allows it to access PP's cached identifiers
+ /// directly (as opposed to performing a lookup each time).
+ class VariadicMacroScopeGuard {
+ const Preprocessor &PP;
+ IdentifierInfo *const Ident__VA_ARGS__;
+ IdentifierInfo *const Ident__VA_OPT__;
+ public:
+ VariadicMacroScopeGuard(const Preprocessor &P)
+ : PP(P), Ident__VA_ARGS__(PP.Ident__VA_ARGS__),
+ Ident__VA_OPT__(PP.Ident__VA_OPT__) {
+ assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
+ "outside an ISO C/C++ variadic "
+ "macro definition!");
+ assert(
+ !Ident__VA_OPT__ ||
+ (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!"));
+ }
+
+ /// Client code should call this function just before the Preprocessor is
+ /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
+ void enterScope() {
+ Ident__VA_ARGS__->setIsPoisoned(false);
+ if (Ident__VA_OPT__)
+ Ident__VA_OPT__->setIsPoisoned(false);
+ }
+
+ /// Client code should call this function as soon as the Preprocessor has
+ /// either completed lexing the macro's definition tokens, or an error
+ /// occured and the context is being exited. This function is idempotent
+ /// (might be explicitly called, and then reinvoked via the destructor).
+ void exitScope() {
+ Ident__VA_ARGS__->setIsPoisoned(true);
+ if (Ident__VA_OPT__)
+ Ident__VA_OPT__->setIsPoisoned(true);
+ }
+
+ ~VariadicMacroScopeGuard() { exitScope(); }
+ };
+
+ /// \brief A class for tracking whether we're inside a VA_OPT during a
+ /// traversal of the tokens of a variadic macro definition.
+ class VAOptDefinitionContext {
+ Preprocessor &PP;
+
+ /// Contains all the locations of so far unmatched lparens.
+ SmallVector<SourceLocation, 8> UnmatchedOpeningParens;
+
+ const IdentifierInfo *const Ident__VA_OPT__;
+
+
+ public:
+ VAOptDefinitionContext(Preprocessor &PP)
+ : PP(PP), Ident__VA_OPT__(PP.Ident__VA_OPT__) {}
+
+ bool isVAOptToken(const Token &T) const {
+ return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__;
+ }
+
+ /// Returns true if we have seen the __VA_OPT__ and '(' but before having
+ /// seen the matching ')'.
+ bool isInVAOpt() const { return UnmatchedOpeningParens.size(); }
+
+ /// Call this function as soon as you see __VA_OPT__ and '('.
+ void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) {
+ assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this");
+ UnmatchedOpeningParens.push_back(LParenLoc);
+
+ }
+
+ SourceLocation getUnmatchedOpeningParenLoc() const {
+ assert(isInVAOpt() && "Must be within VAOPT context to call this");
+ return UnmatchedOpeningParens.back();
+ }
+
+ /// Call this function each time an rparen is seen. It returns true only if
+ /// the rparen that was just seen was the eventual (non-nested) closing
+ /// paren for VAOPT, and ejects us out of the VAOPT context.
+ bool sawClosingParen() {
+ assert(isInVAOpt() && "Must be within VAOPT context to call this");
+ UnmatchedOpeningParens.pop_back();
+ return !UnmatchedOpeningParens.size();
+ }
+
+ /// Call this function each time an lparen is seen.
+ void sawOpeningParen(SourceLocation LParenLoc) {
+ assert(isInVAOpt() && "Must be within VAOPT context to call this");
+ UnmatchedOpeningParens.push_back(LParenLoc);
+ }
+
+ };
+
+ /// \brief A class for tracking whether we're inside a VA_OPT during a
+ /// traversal of the tokens of a macro during macro expansion.
+ class VAOptExpansionContext : VAOptDefinitionContext {
+
+ Token SyntheticEOFToken;
+
+ // The (spelling) location of the current __VA_OPT__ in the replacement list
+ // of the function-like macro being expanded.
+ SourceLocation VAOptLoc;
+
+ // NumOfTokensPriorToVAOpt : when != -1, contains the index *of* the first
+ // token of the current VAOPT contents (so we know where to start eager
+ // token-pasting and stringification) *within* the substituted tokens of
+ // the function-like macro's new replacement list.
+ int NumOfTokensPriorToVAOpt = -1;
+
+ unsigned LeadingSpaceForStringifiedToken : 1;
+
+ unsigned StringifyBefore : 1;
+ unsigned CharifyBefore : 1;
+
+
+ bool hasStringifyBefore() const {
+ assert(!isReset() &&
+ "Must only be called if the state has not been reset");
+ return StringifyBefore;
+ }
+
+ bool isReset() const {
+ return NumOfTokensPriorToVAOpt == -1 ||
+ VAOptLoc.isInvalid();
+ }
+
+ public:
+ VAOptExpansionContext(Preprocessor &PP)
+ : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false),
+ StringifyBefore(false), CharifyBefore(false) {
+ SyntheticEOFToken.startToken();
+ SyntheticEOFToken.setKind(tok::eof);
+ }
+
+ void reset() {
+ VAOptLoc = SourceLocation();
+ NumOfTokensPriorToVAOpt = -1;
+ LeadingSpaceForStringifiedToken = false;
+ StringifyBefore = false;
+ CharifyBefore = false;
+ }
+
+ const Token &getEOFTok() const { return SyntheticEOFToken; }
+
+ void sawHashOrHashAtBefore(const bool HasLeadingSpace,
+ const bool IsHashAt) {
+
+ StringifyBefore = !IsHashAt;
+ CharifyBefore = IsHashAt;
+ LeadingSpaceForStringifiedToken = HasLeadingSpace;
+ }
+
+
+
+ bool hasCharifyBefore() const {
+ assert(!isReset() &&
+ "Must only be called if the state has not been reset");
+ return CharifyBefore;
+ }
+ bool hasStringifyOrCharifyBefore() const {
+ return hasStringifyBefore() || hasCharifyBefore();
+ }
+
+ unsigned int getNumberOfTokensPriorToVAOpt() const {
+ assert(!isReset() &&
+ "Must only be called if the state has not been reset");
+ return NumOfTokensPriorToVAOpt;
+ }
+
+ bool getLeadingSpaceForStringifiedToken() const {
+ assert(hasStringifyBefore() &&
+ "Must only be called if this has been marked for stringification");
+ return LeadingSpaceForStringifiedToken;
+ }
+
+ void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,
+ const unsigned int NumPriorTokens) {
+ assert(VAOptLoc.isFileID() && "Must not come from a macro expansion");
+ assert(isReset() && "Must only be called if the state has been reset");
+ VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation());
+ this->VAOptLoc = VAOptLoc;
+ NumOfTokensPriorToVAOpt = NumPriorTokens;
+ assert(NumOfTokensPriorToVAOpt > -1 &&
+ "Too many prior tokens");
+ }
+
+ SourceLocation getVAOptLoc() const {
+ assert(!isReset() &&
+ "Must only be called if the state has not been reset");
+ assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid");
+ return VAOptLoc;
+ }
+ using VAOptDefinitionContext::isVAOptToken;
+ using VAOptDefinitionContext::isInVAOpt;
+ using VAOptDefinitionContext::sawClosingParen;
+ using VAOptDefinitionContext::sawOpeningParen;
+
+ };
} // end namespace clang
#endif
return Result;
}
+// This function assumes that the variadic arguments are the tokens
+// corresponding to the last parameter (ellipsis) - and since tokens are
+// separated by the 'eof' token, if that is the only token corresponding to that
+// last parameter, we know no variadic arguments were supplied.
+bool MacroArgs::invokedWithVariadicArgument(const MacroInfo *const MI) const {
+ if (!MI->isVariadic())
+ return false;
+ const int VariadicArgIndex = getNumMacroArguments() - 1;
+ return getUnexpArgument(VariadicArgIndex)->isNot(tok::eof);
+}
/// ArgNeedsPreexpansion - If we can prove that the argument won't be affected
/// by pre-expansion, return false. Otherwise, conservatively return true.
// Otherwise, read the body of a function-like macro. While we are at it,
// check C99 6.10.3.2p1: ensure that # operators are followed by macro
// parameters in function-like macro expansions.
+
+ VAOptDefinitionContext VAOCtx(*this);
+
while (Tok.isNot(tok::eod)) {
LastTok = Tok;
if (!Tok.isOneOf(tok::hash, tok::hashat, tok::hashhash)) {
MI->AddTokenToBody(Tok);
+ if (VAOCtx.isVAOptToken(Tok)) {
+ // If we're already within a VAOPT, emit an error.
+ if (VAOCtx.isInVAOpt()) {
+ Diag(Tok, diag::err_pp_vaopt_nested_use);
+ return nullptr;
+ }
+ // Ensure VAOPT is followed by a '(' .
+ LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_pp_missing_lparen_in_vaopt_use);
+ return nullptr;
+ }
+ MI->AddTokenToBody(Tok);
+ VAOCtx.sawVAOptFollowedByOpeningParens(Tok.getLocation());
+ LexUnexpandedToken(Tok);
+ if (Tok.is(tok::hashhash)) {
+ Diag(Tok, diag::err_vaopt_paste_at_start);
+ return nullptr;
+ }
+ continue;
+ } else if (VAOCtx.isInVAOpt()) {
+ if (Tok.is(tok::r_paren)) {
+ if (VAOCtx.sawClosingParen()) {
+ const unsigned NumTokens = MI->getNumTokens();
+ assert(NumTokens >= 3 && "Must have seen at least __VA_OPT__( "
+ "and a subsequent tok::r_paren");
+ if (MI->getReplacementToken(NumTokens - 2).is(tok::hashhash)) {
+ Diag(Tok, diag::err_vaopt_paste_at_end);
+ return nullptr;
+ }
+ }
+ } else if (Tok.is(tok::l_paren)) {
+ VAOCtx.sawOpeningParen(Tok.getLocation());
+ }
+ }
// Get the next token of the macro.
LexUnexpandedToken(Tok);
continue;
continue;
}
+ // Our Token is a stringization operator.
// Get the next token of the macro.
LexUnexpandedToken(Tok);
- // Check for a valid macro arg identifier.
- if (Tok.getIdentifierInfo() == nullptr ||
- MI->getParameterNum(Tok.getIdentifierInfo()) == -1) {
+ // Check for a valid macro arg identifier or __VA_OPT__.
+ if (!VAOCtx.isVAOptToken(Tok) &&
+ (Tok.getIdentifierInfo() == nullptr ||
+ MI->getParameterNum(Tok.getIdentifierInfo()) == -1)) {
// If this is assembler-with-cpp mode, we accept random gibberish after
// the '#' because '#' is often a comment character. However, change
// Things look ok, add the '#' and param name tokens to the macro.
MI->AddTokenToBody(LastTok);
- MI->AddTokenToBody(Tok);
- LastTok = Tok;
- // Get the next token of the macro.
- LexUnexpandedToken(Tok);
+ // If the token following '#' is VAOPT, let the next iteration handle it
+ // and check it for correctness, otherwise add the token and prime the
+ // loop with the next one.
+ if (!VAOCtx.isVAOptToken(Tok)) {
+ MI->AddTokenToBody(Tok);
+ LastTok = Tok;
+
+ // Get the next token of the macro.
+ LexUnexpandedToken(Tok);
+ }
+ }
+ if (VAOCtx.isInVAOpt()) {
+ assert(Tok.is(tok::eod) && "Must be at End Of preprocessing Directive");
+ Diag(Tok, diag::err_pp_expected_after)
+ << LastTok.getKind() << tok::r_paren;
+ Diag(VAOCtx.getUnmatchedOpeningParenLoc(), diag::note_matching) << tok::l_paren;
+ return nullptr;
}
}
MI->setDefinitionEndLoc(LastTok.getLocation());
// We haven't read anything from the external source.
ReadMacrosFromExternalSource = false;
-
- // "Poison" __VA_ARGS__, which can only appear in the expansion of a macro.
- // This gets unpoisoned where it is allowed.
+
+ // "Poison" __VA_ARGS__, __VA_OPT__ which can only appear in the expansion of
+ // a macro. They get unpoisoned where it is allowed.
(Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
SetPoisonReason(Ident__VA_ARGS__,diag::ext_pp_bad_vaargs_use);
-
+ if (getLangOpts().CPlusPlus2a) {
+ (Ident__VA_OPT__ = getIdentifierInfo("__VA_OPT__"))->setIsPoisoned();
+ SetPoisonReason(Ident__VA_OPT__,diag::ext_pp_bad_vaopt_use);
+ } else {
+ Ident__VA_OPT__ = nullptr;
+ }
+
// Initialize the pragma handlers.
RegisterBuiltinPragmas();
// unpoisoned it if we're defining a C99 macro.
if (II.isOutOfDate()) {
bool CurrentIsPoisoned = false;
- if (&II == Ident__VA_ARGS__)
- CurrentIsPoisoned = Ident__VA_ARGS__->isPoisoned();
+ const bool IsSpecialVariadicMacro =
+ &II == Ident__VA_ARGS__ || &II == Ident__VA_OPT__;
+ if (IsSpecialVariadicMacro)
+ CurrentIsPoisoned = II.isPoisoned();
updateOutOfDateIdentifier(II);
Identifier.setKind(II.getTokenID());
- if (&II == Ident__VA_ARGS__)
+ if (IsSpecialVariadicMacro)
II.setIsPoisoned(CurrentIsPoisoned);
}
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/VariadicMacroSupport.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
return true;
}
+void TokenLexer::stringifyVAOPTContents(
+ SmallVectorImpl<Token> &ResultToks, const VAOptExpansionContext &VCtx,
+ const SourceLocation VAOPTClosingParenLoc) {
+ const int NumToksPriorToVAOpt = VCtx.getNumberOfTokensPriorToVAOpt();
+ const unsigned int NumVAOptTokens = ResultToks.size() - NumToksPriorToVAOpt;
+ Token *const VAOPTTokens =
+ NumVAOptTokens ? &ResultToks[NumToksPriorToVAOpt] : nullptr;
+
+ SmallVector<Token, 64> ConcatenatedVAOPTResultToks;
+ // FIXME: Should we keep track within VCtx that we did or didnot
+ // encounter pasting - and only then perform this loop.
+
+ // Perform token pasting (concatenation) prior to stringization.
+ for (unsigned int CurTokenIdx = 0; CurTokenIdx != NumVAOptTokens;
+ ++CurTokenIdx) {
+ const unsigned int PrevTokenIdx = CurTokenIdx;
+
+ if (VAOPTTokens[CurTokenIdx].is(tok::hashhash)) {
+ assert(CurTokenIdx != 0 &&
+ "Can not have __VAOPT__ contents begin with a ##");
+ Token &LHS = VAOPTTokens[CurTokenIdx - 1];
+ pasteTokens(LHS, llvm::makeArrayRef(VAOPTTokens, NumVAOptTokens),
+ CurTokenIdx);
+ // CurTokenIdx is either the same as NumTokens or one past the
+ // last token concatenated.
+ // PrevTokenIdx is the index of the hashhash
+ const unsigned NumTokensPastedTogether = CurTokenIdx - PrevTokenIdx + 1;
+ // Replace the token prior to the first ## in this iteration.
+ ConcatenatedVAOPTResultToks.back() = LHS;
+ if (CurTokenIdx == NumVAOptTokens)
+ break;
+ }
+ ConcatenatedVAOPTResultToks.push_back(VAOPTTokens[CurTokenIdx]);
+ }
+
+ ConcatenatedVAOPTResultToks.push_back(VCtx.getEOFTok());
+ // Get the SourceLocation that represents the start location within
+ // the macro definition that marks where this string is substituted
+ // into: i.e. the __VA_OPT__ and the ')' within the spelling of the
+ // macro definition, and use it to indicate that the stringified token
+ // was generated from that location.
+ const SourceLocation ExpansionLocStartWithinMacro =
+ getExpansionLocForMacroDefLoc(VCtx.getVAOptLoc());
+ const SourceLocation ExpansionLocEndWithinMacro =
+ getExpansionLocForMacroDefLoc(VAOPTClosingParenLoc);
+
+ Token StringifiedVAOPT = MacroArgs::StringifyArgument(
+ &ConcatenatedVAOPTResultToks[0], PP, VCtx.hasCharifyBefore() /*Charify*/,
+ ExpansionLocStartWithinMacro, ExpansionLocEndWithinMacro);
+
+ if (VCtx.getLeadingSpaceForStringifiedToken())
+ StringifiedVAOPT.setFlag(Token::LeadingSpace);
+
+ StringifiedVAOPT.setFlag(Token::StringifiedInMacro);
+ // Resize (shrink) the token stream to just capture this stringified token.
+ ResultToks.resize(NumToksPriorToVAOpt + 1);
+ ResultToks.back() = StringifiedVAOPT;
+}
+
/// Expand the arguments of a function-like macro so that we can quickly
/// return preexpanded tokens from Tokens.
void TokenLexer::ExpandFunctionArguments() {
// we install the newly expanded sequence as the new 'Tokens' list.
bool MadeChange = false;
+ const bool CalledWithVariadicArguments =
+ ActualArgs->invokedWithVariadicArgument(Macro);
+
+ VAOptExpansionContext VCtx(PP);
+
for (unsigned I = 0, E = NumTokens; I != E; ++I) {
- // If we found the stringify operator, get the argument stringified. The
- // preprocessor already verified that the following token is a macro name
- // when the #define was parsed.
+
const Token &CurTok = Tokens[I];
// We don't want a space for the next token after a paste
// operator. In valid code, the token will get smooshed onto the
if (I != 0 && !Tokens[I-1].is(tok::hashhash) && CurTok.hasLeadingSpace())
NextTokGetsSpace = true;
+ if (VCtx.isVAOptToken(CurTok)) {
+ MadeChange = true;
+ assert(Tokens[I + 1].is(tok::l_paren) &&
+ "__VA_OPT__ must be followed by '('");
+
+ ++I; // Skip the l_paren
+ VCtx.sawVAOptFollowedByOpeningParens(CurTok.getLocation(),
+ ResultToks.size());
+
+ continue;
+ }
+
+ // We have entered into the __VA_OPT__ context, so handle tokens
+ // appropriately.
+ if (VCtx.isInVAOpt()) {
+ // If we are about to process a token that is either an argument to
+ // __VA_OPT__ or its closing rparen, then:
+ // 1) If the token is the closing rparen that exits us out of __VA_OPT__,
+ // perform any necessary stringification or placemarker processing,
+ // and/or skip to the next token.
+ // 2) else if macro was invoked without variadic arguments skip this
+ // token.
+ // 3) else (macro was invoked with variadic arguments) process the token
+ // normally.
+
+ if (Tokens[I].is(tok::l_paren))
+ VCtx.sawOpeningParen(Tokens[I].getLocation());
+ // Continue skipping tokens within __VA_OPT__ if the macro was not
+ // called with variadic arguments, else let the rest of the loop handle
+ // this token. Note sawClosingParen() returns true only if the r_paren matches
+ // the closing r_paren of the __VA_OPT__.
+ if (!Tokens[I].is(tok::r_paren) || !VCtx.sawClosingParen()) {
+ if (!CalledWithVariadicArguments) {
+ // Skip this token.
+ continue;
+ }
+ // ... else the macro was called with variadic arguments, and we do not
+ // have a closing rparen - so process this token normally.
+
+ } else {
+ // Current token is the closing r_paren which marks the end of the
+ // __VA_OPT__ invocation, so handle any place-marker pasting (if
+ // empty) by removing hashhash either before (if exists) or after. And
+ // also stringify the entire contents if VAOPT was preceded by a hash,
+ // but do so only after any token concatenation that needs to occur
+ // within the contents of VAOPT.
+
+ if (VCtx.hasStringifyOrCharifyBefore()) {
+ // Replace all the tokens just added from within VAOPT into a single
+ // stringified token. This requires token-pasting to eagerly occur
+ // within these tokens. If either the contents of VAOPT were empty
+ // or the macro wasn't called with any variadic arguments, the result
+ // is a token that represents an empty string.
+ stringifyVAOPTContents(ResultToks, VCtx,
+ /*ClosingParenLoc*/ Tokens[I].getLocation());
+
+ } else if (/*No tokens within VAOPT*/ !(
+ ResultToks.size() - VCtx.getNumberOfTokensPriorToVAOpt())) {
+ // Treat VAOPT as a placemarker token. Eat either the '##' before the
+ // RHS/VAOPT (if one exists, suggesting that the LHS (if any) to that
+ // hashhash was not a placemarker) or the '##'
+ // after VAOPT, but not both.
+
+ if (ResultToks.size() && ResultToks.back().is(tok::hashhash)) {
+ ResultToks.pop_back();
+ } else if ((I + 1 != E) && Tokens[I + 1].is(tok::hashhash)) {
+ ++I; // Skip the following hashhash.
+ }
+ }
+ VCtx.reset();
+ // We processed __VA_OPT__'s closing paren (and the exit out of
+ // __VA_OPT__), so skip to the next token.
+ continue;
+ }
+ }
+
+ // If we found the stringify operator, get the argument stringified. The
+ // preprocessor already verified that the following token is a macro
+ // parameter or __VA_OPT__ when the #define was lexed.
+
if (CurTok.isOneOf(tok::hash, tok::hashat)) {
int ArgNo = Macro->getParameterNum(Tokens[I+1].getIdentifierInfo());
- assert(ArgNo != -1 && "Token following # is not an argument?");
-
+ assert((ArgNo != -1 || VCtx.isVAOptToken(Tokens[I + 1])) &&
+ "Token following # is not an argument or __VA_OPT__!");
+
+ if (ArgNo == -1) {
+ // Handle the __VA_OPT__ case.
+ VCtx.sawHashOrHashAtBefore(NextTokGetsSpace,
+ CurTok.is(tok::hashat));
+ continue;
+ }
+ // Else handle the simple argument case.
SourceLocation ExpansionLocStart =
getExpansionLocForMacroDefLoc(CurTok.getLocation());
SourceLocation ExpansionLocEnd =
!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);
- assert(!NonEmptyPasteBefore || PasteBefore);
+
+ assert((!NonEmptyPasteBefore || PasteBefore || VCtx.isInVAOpt()) &&
+ "unexpected ## in ResultToks");
// Otherwise, if this is not an argument token, just add the token to the
// output buffer.
assert(PasteBefore);
if (NonEmptyPasteBefore) {
assert(ResultToks.back().is(tok::hashhash));
- ResultToks.pop_back();
+ // Do not remove the paste operator if it is the one before __VA_OPT__
+ // (and we are still processing tokens within VA_OPT). We handle the case
+ // of removing the paste operator if __VA_OPT__ reduces to the notional
+ // placemarker above when we encounter the closing paren of VA_OPT.
+ if (!VCtx.isInVAOpt() ||
+ ResultToks.size() > VCtx.getNumberOfTokensPriorToVAOpt())
+ ResultToks.pop_back();
}
// If this is the __VA_ARGS__ token, and if the argument wasn't provided,
--- /dev/null
+// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++2a\r
+\r
+//expected-error@+1{{missing '('}}\r
+#define V1(...) __VA_OPT__ \r
+#undef V1\r
+// OK\r
+#define V1(...) __VA_OPT__ ()\r
+#undef V1 \r
+\r
+//expected-warning@+1{{can only appear in the expansion of a variadic macro}}\r
+#define V2() __VA_OPT__(x) \r
+#undef V2\r
+\r
+//expected-error@+2{{missing ')' after}}\r
+//expected-note@+1{{to match this '('}}\r
+#define V3(...) __VA_OPT__(\r
+#undef V3\r
+\r
+#define V4(...) __VA_OPT__(__VA_ARGS__)\r
+#undef V4\r
+\r
+//expected-error@+1{{nested}}\r
+#define V5(...) __VA_OPT__(__VA_OPT__())\r
+#undef V5\r
+\r
+//expected-error@+1{{not followed by}}\r
+#define V1(...) __VA_OPT__ (#)\r
+#undef V1\r
+\r
+//expected-error@+1{{cannot appear at start}}\r
+#define V1(...) __VA_OPT__ (##)\r
+#undef V1\r
+\r
+//expected-error@+1{{cannot appear at start}}\r
+#define V1(...) __VA_OPT__ (## X) x\r
+#undef V1\r
+\r
+//expected-error@+1{{cannot appear at end}}\r
+#define V1(...) y __VA_OPT__ (X ##)\r
+#undef V1\r
+ \r
+\r
+#define FOO(x,...) # __VA_OPT__(x) #x #__VA_OPT__(__VA_ARGS__) //OK\r
+\r
+//expected-error@+1{{not followed by a macro parameter}}\r
+#define V1(...) __VA_OPT__(#)\r
+#undef V1\r
+\r
+//expected-error@+1{{cannot appear at start}}\r
+#define V1(...) a __VA_OPT__(##) b\r
+#undef V1\r
+\r
+//expected-error@+1{{cannot appear at start}}\r
+#define V1(...) a __VA_OPT__(a ## b) b __VA_OPT__(##)\r
+#undef V1\r
+\r
+#define V1(x,...) # __VA_OPT__(b x) // OK\r
+#undef V1\r
+\r
+//expected-error@+2{{missing ')' after}}\r
+//expected-note@+1{{to match this '('}}\r
+#define V1(...) __VA_OPT__ ((())\r
+#undef V1\r
+\r
--- /dev/null
+// RUN: %clang_cc1 -E %s -pedantic -std=c++2a | FileCheck -strict-whitespace %s\r
+\r
+#define LPAREN ( \r
+#define RPAREN ) \r
+\r
+#define A0 expandedA0\r
+#define A1 expandedA1 A0\r
+#define A2 expandedA2 A1\r
+#define A3 expandedA3 A2\r
+\r
+#define A() B LPAREN )\r
+#define B() C LPAREN )\r
+#define C() D LPAREN )\r
+\r
+\r
+#define F(x, y) x + y \r
+#define ELLIP_FUNC(...) __VA_OPT__(__VA_ARGS__)\r
+\r
+1: ELLIP_FUNC(F, LPAREN, 'a', 'b', RPAREN); \r
+2: ELLIP_FUNC(F LPAREN 'a', 'b' RPAREN); \r
+#undef F\r
+#undef ELLIP_FUNC\r
+\r
+// CHECK: 1: F, (, 'a', 'b', );\r
+// CHECK: 2: 'a' + 'b';\r
+\r
+#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)\r
+3: F(a, b, c) // replaced by f(0, a, b, c) \r
+4: F() // replaced by f(0)\r
+\r
+// CHECK: 3: f(0 , a, b, c) \r
+// CHECK: 4: f(0 )\r
+#undef F\r
+\r
+#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)\r
+\r
+5: G(a, b, c) // replaced by f(0, a , b, c) \r
+6: G(a) // replaced by f(0, a) \r
+7: G(a,) // replaced by f(0, a) \r
+7.1: G(a,,)\r
+\r
+\r
+// CHECK: 5: f(0, a , b, c) \r
+// CHECK: 6: f(0, a ) \r
+// CHECK: 7: f(0, a ) \r
+// CHECK: 7.1: f(0, a , ,)\r
+#undef G \r
+\r
+#define HT_B() TONG\r
+\r
+#define F(x, ...) HT_ ## __VA_OPT__(x x A() #x)\r
+\r
+8: F(1)\r
+9: F(A(),1)\r
+\r
+// CHECK: 8: HT_\r
+// CHECK: 9: TONG C ( ) B ( ) "A()"\r
+#undef HT_B\r
+#undef F\r
+\r
+#define F(a,...) #__VA_OPT__(A1 a)\r
+\r
+10: F(A())\r
+11: F(A1 A(), 1)\r
+// CHECK: 10: ""\r
+// CHECK: 11: "A1 expandedA1 expandedA0 B ( )"\r
+#undef F\r
+\r
+\r
+#define F(a,...) a ## __VA_OPT__(A1 a) ## __VA_ARGS__ ## a\r
+12.0: F()\r
+12: F(,)\r
+13: F(B,)\r
+// CHECK: 12.0: \r
+// CHECK: 12: \r
+// CHECK: 13: BB \r
+#undef F\r
+\r
+#define F(...) #__VA_OPT__() X ## __VA_OPT__() #__VA_OPT__( )\r
+\r
+14: F()\r
+15: F(1)\r
+\r
+// CHECK: 14: "" X ""\r
+// CHECK: 15: "" X ""\r
+\r
+#undef F\r
+\r
+#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })\r
+\r
+16: SDEF(foo); // replaced by S foo; \r
+17: SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; \r
+\r
+// CHECK: 16: S foo ;\r
+// CHECK: 17: S bar = { 1, 2 }; \r
+#undef SDEF\r
+\r
+#define F(a,...) A() #__VA_OPT__(A3 __VA_ARGS__ a ## __VA_ARGS__ ## a ## C A3) A()\r
+\r
+18: F()\r
+19: F(,)\r
+20: F(,A3)\r
+21: F(A3, A(),A0)\r
+\r
+\r
+// CHECK: 18: B ( ) "" B ( ) \r
+// CHECK: 19: B ( ) "" B ( ) \r
+// CHECK: 20: B ( ) "A3 expandedA3 expandedA2 expandedA1 expandedA0 A3C A3" B ( )\r
+// CHECK: 21: B ( ) "A3 B ( ),expandedA0 A3A(),A0A3C A3" B ( )\r
+\r
+#undef F\r
+\r
+#define F(a,...) A() #__VA_OPT__(A3 __VA_ARGS__ a ## __VA_ARGS__ ## a ## C A3) a __VA_OPT__(A0 __VA_ARGS__ a ## __VA_ARGS__ ## a ## C A0) A()\r
+\r
+22: F()\r
+23: F(,)\r
+24: F(,A0)\r
+25: F(A0, A(),A0)\r
+\r
+\r
+// CHECK: 22: B ( ) "" B ( ) \r
+// CHECK: 23: B ( ) "" B ( ) \r
+// CHECK: 24: B ( ) "A3 expandedA0 A0C A3" expandedA0 expandedA0 A0C expandedA0 B ( )\r
+// CHECK: 25: B ( ) "A3 B ( ),expandedA0 A0A(),A0A0C A3" expandedA0 expandedA0 C ( ),expandedA0 A0A(),A0A0C expandedA0 B ( )\r
+\r
+#undef F\r
+\r
+#define F(a,...) __VA_OPT__(B a ## a) ## 1\r
+#define G(a,...) __VA_OPT__(B a) ## 1\r
+26: F(,1)\r
+26_1: G(,1)\r
+// CHECK: 26: B1\r
+// CHECK: 26_1: B1\r
+#undef F\r
+#undef G\r
+\r
+#define F(a,...) B ## __VA_OPT__(a 1) ## 1\r
+#define G(a,...) B ## __VA_OPT__(a ## a 1) ## 1\r
+\r
+27: F(,1)\r
+27_1: F(A0,1)\r
+28: G(,1)\r
+// CHECK: 27: B11\r
+// CHECK: 27_1: BexpandedA0 11\r
+// CHECK: 28: B11\r
+\r
+#undef F\r
+#undef G\r