From 4fa4b480270c14dfdcd0dfd4f76938e973082e3b Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Wed, 14 Nov 2012 02:18:46 +0000 Subject: [PATCH] Suppress elided variadic macro argument extension diagnostic for macros using the related comma pasting extension. In certain cases, we used to get two diagnostics for what is essentially one extension. This change suppresses the first diagnostic in certain cases where we know we're going to print the second diagnostic. The diagnostic is redundant, and it can't be suppressed in the definition of the macro because it points at the use of the macro, so we want to avoid printing it if possible. The implementation works by detecting constructs which look like comma pasting at the time of the definition of the macro; this information is then used when the macro is used. (We can't actually detect whether we're using the comma pasting extension until the macro is actually used, but we can detecting constructs which will be comma pasting if the varargs argument is elided.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167907 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Lex/MacroInfo.h | 6 ++++++ lib/Lex/MacroInfo.cpp | 2 ++ lib/Lex/PPDirectives.cpp | 31 ++++++++++++++++++++++++++++++- lib/Lex/PPMacroExpansion.cpp | 11 ++++++++--- lib/Serialization/ASTReader.cpp | 2 ++ lib/Serialization/ASTWriter.cpp | 1 + test/Preprocessor/macro_fn.c | 6 ++++++ 7 files changed, 55 insertions(+), 4 deletions(-) diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h index aba77d580d..aeedd735e3 100644 --- a/include/clang/Lex/MacroInfo.h +++ b/include/clang/Lex/MacroInfo.h @@ -76,6 +76,9 @@ class MacroInfo { /// it has not yet been redefined or undefined. bool IsBuiltinMacro : 1; + /// \brief Whether this macro contains the sequence ", ## __VA_ARGS__" + bool HasCommaPasting : 1; + /// \brief True if this macro was loaded from an AST file. bool IsFromAST : 1; @@ -253,6 +256,9 @@ class MacroInfo { /// __LINE__, which requires processing before expansion. bool isBuiltinMacro() const { return IsBuiltinMacro; } + bool hasCommaPasting() const { return HasCommaPasting; } + void setHasCommaPasting() { HasCommaPasting = true; } + /// isFromAST - Return true if this macro was loaded from an AST file. bool isFromAST() const { return IsFromAST; } diff --git a/lib/Lex/MacroInfo.cpp b/lib/Lex/MacroInfo.cpp index 904f04e4f8..d1875c79d0 100644 --- a/lib/Lex/MacroInfo.cpp +++ b/lib/Lex/MacroInfo.cpp @@ -25,6 +25,7 @@ MacroInfo::MacroInfo(SourceLocation DefLoc) IsC99Varargs(false), IsGNUVarargs(false), IsBuiltinMacro(false), + HasCommaPasting(false), IsFromAST(false), ChangedAfterLoad(false), IsDisabled(false), @@ -50,6 +51,7 @@ MacroInfo::MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator) IsC99Varargs(MI.IsC99Varargs), IsGNUVarargs(MI.IsGNUVarargs), IsBuiltinMacro(MI.IsBuiltinMacro), + HasCommaPasting(MI.HasCommaPasting), IsFromAST(MI.IsFromAST), ChangedAfterLoad(MI.ChangedAfterLoad), IsDisabled(MI.IsDisabled), diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index b7c1846e82..50b161a172 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -1809,7 +1809,7 @@ void Preprocessor::HandleDefineDirective(Token &DefineTok) { while (Tok.isNot(tok::eod)) { LastTok = Tok; - if (Tok.isNot(tok::hash)) { + if (Tok.isNot(tok::hash) && Tok.isNot(tok::hashhash)) { MI->AddTokenToBody(Tok); // Get the next token of the macro. @@ -1817,6 +1817,35 @@ void Preprocessor::HandleDefineDirective(Token &DefineTok) { continue; } + if (Tok.is(tok::hashhash)) { + + // If we see token pasting, check if it looks like the gcc comma + // pasting extension. We'll use this information to suppress + // diagnostics later on. + + // Get the next token of the macro. + LexUnexpandedToken(Tok); + + if (Tok.is(tok::eod)) { + MI->AddTokenToBody(LastTok); + break; + } + + unsigned NumTokens = MI->getNumTokens(); + if (NumTokens && Tok.getIdentifierInfo() == Ident__VA_ARGS__ && + MI->getReplacementToken(NumTokens-1).is(tok::comma)) + MI->setHasCommaPasting(); + + // 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); + continue; + } + // Get the next token of the macro. LexUnexpandedToken(Tok); diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index eee4342e27..de98d5019d 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -619,9 +619,14 @@ MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName, // Varargs where the named vararg parameter is missing: OK as extension. // #define A(x, ...) // A("blah") - Diag(Tok, diag::ext_missing_varargs_arg); - Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + // + // If the macro contains the comma pasting extension, the diagnostic + // is suppressed; we know we'll get another diagnostic later. + if (!MI->hasCommaPasting()) { + Diag(Tok, diag::ext_missing_varargs_arg); + Diag(MI->getDefinitionLoc(), diag::note_macro_here) + << MacroName.getIdentifierInfo(); + } // Remember this occurred, allowing us to elide the comma when used for // cases like: diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index deba302e21..f64962e341 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1141,6 +1141,7 @@ void ASTReader::ReadMacroRecord(ModuleFile &F, uint64_t Offset, // Decode function-like macro info. bool isC99VarArgs = Record[NextIndex++]; bool isGNUVarArgs = Record[NextIndex++]; + bool hasCommaPasting = Record[NextIndex++]; MacroArgs.clear(); unsigned NumArgs = Record[NextIndex++]; for (unsigned i = 0; i != NumArgs; ++i) @@ -1150,6 +1151,7 @@ void ASTReader::ReadMacroRecord(ModuleFile &F, uint64_t Offset, MI->setIsFunctionLike(); if (isC99VarArgs) MI->setIsC99Varargs(); if (isGNUVarArgs) MI->setIsGNUVarargs(); + if (hasCommaPasting) MI->setHasCommaPasting(); MI->setArgumentList(MacroArgs.data(), MacroArgs.size(), PP.getPreprocessorAllocator()); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index a2e8b71123..a34244d3f3 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1855,6 +1855,7 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) { Record.push_back(MI->isC99Varargs()); Record.push_back(MI->isGNUVarargs()); + Record.push_back(MI->hasCommaPasting()); Record.push_back(MI->getNumArgs()); for (MacroInfo::arg_iterator I = MI->arg_begin(), E = MI->arg_end(); I != E; ++I) diff --git a/test/Preprocessor/macro_fn.c b/test/Preprocessor/macro_fn.c index f93d52c7ed..8ac9ed9a24 100644 --- a/test/Preprocessor/macro_fn.c +++ b/test/Preprocessor/macro_fn.c @@ -44,3 +44,9 @@ one_dot() /* empty first argument, elided ...: expected-warning {{must specify #define E() (i == 0) #if E #endif + + +/* */ +#define NSAssert(condition, desc, ...) /* expected-warning {{variadic macros are a C99 feature}} */ \ + SomeComplicatedStuff((desc), ##__VA_ARGS__) /* expected-warning {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} */ +NSAssert(somecond, somedesc)