@@ -6594,6 +6594,20 @@ c_option_controlling_cpp_diagnostic (enum cpp_warning_reason reason)
return 0;
}
+/* Return TRUE if the given option index corresponds to a diagnostic
+ issued by libcpp. Linear search seems fine for now. */
+bool
+c_option_is_from_cpp_diagnostics (int option_index)
+{
+ for (auto entry = cpp_reason_option_codes; entry->reason != CPP_W_NONE;
+ ++entry)
+ {
+ if (entry->option_code == option_index)
+ return true;
+ }
+ return false;
+}
+
/* Callback from cpp_diagnostic for PFILE to print diagnostics from the
preprocessor. The diagnostic is of type LEVEL, with REASON set
to the reason code if LEVEL is represents a warning, at location
@@ -907,6 +907,7 @@ extern tree fold_for_warn (tree);
extern tree c_common_get_narrower (tree, int *);
extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
extern void c_common_finalize_early_debug (void);
+extern bool c_option_is_from_cpp_diagnostics (int);
/* Used by convert_and_check; in front ends. */
extern tree convert_init (tree, tree);
@@ -1187,6 +1188,7 @@ extern void preprocess_file (cpp_reader *);
extern void pp_file_change (const line_map_ordinary *);
extern void pp_dir_change (cpp_reader *, const char *);
extern bool check_missing_format_attribute (tree, tree);
+extern void c_pp_output_diagnostic_pragma (const char *, const char *);
/* In c-omp.c */
typedef wide_int_bitmask omp_clause_mask;
@@ -110,6 +110,14 @@ preprocess_file (cpp_reader *pfile)
putc ('\n', print.outf);
}
+/* Don't emit #pragma or #ident directives if we are processing
+ assembly language; the assembler may choke on them. */
+static bool
+should_output_pragmas ()
+{
+ return cpp_get_options (parse_in)->lang != CLK_ASM;
+}
+
/* Set up the callbacks as appropriate. */
void
init_pp_output (FILE *out_stream)
@@ -121,7 +129,7 @@ init_pp_output (FILE *out_stream)
cb->line_change = cb_line_change;
/* Don't emit #pragma or #ident directives if we are processing
assembly language; the assembler may choke on them. */
- if (cpp_get_options (parse_in)->lang != CLK_ASM)
+ if (should_output_pragmas ())
{
cb->ident = cb_ident;
cb->def_pragma = cb_def_pragma;
@@ -173,6 +181,7 @@ class token_streamer
bool avoid_paste;
bool do_line_adjustments;
bool in_pragma;
+ bool skip_this_pragma;
bool line_marker_emitted;
public:
@@ -181,6 +190,7 @@ class token_streamer
do_line_adjustments (cpp_get_options (pfile)->lang != CLK_ASM
&& !flag_no_line_commands),
in_pragma (false),
+ skip_this_pragma (false),
line_marker_emitted (false)
{
}
@@ -188,6 +198,7 @@ class token_streamer
void begin_pragma ()
{
in_pragma = true;
+ skip_this_pragma = false;
}
void stream (cpp_reader *pfile, const cpp_token *tok, location_t);
@@ -235,7 +246,7 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
print.printed = true;
}
}
- else if (token->flags & PREV_WHITE)
+ else if (token->flags & PREV_WHITE && token->type != CPP_PRAGMA)
{
unsigned src_line = LOCATION_LINE (loc);
@@ -252,23 +263,32 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
print.prev = token;
if (token->type == CPP_PRAGMA)
{
- const char *space;
- const char *name;
-
- line_marker_emitted = maybe_print_line (token->src_loc);
- fputs ("#pragma ", print.outf);
- c_pp_lookup_pragma (token->val.pragma, &space, &name);
- if (space)
- fprintf (print.outf, "%s %s", space, name);
- else
- fprintf (print.outf, "%s", name);
- print.printed = true;
in_pragma = true;
+ if (should_output_pragmas ())
+ {
+ const char *space;
+ const char *name;
+
+ line_marker_emitted = maybe_print_line (token->src_loc);
+ fputs ("#pragma ", print.outf);
+ c_pp_lookup_pragma (token->val.pragma, &space, &name);
+ if (space)
+ fprintf (print.outf, "%s %s", space, name);
+ else
+ fprintf (print.outf, "%s", name);
+ print.printed = true;
+ skip_this_pragma = false;
+ }
+ else
+ skip_this_pragma = true;
+ if (token->val.pragma >= PRAGMA_FIRST_EXTERNAL)
+ c_pp_invoke_early_pragma_handler (token->val.pragma);
}
else if (token->type == CPP_PRAGMA_EOL)
{
- maybe_print_line (UNKNOWN_LOCATION);
- in_pragma = false;
+ if (!skip_this_pragma)
+ maybe_print_line (UNKNOWN_LOCATION);
+ in_pragma = skip_this_pragma = false;
}
else
{
@@ -287,9 +307,12 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
do_line_change (pfile, token, loc, false);
print.prev_was_system_token = !!in_system_header_at (loc);
}
- cpp_output_token (token, print.outf);
- line_marker_emitted = false;
- print.printed = true;
+ if (!(in_pragma && skip_this_pragma))
+ {
+ cpp_output_token (token, print.outf);
+ line_marker_emitted = false;
+ print.printed = true;
+ }
}
/* CPP_COMMENT tokens and raw-string literal tokens can have
@@ -561,8 +584,12 @@ do_line_change (cpp_reader *pfile, const cpp_token *token,
one space per column greater than 2, since scan_translation_unit
will provide a space if PREV_WHITE. Don't bother trying to
reconstruct tabs; we can't get it right in general, and nothing
- ought to care. Some things do care; the fault lies with them. */
- if (!CPP_OPTION (pfile, traditional))
+ ought to care. Some things do care; the fault lies with them.
+
+ Also do not output the spaces if this is a CPP_PRAGMA token. In this
+ case, libcpp has provided the location of the first token after #pragma,
+ so we would start at the wrong column. */
+ if (!CPP_OPTION (pfile, traditional) && token->type != CPP_PRAGMA)
{
int spaces = LOCATION_COLUMN (src_loc) - 2;
print.printed = true;
@@ -782,6 +809,20 @@ cb_def_pragma (cpp_reader *pfile, location_t line)
print.src_line++;
}
+/* Similar, for the portion of a diagnostic pragma that was parsed
+ internally and so not seen by our token streamer. */
+void
+c_pp_output_diagnostic_pragma (const char *kind, const char *option)
+{
+ if (!should_output_pragmas ())
+ return;
+ gcc_assert (print.printed);
+ if (kind)
+ fprintf (print.outf, " %s", kind);
+ if (option)
+ fprintf (print.outf, " \"%s\"", option);
+}
+
/* Dump out the hash table. */
static int
dump_macro (cpp_reader *pfile, cpp_hashnode *node, void *v ATTRIBUTE_UNUSED)
@@ -762,137 +762,323 @@ handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED)
warning (OPT_Wpragmas, "junk at end of %<#pragma GCC visibility%>");
}
-static void
-handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
+/* Helper routines for parsing #pragma GCC diagnostic. */
+struct pragma_diagnostic_data
{
+ bool valid;
+ location_t loc_kind, loc_option;
+ enum pd_kind_t
+ {
+ PD_KIND_INVALID,
+ PD_KIND_PUSH,
+ PD_KIND_POP,
+ PD_KIND_IGNORED_ATTRIBUTES,
+ PD_KIND_DIAGNOSTIC,
+ } pd_kind;
+ diagnostic_t diagnostic_kind;
+ const char *kind_str;
+ const char *option_str;
+ bool own_option_str;
+
+ pragma_diagnostic_data () :
+ valid (false),
+ loc_kind (UNKNOWN_LOCATION),
+ loc_option (UNKNOWN_LOCATION),
+ pd_kind (PD_KIND_INVALID),
+ diagnostic_kind (DK_UNSPECIFIED),
+ kind_str (nullptr),
+ option_str (nullptr),
+ own_option_str (false)
+ {}
+
+ ~pragma_diagnostic_data ()
+ {
+ if (own_option_str && option_str)
+ free (const_cast<char*> (option_str));
+ }
+
+ void set_kind (const char *kind_string)
+ {
+ kind_str = kind_string;
+
+ pd_kind = PD_KIND_INVALID;
+ diagnostic_kind = DK_UNSPECIFIED;
+ if (strcmp (kind_str, "push") == 0)
+ pd_kind = PD_KIND_PUSH;
+ else if (strcmp (kind_str, "pop") == 0)
+ pd_kind = PD_KIND_POP;
+ else if (strcmp (kind_str, "ignored_attributes") == 0)
+ pd_kind = PD_KIND_IGNORED_ATTRIBUTES;
+ else if (strcmp (kind_str, "error") == 0)
+ {
+ pd_kind = PD_KIND_DIAGNOSTIC;
+ diagnostic_kind = DK_ERROR;
+ }
+ else if (strcmp (kind_str, "warning") == 0)
+ {
+ pd_kind = PD_KIND_DIAGNOSTIC;
+ diagnostic_kind = DK_WARNING;
+ }
+ else if (strcmp (kind_str, "ignored") == 0)
+ {
+ pd_kind = PD_KIND_DIAGNOSTIC;
+ diagnostic_kind = DK_IGNORED;
+ }
+ }
+
+ bool needs_option () const
+ {
+ return pd_kind == PD_KIND_IGNORED_ATTRIBUTES
+ || pd_kind == PD_KIND_DIAGNOSTIC;
+ }
+
+};
+
+/* When compiling normally, use pragma_lex() to obtain the needed tokens. This
+ will call into either the C or C++ frontends as appropriate. */
+static pragma_diagnostic_data
+pragma_diagnostic_lex_normal ()
+{
+ pragma_diagnostic_data result;
+
tree x;
- location_t loc;
- enum cpp_ttype token = pragma_lex (&x, &loc);
- if (token != CPP_NAME)
+ auto ttype = pragma_lex (&x, &result.loc_kind);
+ if (ttype != CPP_NAME)
+ return result;
+ result.set_kind (IDENTIFIER_POINTER (x));
+ if (result.pd_kind == pragma_diagnostic_data::PD_KIND_INVALID)
+ return result;
+
+ if (result.needs_option ())
{
- warning_at (loc, OPT_Wpragmas,
- "missing [error|warning|ignored|push|pop|ignored_attributes]"
- " after %<#pragma GCC diagnostic%>");
- return;
+ ttype = pragma_lex (&x, &result.loc_option);
+ if (ttype != CPP_STRING)
+ return result;
+ result.option_str = TREE_STRING_POINTER (x);
}
- diagnostic_t kind;
- const char *kind_string = IDENTIFIER_POINTER (x);
- if (strcmp (kind_string, "error") == 0)
- kind = DK_ERROR;
- else if (strcmp (kind_string, "warning") == 0)
- kind = DK_WARNING;
- else if (strcmp (kind_string, "ignored") == 0)
- kind = DK_IGNORED;
- else if (strcmp (kind_string, "push") == 0)
+ result.valid = true;
+ return result;
+}
+
+/* When preprocessing only, pragma_lex() is not available, so obtain the tokens
+ directly from libcpp. */
+static pragma_diagnostic_data
+pragma_diagnostic_lex_pp ()
+{
+ pragma_diagnostic_data result;
+
+ auto tok = cpp_get_token_with_location (parse_in, &result.loc_kind);
+ if (!(tok->type == CPP_NAME || tok->type == CPP_KEYWORD))
+ return result;
+ const unsigned char *const kind_u = cpp_token_as_text (parse_in, tok);
+ result.set_kind ((const char *)kind_u);
+ if (result.pd_kind == pragma_diagnostic_data::PD_KIND_INVALID)
+ return result;
+
+ if (result.needs_option ())
{
- diagnostic_push_diagnostics (global_dc, input_location);
- return;
+ tok = cpp_get_token_with_location (parse_in, &result.loc_option);
+ if (tok->type != CPP_STRING)
+ return result;
+ cpp_string str;
+ if (!cpp_interpret_string_notranslate (parse_in, &tok->val.str, 1, &str,
+ CPP_STRING)
+ || !str.len)
+ return result;
+ result.option_str = (const char *)str.text;
+ result.own_option_str = true;
}
- else if (strcmp (kind_string, "pop") == 0)
+
+ result.valid = true;
+ return result;
+}
+
+/* Handle #pragma GCC diagnostic. Early mode is used by frontends (such as C++)
+ that do not process the deferred pragma while they are consuming tokens; they
+ can use early mode to make sure diagnostics affecting the preprocessor itself
+ are correctly modified by the #pragma. */
+template<bool early, bool is_pp> static void
+handle_pragma_diagnostic_impl ()
+{
+ static const bool want_diagnostics = (is_pp || !early);
+
+ const auto data
+ = is_pp ? pragma_diagnostic_lex_pp () : pragma_diagnostic_lex_normal ();
+
+ /* When in preprocess-only mode, we need to let c-ppoutput.c know to output
+ these tokens, since it will not see them itself. */
+ if (is_pp)
+ c_pp_output_diagnostic_pragma (data.kind_str, data.option_str);
+
+ if (!data.kind_str)
{
- diagnostic_pop_diagnostics (global_dc, input_location);
+ if (want_diagnostics)
+ warning_at (data.loc_kind, OPT_Wpragmas,
+ "missing [error|warning|ignored|push|pop|ignored_attributes]"
+ " after %<#pragma GCC diagnostic%>");
return;
}
- else if (strcmp (kind_string, "ignored_attributes") == 0)
+
+ switch (data.pd_kind)
{
- token = pragma_lex (&x, &loc);
- if (token != CPP_STRING)
- {
- warning_at (loc, OPT_Wpragmas,
- "missing attribute name after %<#pragma GCC diagnostic "
- "ignored_attributes%>");
- return;
- }
- char *args = xstrdup (TREE_STRING_POINTER (x));
- const size_t l = strlen (args);
- if (l == 0)
- {
- warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
- "diagnostic ignored_attributes%>");
- free (args);
+
+ case pragma_diagnostic_data::PD_KIND_PUSH:
+ diagnostic_push_diagnostics (global_dc, input_location);
+ return;
+
+ case pragma_diagnostic_data::PD_KIND_POP:
+ diagnostic_pop_diagnostics (global_dc, input_location);
+ return;
+
+ case pragma_diagnostic_data::PD_KIND_IGNORED_ATTRIBUTES:
+ {
+ if (early)
return;
- }
- else if (args[l - 1] == ',')
+ if (!data.option_str)
+ {
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "missing attribute name after %<#pragma GCC diagnostic "
+ "ignored_attributes%>");
+ return;
+ }
+ char *args = xstrdup (data.option_str);
+ const size_t l = strlen (args);
+ if (l == 0)
+ {
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "missing argument to %<#pragma GCC "
+ "diagnostic ignored_attributes%>");
+ free (args);
+ return;
+ }
+ else if (args[l - 1] == ',')
+ {
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "trailing %<,%> in arguments for "
+ "%<#pragma GCC diagnostic ignored_attributes%>");
+ free (args);
+ return;
+ }
+ auto_vec<char *> v;
+ for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
+ v.safe_push (p);
+ handle_ignored_attributes_option (&v);
+ free (args);
+ return;
+ }
+
+ case pragma_diagnostic_data::PD_KIND_DIAGNOSTIC:
+ if (!data.option_str)
{
- warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
- "%<#pragma GCC diagnostic ignored_attributes%>");
- free (args);
+ if (want_diagnostics)
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "missing option after %<#pragma GCC diagnostic%> kind");
return;
}
- auto_vec<char *> v;
- for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
- v.safe_push (p);
- handle_ignored_attributes_option (&v);
- free (args);
- return;
- }
- else
- {
- warning_at (loc, OPT_Wpragmas,
+ break;
+
+ default:
+ if (want_diagnostics)
+ warning_at (data.loc_kind, OPT_Wpragmas,
"expected [error|warning|ignored|push|pop|ignored_attributes]"
- " after %<#pragma GCC diagnostic%>");
+ " after %<#pragma GCC diagnostic%>");
return;
- }
- token = pragma_lex (&x, &loc);
- if (token != CPP_STRING)
- {
- warning_at (loc, OPT_Wpragmas,
- "missing option after %<#pragma GCC diagnostic%> kind");
- return;
}
- const char *option_string = TREE_STRING_POINTER (x);
+ gcc_assert (data.pd_kind == pragma_diagnostic_data::PD_KIND_DIAGNOSTIC);
+ gcc_assert (data.valid);
+
unsigned int lang_mask = c_common_option_lang_mask () | CL_COMMON;
/* option_string + 1 to skip the initial '-' */
- unsigned int option_index = find_opt (option_string + 1, lang_mask);
+ unsigned int option_index = find_opt (data.option_str + 1, lang_mask);
+
+ if (early && !c_option_is_from_cpp_diagnostics (option_index))
+ return;
+
+ const char *arg = NULL;
+ if (cl_options[option_index].flags & CL_JOINED)
+ arg = data.option_str + 1 + cl_options[option_index].opt_len;
+ if (early && arg)
+ {
+ /* This warning is needed because of PR71603 - popping the diagnostic
+ state does not currently reset changes to option arguments, only
+ changes to the option dispositions. */
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "a diagnostic pragma attempting to modify a preprocessor"
+ " option argument may not work as expected");
+ }
+
if (option_index == OPT_SPECIAL_unknown)
{
- auto_diagnostic_group d;
- if (warning_at (loc, OPT_Wpragmas,
- "unknown option after %<#pragma GCC diagnostic%> kind"))
+ if (want_diagnostics)
{
- option_proposer op;
- const char *hint = op.suggest_option (option_string + 1);
- if (hint)
- inform (loc, "did you mean %<-%s%>?", hint);
+ auto_diagnostic_group d;
+ if (warning_at (data.loc_option, OPT_Wpragmas,
+ "unknown option after %<#pragma GCC diagnostic%> kind"))
+ {
+ option_proposer op;
+ const char *hint = op.suggest_option (data.option_str + 1);
+ if (hint)
+ inform (data.loc_option, "did you mean %<-%s%>?", hint);
+ }
}
return;
}
else if (!(cl_options[option_index].flags & CL_WARNING))
{
- warning_at (loc, OPT_Wpragmas,
- "%qs is not an option that controls warnings", option_string);
+ if (want_diagnostics)
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "%qs is not an option that controls warnings",
+ data.option_str);
return;
}
else if (!(cl_options[option_index].flags & lang_mask))
{
- char *ok_langs = write_langs (cl_options[option_index].flags);
- char *bad_lang = write_langs (c_common_option_lang_mask ());
- warning_at (loc, OPT_Wpragmas,
- "option %qs is valid for %s but not for %s",
- option_string, ok_langs, bad_lang);
- free (ok_langs);
- free (bad_lang);
+ if (want_diagnostics)
+ {
+ char *ok_langs = write_langs (cl_options[option_index].flags);
+ char *bad_lang = write_langs (c_common_option_lang_mask ());
+ warning_at (data.loc_option, OPT_Wpragmas,
+ "option %qs is valid for %s but not for %s",
+ data.option_str, ok_langs, bad_lang);
+ free (ok_langs);
+ free (bad_lang);
+ }
return;
}
struct cl_option_handlers handlers;
set_default_handlers (&handlers, NULL);
- const char *arg = NULL;
- if (cl_options[option_index].flags & CL_JOINED)
- arg = option_string + 1 + cl_options[option_index].opt_len;
/* FIXME: input_location isn't the best location here, but it is
what we used to do here before and changing it breaks e.g.
PR69543 and PR69558. */
- control_warning_option (option_index, (int) kind,
- arg, kind != DK_IGNORED,
+ control_warning_option (option_index, (int) data.diagnostic_kind,
+ arg, data.diagnostic_kind != DK_IGNORED,
input_location, lang_mask, &handlers,
&global_options, &global_options_set,
global_dc);
}
+static void
+handle_pragma_diagnostic (cpp_reader *ARG_UNUSED(dummy))
+{
+ handle_pragma_diagnostic_impl<false, false> ();
+}
+
+static void
+handle_pragma_diagnostic_early (cpp_reader *ARG_UNUSED(dummy))
+{
+ handle_pragma_diagnostic_impl<true, false> ();
+}
+
+static void
+handle_pragma_diagnostic_early_pp (cpp_reader *ARG_UNUSED(dummy))
+{
+ handle_pragma_diagnostic_impl<true, true> ();
+}
+
/* Parse #pragma GCC target (xxx) to set target specific options. */
static void
handle_pragma_target(cpp_reader *ARG_UNUSED(dummy))
@@ -1328,14 +1514,15 @@ handle_pragma_float_const_decimal64 (cpp_reader *ARG_UNUSED (dummy))
static vec<internal_pragma_handler> registered_pragmas;
-struct pragma_ns_name
+struct pragma_pp_data
{
const char *space;
const char *name;
+ pragma_handler_1arg early_handler;
};
-static vec<pragma_ns_name> registered_pp_pragmas;
+static vec<pragma_pp_data> registered_pp_pragmas;
struct omp_pragma_def { const char *name; unsigned int id; };
static const struct omp_pragma_def oacc_pragmas[] = {
@@ -1448,14 +1635,14 @@ c_register_pragma_1 (const char *space, const char *name,
if (flag_preprocess_only)
{
- pragma_ns_name ns_name;
-
- if (!allow_expansion)
+ if (!(allow_expansion || ihandler.early_handler.handler_1arg))
return;
- ns_name.space = space;
- ns_name.name = name;
- registered_pp_pragmas.safe_push (ns_name);
+ pragma_pp_data pp_data;
+ pp_data.space = space;
+ pp_data.name = name;
+ pp_data.early_handler = ihandler.early_handler.handler_1arg;
+ registered_pp_pragmas.safe_push (pp_data);
id = registered_pp_pragmas.length ();
id += PRAGMA_FIRST_EXTERNAL - 1;
}
@@ -1480,10 +1667,17 @@ c_register_pragma_1 (const char *space, const char *name,
void
c_register_pragma (const char *space, const char *name,
pragma_handler_1arg handler)
+{
+ c_register_pragma_with_early_handler (space, name, handler, nullptr);
+}
+void c_register_pragma_with_early_handler (const char *space, const char *name,
+ pragma_handler_1arg handler,
+ pragma_handler_1arg early_handler)
{
internal_pragma_handler ihandler;
ihandler.handler.handler_1arg = handler;
+ ihandler.early_handler.handler_1arg = early_handler;
ihandler.extra_data = false;
ihandler.data = NULL;
c_register_pragma_1 (space, name, ihandler, false);
@@ -1500,6 +1694,7 @@ c_register_pragma_with_data (const char *space, const char *name,
internal_pragma_handler ihandler;
ihandler.handler.handler_2arg = handler;
+ ihandler.early_handler.handler_2arg = nullptr;
ihandler.extra_data = true;
ihandler.data = data;
c_register_pragma_1 (space, name, ihandler, false);
@@ -1519,6 +1714,7 @@ c_register_pragma_with_expansion (const char *space, const char *name,
internal_pragma_handler ihandler;
ihandler.handler.handler_1arg = handler;
+ ihandler.early_handler.handler_1arg = nullptr;
ihandler.extra_data = false;
ihandler.data = NULL;
c_register_pragma_1 (space, name, ihandler, true);
@@ -1540,6 +1736,7 @@ c_register_pragma_with_expansion_and_data (const char *space, const char *name,
internal_pragma_handler ihandler;
ihandler.handler.handler_2arg = handler;
+ ihandler.early_handler.handler_2arg = nullptr;
ihandler.extra_data = true;
ihandler.data = data;
c_register_pragma_1 (space, name, ihandler, true);
@@ -1566,6 +1763,38 @@ c_invoke_pragma_handler (unsigned int id)
}
}
+/* In contrast to the normal handler, the early handler is optional. */
+void
+c_invoke_early_pragma_handler (unsigned int id)
+{
+ internal_pragma_handler *ihandler;
+ pragma_handler_1arg handler_1arg;
+ pragma_handler_2arg handler_2arg;
+
+ id -= PRAGMA_FIRST_EXTERNAL;
+ ihandler = ®istered_pragmas[id];
+ if (ihandler->extra_data)
+ {
+ handler_2arg = ihandler->early_handler.handler_2arg;
+ if (handler_2arg)
+ handler_2arg (parse_in, ihandler->data);
+ }
+ else
+ {
+ handler_1arg = ihandler->early_handler.handler_1arg;
+ if (handler_1arg)
+ handler_1arg (parse_in);
+ }
+}
+
+void
+c_pp_invoke_early_pragma_handler (unsigned int id)
+{
+ const auto data = ®istered_pp_pragmas[id - PRAGMA_FIRST_EXTERNAL];
+ if (data->early_handler)
+ data->early_handler (parse_in);
+}
+
/* Set up front-end pragmas. */
void
init_pragma (void)
@@ -1622,7 +1851,14 @@ init_pragma (void)
c_register_pragma ("GCC", "visibility", handle_pragma_visibility);
- c_register_pragma ("GCC", "diagnostic", handle_pragma_diagnostic);
+ if (flag_preprocess_only)
+ c_register_pragma_with_early_handler ("GCC", "diagnostic",
+ nullptr,
+ handle_pragma_diagnostic_early_pp);
+ else
+ c_register_pragma_with_early_handler ("GCC", "diagnostic",
+ handle_pragma_diagnostic,
+ handle_pragma_diagnostic_early);
c_register_pragma ("GCC", "target", handle_pragma_target);
c_register_pragma ("GCC", "optimize", handle_pragma_optimize);
c_register_pragma ("GCC", "push_options", handle_pragma_push_options);
@@ -217,7 +217,7 @@ union gen_pragma_handler {
};
/* Internally used to keep the data of the handler. */
struct internal_pragma_handler {
- union gen_pragma_handler handler;
+ union gen_pragma_handler handler, early_handler;
/* Permits to know if handler is a pragma_handler_1arg (extra_data is false)
or a pragma_handler_2arg (extra_data is true). */
bool extra_data;
@@ -240,6 +240,17 @@ extern void c_register_pragma_with_expansion_and_data (const char *space,
void *data);
extern void c_invoke_pragma_handler (unsigned int);
+/* Early pragma handlers run in addition to the normal ones. They can be used
+ by frontends such as C++ that may want to process some pragmas during lexing
+ before they start processing them. */
+extern void
+c_register_pragma_with_early_handler (const char *space, const char *name,
+ pragma_handler_1arg handler,
+ pragma_handler_1arg early_handler);
+extern void c_invoke_early_pragma_handler (unsigned int);
+extern void c_pp_invoke_early_pragma_handler (unsigned int);
+
+
extern void maybe_apply_pragma_weak (tree);
extern void maybe_apply_pending_pragma_weaks (void);
extern tree maybe_apply_renaming_pragma (tree, tree);
@@ -629,8 +629,68 @@ cp_lexer_alloc (void)
return lexer;
}
+/* 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);
+}
+
+/* Return TRUE if token is the start of a module declaration that will be
+ terminated by a CPP_PRAGMA_EOL token. */
+static inline bool
+cp_token_is_module_directive (cp_token *token)
+{
+ return token->keyword == RID__EXPORT
+ || token->keyword == RID__MODULE
+ || token->keyword == RID__IMPORT;
+}
+
+/* Handle #pragma gcc diagnostic, which needs to be done during preprocessing
+ for the case of preprocessing-related diagnostics. */
+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)
@@ -652,6 +712,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)
{
@@ -659,6 +723,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);
}
@@ -2097,11 +2166,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
@@ -2860,18 +2924,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
@@ -3805,9 +3857,7 @@ cp_parser_skip_to_closing_parenthesis_1 (cp_parser *parser,
break;
case CPP_KEYWORD:
- if (token->keyword != RID__EXPORT
- && token->keyword != RID__MODULE
- && token->keyword != RID__IMPORT)
+ if (!cp_token_is_module_directive (token))
break;
/* FALLTHROUGH */
@@ -3908,9 +3958,7 @@ cp_parser_skip_to_end_of_statement (cp_parser* parser)
break;
case CPP_KEYWORD:
- if (token->keyword != RID__EXPORT
- && token->keyword != RID__MODULE
- && token->keyword != RID__IMPORT)
+ if (!cp_token_is_module_directive (token))
break;
/* FALLTHROUGH */
@@ -3997,9 +4045,7 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
break;
case CPP_KEYWORD:
- if (token->keyword != RID__EXPORT
- && token->keyword != RID__MODULE
- && token->keyword != RID__IMPORT)
+ if (!cp_token_is_module_directive (token))
break;
/* FALLTHROUGH */
@@ -14860,9 +14906,7 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
else
cp_parser_module_export (parser);
}
- else if (token1->keyword == RID__EXPORT
- || token1->keyword == RID__IMPORT
- || token1->keyword == RID__MODULE)
+ else if (cp_token_is_module_directive (token1))
{
bool exporting = token1->keyword == RID__EXPORT;
cp_token *next = exporting ? token2 : token1;
@@ -47413,11 +47457,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
@@ -47912,9 +47952,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 ();
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-Wundef" } */
+#pragma GCC diagnostic ignored "-Wundef"
+#if FOO
+#endif
+#define P _Pragma ("GCC diagnostic push") _Pragma ("GCC diagnostic warning \"-Wundef\"")
+P
+#if FOO2 /* { dg-warning "is not defined" } */
+#endif
+#pragma GCC diagnostic pop
+#if FOO3
+#endif
+int i;
new file mode 100644
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-E -Wdate-time" } */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdate-time"
+const char *date = __DATE__;
+_Pragma ("GCC diagnostic pop");
+const char *date2 = __DATE__; /* { dg-warning "__DATE__" } */
+/* { dg-final { scan-assembler "#pragma GCC diagnostic push" } } */
+/* { dg-final { scan-assembler "#pragma GCC diagnostic ignored \"-Wdate-time\"" } } */
+/* { dg-final { scan-assembler "#pragma GCC diagnostic pop" } } */
new file mode 100644
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+#pragma GCC diagnostic /* { dg-warning "missing" } */
+#pragma GCC diagnostic warn /* { dg-warning "24:expected" } */
+#pragma GCC diagnostic ignored "-Wfoo" /* { dg-warning "32:unknown" } */