diff options
Diffstat (limited to 'libcpp/macro.c')
-rw-r--r-- | libcpp/macro.c | 170 |
1 files changed, 168 insertions, 2 deletions
diff --git a/libcpp/macro.c b/libcpp/macro.c index fab1cb0..bf473ea 100644 --- a/libcpp/macro.c +++ b/libcpp/macro.c @@ -89,6 +89,155 @@ struct macro_arg_saved_data { union _cpp_hashnode_value value; }; +static const char *vaopt_paste_error = + N_("'##' cannot appear at either end of __VA_OPT__"); + +/* A class for tracking __VA_OPT__ state while iterating over a + sequence of tokens. This is used during both macro definition and + expansion. */ +class vaopt_state { + + public: + + /* Initialize the state tracker. ANY_ARGS is true if variable + arguments were provided to the macro invocation. */ + vaopt_state (cpp_reader *pfile, bool is_variadic, bool any_args) + : m_pfile (pfile), + m_allowed (any_args), + m_variadic (is_variadic), + m_state (0), + m_last_was_paste (false), + m_paste_location (0), + m_location (0) + { + } + + enum update_type + { + ERROR, + DROP, + INCLUDE + }; + + /* Given a token, update the state of this tracker and return a + boolean indicating whether the token should be be included in the + expansion. */ + update_type update (const cpp_token *token) + { + /* If the macro isn't variadic, just don't bother. */ + if (!m_variadic) + return INCLUDE; + + if (token->type == CPP_NAME + && token->val.node.node == m_pfile->spec_nodes.n__VA_OPT__) + { + if (m_state > 0) + { + cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc, + "__VA_OPT__ may not appear in a __VA_OPT__"); + return ERROR; + } + ++m_state; + m_location = token->src_loc; + return DROP; + } + else if (m_state == 1) + { + if (token->type != CPP_OPEN_PAREN) + { + cpp_error_at (m_pfile, CPP_DL_ERROR, m_location, + "__VA_OPT__ must be followed by an " + "open parenthesis"); + return ERROR; + } + ++m_state; + return DROP; + } + else if (m_state >= 2) + { + if (m_state == 2 && token->type == CPP_PASTE) + { + cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc, + vaopt_paste_error); + return ERROR; + } + /* Advance states before further considering this token, in + case we see a close paren immediately after the open + paren. */ + if (m_state == 2) + ++m_state; + + bool was_paste = m_last_was_paste; + m_last_was_paste = false; + if (token->type == CPP_PASTE) + { + m_last_was_paste = true; + m_paste_location = token->src_loc; + } + else if (token->type == CPP_OPEN_PAREN) + ++m_state; + else if (token->type == CPP_CLOSE_PAREN) + { + --m_state; + if (m_state == 2) + { + /* Saw the final paren. */ + m_state = 0; + + if (was_paste) + { + cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc, + vaopt_paste_error); + return ERROR; + } + + return DROP; + } + } + return m_allowed ? INCLUDE : DROP; + } + + /* Nothing to do with __VA_OPT__. */ + return INCLUDE; + } + + /* Ensure that any __VA_OPT__ was completed. If ok, return true. + Otherwise, issue an error and return false. */ + bool completed () + { + if (m_variadic && m_state != 0) + cpp_error_at (m_pfile, CPP_DL_ERROR, m_location, + "unterminated __VA_OPT__"); + return m_state == 0; + } + + private: + + /* The cpp_reader. */ + cpp_reader *m_pfile; + + /* True if there were varargs. */ + bool m_allowed; + /* True if the macro is variadic. */ + bool m_variadic; + + /* The state variable: + 0 means not parsing + 1 means __VA_OPT__ seen, looking for "(" + 2 means "(" seen (so the next token can't be "##") + >= 3 means looking for ")", the number encodes the paren depth. */ + int m_state; + + /* If true, the previous token was ##. This is used to detect when + a paste occurs at the end of the sequence. */ + bool m_last_was_paste; + /* The location of the paste token. */ + source_location m_paste_location; + + /* Location of the __VA_OPT__ token. */ + source_location m_location; +}; + /* Macro expansion. */ static int enter_macro_context (cpp_reader *, cpp_hashnode *, @@ -776,7 +925,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node if (argc < macro->paramc) { - /* As an extension, variadic arguments are allowed to not appear in + /* In C++2a (here the va_opt flag is used), and also as a GNU + extension, variadic arguments are allowed to not appear in the invocation at all. e.g. #define debug(format, args...) something debug("string"); @@ -786,7 +936,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node if (argc + 1 == macro->paramc && macro->variadic) { - if (CPP_PEDANTIC (pfile) && ! macro->syshdr) + if (CPP_PEDANTIC (pfile) && ! macro->syshdr + && ! CPP_OPTION (pfile, va_opt)) { if (CPP_OPTION (pfile, cplusplus)) cpp_error (pfile, CPP_DL_PEDWARN, @@ -1678,6 +1829,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, num_macro_tokens); } i = 0; + vaopt_state vaopt_tracker (pfile, macro->variadic, + args[macro->paramc - 1].count > 0); for (src = macro->exp.tokens; src < limit; src++) { unsigned int arg_tokens_count; @@ -1685,6 +1838,10 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, const cpp_token **paste_flag = NULL; const cpp_token **tmp_token_ptr; + /* __VA_OPT__ handling. */ + if (vaopt_tracker.update (src) != vaopt_state::INCLUDE) + continue; + if (src->type != CPP_MACRO_ARG) { /* Allocate a virtual location for token SRC, and add that @@ -3076,6 +3233,9 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro) *token = *ctoken; } + /* The argument doesn't matter here. */ + vaopt_state vaopt_tracker (pfile, macro->variadic, true); + for (;;) { /* Check the stringifying # constraint 6.10.3.2.1 of @@ -3144,10 +3304,16 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro) } } + if (vaopt_tracker.update (token) == vaopt_state::ERROR) + return false; + following_paste_op = (token->type == CPP_PASTE); token = lex_expansion_token (pfile, macro); } + if (!vaopt_tracker.completed ()) + return false; + macro->exp.tokens = (cpp_token *) BUFF_FRONT (pfile->a_buff); macro->traditional = 0; |