When an initializer list is used within a marco argument, each comma is the
list is treated as a macro argument separator. When this happens, the only
error message is "too many macro arguments provided". This patch changes the
handling of function-like macros to give additional notes detailing the
problems.
For a macro FOO(vector<int>{1,2,3}, 3), it will suggest parenthesis to preserve
the initializer list with the corrected code as FOO((vector<int>{1,2,3}), 3).
For a macro such as FOO( {1,2,3} ), it will provide a different note saying
that initializer lists cannot be used as macro arguments. This is because
adding parentheses around the braces would make it no longer an initializer
list.
http://llvm-reviews.chandlerc.com/D986
Files:
lib/Lex/PPMacroExpansion.cpp
test/Preprocessor/macro_with_initializer_list.cpp
include/clang/Basic/DiagnosticLexKinds.td
include/clang/Lex/Preprocessor.h
Index: lib/Lex/PPMacroExpansion.cpp
===================================================================
--- lib/Lex/PPMacroExpansion.cpp
+++ lib/Lex/PPMacroExpansion.cpp
@@ -397,89 +397,235 @@
MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName,
MacroInfo *MI,
SourceLocation &MacroEnd) {
- // The number of fixed arguments to parse.
- unsigned NumFixedArgsLeft = MI->getNumArgs();
- bool isVariadic = MI->isVariadic();
-
- // Outer loop, while there are more arguments, keep reading them.
Token Tok;
// Read arguments as unexpanded tokens. This avoids issues, e.g., where
// an argument value in a macro could expand to ',' or '(' or ')'.
LexUnexpandedToken(Tok);
assert(Tok.is(tok::l_paren) && "Error computing l-paren-ness?");
+ SmallVector<Token, 64> MacroTokens;
+ MacroTokens.push_back(Tok);
+ unsigned Parens = 1; // from the paren already lexed
+ bool HasCodeCompletion = false;
+ while (Tok.isNot(tok::r_paren) || Parens != 0) {
+ LexUnexpandedToken(Tok);
+ if (Tok.is(tok::eof) || Tok.is(tok::eod)) {
+ MacroTokens.push_back(Tok);
+ break;
+ } else if (Tok.is(tok::l_paren)) {
+ ++Parens;
+ } else if (Tok.is(tok::r_paren)) {
+ --Parens;
+ } else if (Tok.is(tok::code_completion)) {
+ HasCodeCompletion = true;
+ } else if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
+ // Reading macro arguments can cause macros that we are currently
+ // expanding from to be popped off the expansion stack. Doing so causes
+ // them to be reenabled for expansion. Here we record whether any
+ // identifiers we lex as macro arguments correspond to disabled macros.
+ // If so, we mark the token as noexpand. This is a subtle aspect of
+ // C99 6.10.3.4p2.
+ if (MacroInfo *MI = getMacroInfo(II))
+ if (!MI->isEnabled())
+ Tok.setFlag(Token::DisableExpand);
+ }
+ MacroTokens.push_back(Tok);
+ }
+
+ if (Tok.is(tok::r_paren))
+ MacroEnd = Tok.getLocation();
+
+ MacroArgs *MA = ProcessFunctionLikeMacroArgTokens(MacroTokens, MacroName, MI,
+ /*EmitErrors*/true);
+
+ if (MA) return MA;
+ if (HasCodeCompletion) return 0;
+ if (MacroTokens.back().isNot(tok::r_paren)) return 0;
+
+ // Checks that braces and parens are properly nested. If not, return.
+ SmallVector<bool, 8> IsParen;
+ for (SmallVector<Token, 64>::iterator TokenIterator = MacroTokens.begin(),
+ TokenEnd = MacroTokens.end();
+ TokenIterator != TokenEnd; ++TokenIterator) {
+ if (TokenIterator->is(tok::l_paren)) {
+ IsParen.push_back(true);
+ } else if (TokenIterator->is(tok::r_paren)) {
+ if (IsParen.empty() || !IsParen.back())
+ return 0;
+ IsParen.pop_back();
+ } else if (TokenIterator->is(tok::l_brace)) {
+ IsParen.push_back(false);
+ } else if (TokenIterator->is(tok::r_brace)) {
+ if (IsParen.empty() || IsParen.back())
+ return 0;
+ IsParen.pop_back();
+ }
+ }
+ if (!IsParen.empty()) return 0;
+
+ Parens = 0;
+ unsigned Braces = 0;
+ bool FoundComma = false;
+ bool FoundBrace = false;
+
+ // Attempt braced list correction.
+ SmallVector<Token, 64> CorrectedMacroTokens;
+ SmallVector<SourceRange, 4> ParenHints;
+ SmallVector<SourceRange, 4> InitLists;
+ for (SmallVector<Token, 64>::iterator TokenIterator = MacroTokens.begin(),
+ TokenEnd = MacroTokens.end(),
+ ArgStart;
+ TokenIterator != TokenEnd; ++TokenIterator) {
+
+ // Update brace and paren counts.
+ if (TokenIterator->is(tok::l_paren)) {
+ ++Parens;
+ } else if (TokenIterator->is(tok::r_paren)) {
+ --Parens;
+ } else if (TokenIterator->is(tok::l_brace)) {
+ ++Braces;
+ if (Parens == 1)
+ FoundBrace = true;
+ } else if (TokenIterator->is(tok::r_brace)) {
+ --Braces;
+ } else if (TokenIterator->is(tok::comma)) {
+ if (Braces > 0 && Parens == 1)
+ FoundComma = true;
+ }
+
+ if ((Braces == 0 && Parens == 1 && TokenIterator->is(tok::comma)) ||
+ (Braces == 0 && Parens == 0 && TokenIterator->is(tok::r_paren))) {
+ // End of argument.
+ if (!FoundComma || !FoundBrace) {
+ CorrectedMacroTokens.insert(CorrectedMacroTokens.end(),
+ ArgStart, TokenIterator);
+ } else if (ArgStart->is(tok::l_brace) &&
+ (TokenIterator - 1)->is(tok::r_brace)) {
+ InitLists.push_back(SourceRange(ArgStart->getLocation(),
+ (TokenIterator - 1)->getLocation()));
+ } else {
+ ParenHints.push_back(SourceRange(ArgStart->getLocation(),
+ TokenIterator->getLocation()));
+ CorrectedMacroTokens.push_back(MacroTokens.front());
+ CorrectedMacroTokens.insert(CorrectedMacroTokens.end(),
+ ArgStart, TokenIterator);
+ CorrectedMacroTokens.push_back(MacroTokens.back());
+ }
+ }
+
+ if (Braces == 0 && Parens == 1 &&
+ (TokenIterator->is(tok::comma) || TokenIterator->is(tok::l_paren))) {
+ // Start of argument.
+ ArgStart = TokenIterator + 1;
+ FoundComma = false;
+ FoundBrace = false;
+ CorrectedMacroTokens.push_back(*TokenIterator);
+ }
+ }
+ CorrectedMacroTokens.push_back(MacroTokens.back());
+
+ if (!InitLists.empty()) {
+ DiagnosticBuilder DB =
+ Diag(MacroName, diag::note_cannot_use_init_list_as_macro_argument);
+ for (SmallVector<SourceRange, 4>::iterator
+ Range = InitLists.begin(), RangeEnd = InitLists.end();
+ Range != RangeEnd; ++Range)
+ DB << *Range;
+
+ return 0;
+ }
+
+ if (ParenHints.empty()) return 0;
+ MA = ProcessFunctionLikeMacroArgTokens(CorrectedMacroTokens, MacroName, MI,
+ /*EmitErrors*/false);
+
+ if (!MA) return 0;
+
+ DiagnosticBuilder DB = Diag(MacroName, diag::note_suggest_parens_for_macro);
+ for (SmallVector<SourceRange, 4>::iterator
+ ParenLocation = ParenHints.begin(), ParenEnd = ParenHints.end();
+ ParenLocation != ParenEnd; ++ParenLocation) {
+ DB << FixItHint::CreateInsertion(ParenLocation->getBegin(), "(")
+ << FixItHint::CreateInsertion(ParenLocation->getEnd(), ")");
+ }
+
+ return MA;
+}
+
+MacroArgs *Preprocessor::ProcessFunctionLikeMacroArgTokens(
+ const SmallVector<Token, 64> &MacroTokens, Token &MacroName,
+ MacroInfo *MI, bool EmitErrors) {
+
+ // The number of fixed arguments to parse.
+ unsigned NumFixedArgsLeft = MI->getNumArgs();
+ bool isVariadic = MI->isVariadic();
+
// ArgTokens - Build up a list of tokens that make up each argument. Each
// argument is separated by an EOF token. Use a SmallVector so we can avoid
// heap allocations in the common case.
SmallVector<Token, 64> ArgTokens;
bool ContainsCodeCompletionTok = false;
unsigned NumActuals = 0;
- while (Tok.isNot(tok::r_paren)) {
- if (ContainsCodeCompletionTok && (Tok.is(tok::eof) || Tok.is(tok::eod)))
+ SmallVector<Token, 64>::const_iterator TokIter = MacroTokens.begin();
+ while (TokIter->isNot(tok::r_paren)) {
+ if (ContainsCodeCompletionTok &&
+ (TokIter->is(tok::eof) || TokIter->is(tok::eod)))
break;
- assert((Tok.is(tok::l_paren) || Tok.is(tok::comma)) &&
+ assert((TokIter->is(tok::l_paren) || TokIter->is(tok::comma)) &&
"only expect argument separators here");
unsigned ArgTokenStart = ArgTokens.size();
- SourceLocation ArgStartLoc = Tok.getLocation();
+ SourceLocation ArgStartLoc = TokIter->getLocation();
// C99 6.10.3p11: Keep track of the number of l_parens we have seen. Note
// that we already consumed the first one.
unsigned NumParens = 0;
while (1) {
// Read arguments as unexpanded tokens. This avoids issues, e.g., where
// an argument value in a macro could expand to ',' or '(' or ')'.
- LexUnexpandedToken(Tok);
+ ++TokIter;
- if (Tok.is(tok::eof) || Tok.is(tok::eod)) { // "#if f(<eof>" & "#if f(\n"
+ if (TokIter->is(tok::eof) || TokIter->is(tok::eod)) {
+ // "#if f(<eof>" & "#if f(\n"
if (!ContainsCodeCompletionTok) {
- Diag(MacroName, diag::err_unterm_macro_invoc);
- Diag(MI->getDefinitionLoc(), diag::note_macro_here)
- << MacroName.getIdentifierInfo();
+ if (EmitErrors) {
+ Diag(MacroName, diag::err_unterm_macro_invoc);
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here)
+ << MacroName.getIdentifierInfo();
+ }
// Do not lose the EOF/EOD. Return it to the client.
- MacroName = Tok;
+ MacroName = *TokIter;
return 0;
} else {
// Do not lose the EOF/EOD.
Token *Toks = new Token[1];
- Toks[0] = Tok;
+ Toks[0] = *TokIter;
EnterTokenStream(Toks, 1, true, true);
break;
}
- } else if (Tok.is(tok::r_paren)) {
+ } else if (TokIter->is(tok::r_paren)) {
// If we found the ) token, the macro arg list is done.
if (NumParens-- == 0) {
- MacroEnd = Tok.getLocation();
break;
}
- } else if (Tok.is(tok::l_paren)) {
+ } else if (TokIter->is(tok::l_paren)) {
++NumParens;
- } else if (Tok.is(tok::comma) && NumParens == 0) {
+ } else if (TokIter->is(tok::comma) && NumParens == 0) {
// Comma ends this argument if there are more fixed arguments expected.
// However, if this is a variadic macro, and this is part of the
// variadic part, then the comma is just an argument token.
if (!isVariadic) break;
if (NumFixedArgsLeft > 1)
break;
- } else if (Tok.is(tok::comment) && !KeepMacroComments) {
+ } else if (TokIter->is(tok::comment) && !KeepMacroComments) {
// If this is a comment token in the argument list and we're just in
// -C mode (not -CC mode), discard the comment.
continue;
- } else if (Tok.getIdentifierInfo() != 0) {
- // Reading macro arguments can cause macros that we are currently
- // expanding from to be popped off the expansion stack. Doing so causes
- // them to be reenabled for expansion. Here we record whether any
- // identifiers we lex as macro arguments correspond to disabled macros.
- // If so, we mark the token as noexpand. This is a subtle aspect of
- // C99 6.10.3.4p2.
- if (MacroInfo *MI = getMacroInfo(Tok.getIdentifierInfo()))
- if (!MI->isEnabled())
- Tok.setFlag(Token::DisableExpand);
- } else if (Tok.is(tok::code_completion)) {
+ } else if (TokIter->is(tok::code_completion)) {
ContainsCodeCompletionTok = true;
if (CodeComplete)
CodeComplete->CodeCompleteMacroArgument(MacroName.getIdentifierInfo(),
@@ -489,12 +635,12 @@
// code-completion callback.
}
- ArgTokens.push_back(Tok);
+ ArgTokens.push_back(*TokIter);
}
// If this was an empty argument list foo(), don't add this as an empty
// argument.
- if (ArgTokens.empty() && Tok.getKind() == tok::r_paren)
+ if (ArgTokens.empty() && TokIter->getKind() == tok::r_paren)
break;
// If this is not a variadic macro, and too many args were specified, emit
@@ -504,27 +650,30 @@
ArgStartLoc = ArgTokens[ArgTokenStart].getLocation();
if (!ContainsCodeCompletionTok) {
- // Emit the diagnostic at the macro name in case there is a missing ).
- // Emitting it at the , could be far away from the macro name.
- Diag(ArgStartLoc, diag::err_too_many_args_in_macro_invoc);
- Diag(MI->getDefinitionLoc(), diag::note_macro_here)
- << MacroName.getIdentifierInfo();
+ if (EmitErrors) {
+ // Emit the diagnostic at the macro name in case there is a missing ).
+ // Emitting it at the , could be far away from the macro name.
+ Diag(ArgStartLoc, diag::err_too_many_args_in_macro_invoc);
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here)
+ << MacroName.getIdentifierInfo();
+ }
return 0;
}
}
- // Empty arguments are standard in C99 and C++0x, and are supported as an extension in
- // other modes.
+ // Empty arguments are standard in C99 and C++0x, and are supported as an
+ // extension in other modes.
if (ArgTokens.size() == ArgTokenStart && !LangOpts.C99)
- Diag(Tok, LangOpts.CPlusPlus11 ?
- diag::warn_cxx98_compat_empty_fnmacro_arg :
- diag::ext_empty_fnmacro_arg);
+ if (EmitErrors)
+ Diag(*TokIter, LangOpts.CPlusPlus11 ?
+ diag::warn_cxx98_compat_empty_fnmacro_arg :
+ diag::ext_empty_fnmacro_arg);
// Add a marker EOF token to the end of the token list for this argument.
Token EOFTok;
EOFTok.startToken();
EOFTok.setKind(tok::eof);
- EOFTok.setLocation(Tok.getLocation());
+ EOFTok.setLocation(TokIter->getLocation());
EOFTok.setLength(0);
ArgTokens.push_back(EOFTok);
++NumActuals;
@@ -546,7 +695,7 @@
Token EOFTok;
EOFTok.startToken();
EOFTok.setKind(tok::eof);
- EOFTok.setLocation(Tok.getLocation());
+ EOFTok.setLocation(TokIter->getLocation());
EOFTok.setLength(0);
for (; NumActuals < MinArgsExpected; ++NumActuals)
ArgTokens.push_back(EOFTok);
@@ -571,9 +720,11 @@
// 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();
+ if (EmitErrors) {
+ Diag(*TokIter, 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
@@ -584,15 +735,18 @@
// A(x) B(x) C()
isVarargsElided = true;
} else if (!ContainsCodeCompletionTok) {
- // Otherwise, emit the error.
- Diag(Tok, diag::err_too_few_args_in_macro_invoc);
- Diag(MI->getDefinitionLoc(), diag::note_macro_here)
- << MacroName.getIdentifierInfo();
+ if (EmitErrors) {
+ // Otherwise, emit the error.
+ Diag(*TokIter, diag::err_too_few_args_in_macro_invoc);
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here)
+ << MacroName.getIdentifierInfo();
+ }
return 0;
}
// Add a marker EOF token to the end of the token list for this argument.
- SourceLocation EndLoc = Tok.getLocation();
+ Token Tok = *TokIter;
+ SourceLocation EndLoc = TokIter->getLocation();
Tok.startToken();
Tok.setKind(tok::eof);
Tok.setLocation(EndLoc);
@@ -605,11 +759,13 @@
} else if (NumActuals > MinArgsExpected && !MI->isVariadic() &&
!ContainsCodeCompletionTok) {
- // Emit the diagnostic at the macro name in case there is a missing ).
- // Emitting it at the , could be far away from the macro name.
- Diag(MacroName, diag::err_too_many_args_in_macro_invoc);
- Diag(MI->getDefinitionLoc(), diag::note_macro_here)
- << MacroName.getIdentifierInfo();
+ if (EmitErrors) {
+ // Emit the diagnostic at the macro name in case there is a missing ).
+ // Emitting it at the , could be far away from the macro name.
+ Diag(MacroName, diag::err_too_many_args_in_macro_invoc);
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here)
+ << MacroName.getIdentifierInfo();
+ }
return 0;
}
Index: test/Preprocessor/macro_with_initializer_list.cpp
===================================================================
--- test/Preprocessor/macro_with_initializer_list.cpp
+++ test/Preprocessor/macro_with_initializer_list.cpp
@@ -0,0 +1,85 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+namespace std {
+ template <class X>
+ class initializer_list {
+ public:
+ initializer_list();
+ };
+}
+
+class Foo {
+public:
+ Foo();
+ Foo(std::initializer_list<int>);
+ bool operator==(const Foo);
+};
+
+#define EQ(x,y) (void)(x == y) // expected-note 3{{defined here}}
+void test_EQ() {
+ Foo F;
+ F = Foo{1,2};
+
+ EQ(F,F);
+ EQ(F,Foo());
+ EQ(F,Foo({1,2,3}));
+ EQ(Foo({1,2,3}),F);
+ EQ((Foo{1,2,3}),(Foo{1,2,3}));
+
+ EQ(F,Foo{1,2,3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ EQ(Foo{1,2,3},F);
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ EQ(Foo{1,2,3},Foo{1,2,3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+}
+
+#define NE(x,y) (void)(x != y) // expected-note 3{{defined here}}
+// Operator != isn't defined. This tests that the corrected macro arguments
+// are used in the macro expansion.
+void test_NE() {
+ Foo F;
+
+ NE(F,F);
+ // expected-error@-1 {{invalid operands}}
+ NE(F,Foo());
+ // expected-error@-1 {{invalid operands}}
+ NE(F,Foo({1,2,3}));
+ // expected-error@-1 {{invalid operands}}
+ NE((Foo{1,2,3}),(Foo{1,2,3}));
+ // expected-error@-1 {{invalid operands}}
+
+ NE(F,Foo{1,2,3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ // expected-error@-3 {{invalid operands}}
+ NE(Foo{1,2,3},F);
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ // expected-error@-3 {{invalid operands}}
+ NE(Foo{1,2,3},Foo{1,2,3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ // expected-error@-3 {{invalid operands}}
+}
+
+#define INIT(var, init) Foo var = init; // expected-note 2{{defined here}}
+// Can't use an initializer list as a macro argument. The commas in the list
+// will be interpretted as argument separaters and adding parenthesis will
+// make it no longer an initializer list.
+void test() {
+ INIT(a, Foo());
+ INIT(b, Foo({1, 2, 3}));
+ INIT(c, Foo());
+
+ INIT(d, Foo{1, 2, 3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{require parenthesis}}
+ INIT(e, {1, 2, 3});
+ // expected-error@-1 {{too many arguments provided}}
+ // expected-note@-2 {{cannot use initializer list as macro argument}}
+ // expected-error@-3 {{use of undeclared identifier}}
+}
Index: include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- include/clang/Basic/DiagnosticLexKinds.td
+++ include/clang/Basic/DiagnosticLexKinds.td
@@ -460,6 +460,10 @@
"unterminated function-like macro invocation">;
def err_too_many_args_in_macro_invoc : Error<
"too many arguments provided to function-like macro invocation">;
+def note_suggest_parens_for_macro : Note<
+ "braced lists require parenthesis to be recognized inside macros">;
+def note_cannot_use_init_list_as_macro_argument : Note<
+ "cannot use initializer list as macro argument">;
def err_too_few_args_in_macro_invoc : Error<
"too few arguments provided to function-like macro invocation">;
def err_pp_bad_paste : Error<
Index: include/clang/Lex/Preprocessor.h
===================================================================
--- include/clang/Lex/Preprocessor.h
+++ include/clang/Lex/Preprocessor.h
@@ -1364,6 +1364,14 @@
MacroArgs *ReadFunctionLikeMacroArgs(Token &MacroName, MacroInfo *MI,
SourceLocation &ExpansionEnd);
+ /// ProcessFunctionLikeMacroArgTokens - Helper function to
+ /// ReadFunctionLikeMacroArgs. Thie does the actual processing of the tokens,
+ /// and can be used to validate correctness of tokens added to the
+ /// macro arguments.
+ MacroArgs *ProcessFunctionLikeMacroArgTokens(
+ const SmallVector<Token, 64> &MacroTokens, Token &MacroName,
+ MacroInfo *MI, bool EmitErrors);
+
/// ExpandBuiltinMacro - If an identifier token is read that is to be expanded
/// as a builtin macro, handle it and return the next token as 'Tok'.
void ExpandBuiltinMacro(Token &Tok);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits