aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/parser.cc
diff options
context:
space:
mode:
authorLewis Hyatt <lhyatt@gmail.com>2022-07-05 17:15:28 -0400
committerLewis Hyatt <lhyatt@gmail.com>2022-07-06 15:00:09 -0400
commite46f4d7430c5210465791603735ab219ef263c51 (patch)
treeb30bcd37b4da28ccf92c9c5d93886467cf95e123 /gcc/cp/parser.cc
parent208fbc779c713715da1465a1a2c6710c084c9b05 (diff)
downloadgcc-e46f4d7430c5210465791603735ab219ef263c51.zip
gcc-e46f4d7430c5210465791603735ab219ef263c51.tar.gz
gcc-e46f4d7430c5210465791603735ab219ef263c51.tar.bz2
diagnostics: Honor #pragma GCC diagnostic in the preprocessor [PR53431]
As discussed on PR c++/53431, currently, "#pragma GCC diagnostic" does not always take effect for diagnostics generated by libcpp. The reason is that libcpp itself does not interpret this pragma and only sends it on to the frontend, hence the pragma is only honored if the frontend arranges for it. The C frontend does process the pragma immediately (more or less) after seeing the token, so things work fine there. The PR points out that it doesn't work for C++, because the C++ frontend doesn't handle anything until it has read all the tokens from libcpp. The underlying problem is not C++-specific, though, and for instance, gcc -E has the same issue. This commit fixes the PR by adding the concept of an early pragma handler that can be registered by frontends, which gives them a chance to process diagnostic pragmas from libcpp before it is too late for them to take effect. The C++ and preprocess-only frontends are modified to use early pragmas and correct the behavior. gcc/c-family/ChangeLog: PR preprocessor/53920 PR c++/53431 * c-common.cc (c_option_is_from_cpp_diagnostics): New function. * c-common.h (c_option_is_from_cpp_diagnostics): Declare. (c_pp_stream_token): Declare. * c-ppoutput.cc (init_pp_output): Refactor logic about skipping pragmas to... (should_output_pragmas): ...here. New function. (token_streamer::stream): Support handling early pragmas. (do_line_change): Likewise. (c_pp_stream_token): New function. * c-pragma.cc (struct pragma_diagnostic_data): New helper class. (pragma_diagnostic_lex_normal): New function. Moved logic for interpreting GCC diagnostic pragmas here. (pragma_diagnostic_lex_pp): New function for parsing diagnostic pragmas directly from libcpp. (handle_pragma_diagnostic): Refactor into helper function... (handle_pragma_diagnostic_impl): ...here. New function. (handle_pragma_diagnostic_early): New function. (handle_pragma_diagnostic_early_pp): New function. (struct pragma_ns_name): Renamed to... (struct pragma_pp_data): ...this. Add new "early_handler" member. (c_register_pragma_1): Support early pragmas in the preprocessor. (c_register_pragma_with_early_handler): New function. (c_register_pragma): Support the new early handlers in struct internal_pragma_handler. (c_register_pragma_with_data): Likewise. (c_register_pragma_with_expansion): Likewise. (c_register_pragma_with_expansion_and_data): Likewise. (c_invoke_early_pragma_handler): New function. (c_pp_invoke_early_pragma_handler): New function. (init_pragma): Add early pragma support for diagnostic pragmas. * c-pragma.h (struct internal_pragma_handler): Add new early handler members. (c_register_pragma_with_early_handler): Declare. (c_invoke_early_pragma_handler): Declare. (c_pp_invoke_early_pragma_handler): Declare. gcc/cp/ChangeLog: PR c++/53431 * parser.cc (cp_parser_pragma_kind): Move earlier in the file. (cp_lexer_handle_early_pragma): New function. (cp_lexer_new_main): Support parsing and handling early pragmas. (c_parse_file): Adapt to changes in cp_lexer_new_main. gcc/testsuite/ChangeLog: PR preprocessor/53920 PR c++/53431 * c-c++-common/pragma-diag-11.c: New test. * c-c++-common/pragma-diag-12.c: New test. * c-c++-common/pragma-diag-13.c: New test.
Diffstat (limited to 'gcc/cp/parser.cc')
-rw-r--r--gcc/cp/parser.cc89
1 files changed, 64 insertions, 25 deletions
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 5cd6a52..bf9ea36 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -639,8 +639,61 @@ cp_token_is_module_directive (cp_token *token)
|| token->keyword == RID__IMPORT;
}
+/* Return TOKEN's pragma_kind if it is CPP_PRAGMA, otherwise
+ PRAGMA_NONE. */
+
+static enum pragma_kind
+cp_parser_pragma_kind (cp_token *token)
+{
+ if (token->type != CPP_PRAGMA)
+ return PRAGMA_NONE;
+ /* We smuggled the cpp_token->u.pragma value in an INTEGER_CST. */
+ return (enum pragma_kind) TREE_INT_CST_LOW (token->u.value);
+}
+
+/* Handle early pragmas such as #pragma GCC diagnostic, which needs to be done
+ during preprocessing for the case of preprocessing-related diagnostics. This
+ is called immediately after pushing the CPP_PRAGMA_EOL token onto
+ lexer->buffer. */
+
+static void
+cp_lexer_handle_early_pragma (cp_lexer *lexer)
+{
+ const auto first_token = lexer->buffer->address ();
+ const auto last_token = first_token + lexer->buffer->length () - 1;
+
+ /* Back up to the start of the pragma so pragma_lex () can parse it when
+ c-pragma lib asks it to. */
+ auto begin = last_token;
+ gcc_assert (begin->type == CPP_PRAGMA_EOL);
+ while (begin->type != CPP_PRAGMA)
+ {
+ if (cp_token_is_module_directive (begin))
+ return;
+ gcc_assert (begin != first_token);
+ --begin;
+ }
+ gcc_assert (!lexer->next_token);
+ gcc_assert (!lexer->last_token);
+ lexer->next_token = begin;
+ lexer->last_token = last_token;
+
+ /* Dispatch it. */
+ const unsigned int id
+ = cp_parser_pragma_kind (cp_lexer_consume_token (lexer));
+ if (id >= PRAGMA_FIRST_EXTERNAL)
+ c_invoke_early_pragma_handler (id);
+
+ /* Reset to normal state. */
+ lexer->next_token = lexer->last_token = nullptr;
+}
+
+/* The parser. */
+static cp_parser *cp_parser_new (cp_lexer *);
+static GTY (()) cp_parser *the_parser;
+
/* Create a new main C++ lexer, the lexer that gets tokens from the
- preprocessor. */
+ preprocessor, and also create the main parser. */
static cp_lexer *
cp_lexer_new_main (void)
@@ -662,6 +715,10 @@ cp_lexer_new_main (void)
if (modules_p ())
filter = module_token_cdtor (parse_in, filter);
+ /* Create the parser now, so we can use it to handle early pragmas. */
+ gcc_assert (!the_parser);
+ the_parser = cp_parser_new (lexer);
+
/* Get the remaining tokens from the preprocessor. */
while (tok->type != CPP_EOF)
{
@@ -669,6 +726,11 @@ cp_lexer_new_main (void)
/* Process the previous token. */
module_token_lang (tok->type, tok->keyword, tok->u.value,
tok->location, filter);
+
+ /* Check for early pragmas that need to be handled now. */
+ if (tok->type == CPP_PRAGMA_EOL)
+ cp_lexer_handle_early_pragma (lexer);
+
tok = vec_safe_push (lexer->buffer, cp_token ());
cp_lexer_get_preprocessor_token (C_LEX_STRING_NO_JOIN, tok);
}
@@ -2131,11 +2193,6 @@ pop_unparsed_function_queues (cp_parser *parser)
/* Prototypes. */
-/* Constructors and destructors. */
-
-static cp_parser *cp_parser_new
- (cp_lexer *);
-
/* Routines to parse various constructs.
Those that return `tree' will return the error_mark_node (rather
@@ -2898,18 +2955,6 @@ cp_parser_is_keyword (cp_token* token, enum rid keyword)
return token->keyword == keyword;
}
-/* Return TOKEN's pragma_kind if it is CPP_PRAGMA, otherwise
- PRAGMA_NONE. */
-
-static enum pragma_kind
-cp_parser_pragma_kind (cp_token *token)
-{
- if (token->type != CPP_PRAGMA)
- return PRAGMA_NONE;
- /* We smuggled the cpp_token->u.pragma value in an INTEGER_CST. */
- return (enum pragma_kind) TREE_INT_CST_LOW (token->u.value);
-}
-
/* Helper function for cp_parser_error.
Having peeked a token of kind TOK1_KIND that might signify
a conflict marker, peek successor tokens to determine
@@ -47936,11 +47981,7 @@ cp_parser_transaction_cancel (cp_parser *parser)
return stmt;
}
-/* The parser. */
-
-static GTY (()) cp_parser *the_parser;
-
/* Special handling for the first token or line in the file. The first
thing in the file might be #pragma GCC pch_preprocess, which loads a
PCH file, which is a GC collection point. So we need to handle this
@@ -48435,9 +48476,7 @@ c_parse_file (void)
/* cp_lexer_new_main is called before doing any GC allocation
because tokenization might load a PCH file. */
- cp_lexer *lexer = cp_lexer_new_main ();
-
- the_parser = cp_parser_new (lexer);
+ cp_lexer_new_main ();
cp_parser_translation_unit (the_parser);
class_decl_loc_t::diag_mismatched_tags ();