Improve warning suppression for inlined functions [PR 98512]. Resolves: PR middle-end/98871 - Cannot silence -Wmaybe-uninitialized at declaration site PR middle-end/98512 - #pragma GCC diagnostic ignored ineffective in conjunction with alias attribute gcc/ChangeLog: * diagnostic.c (update_inlining_context): New. (update_effective_level_from_pragmas): Handle inlining context. (diagnostic_enabled): Same. (diagnostic_report_diagnostic): Same. * diagnostic.h (struct diagnostic_info): Add ctor. (struct diagnostic_context): Add new member. * tree-diagnostic.c (set_inlining_locations): New. (tree_diagnostics_defaults): Set new callback pointer. diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index d58586f2526..3a22d4d26a6 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -991,51 +991,93 @@ print_parseable_fixits (pretty_printer *pp, rich_location *richloc, pp_set_prefix (pp, saved_prefix); } -/* Update the diag_class of DIAGNOSTIC based on its location - relative to any +/* Update the inlininig context in CONTEXT for a DIAGNOSTIC. */ + +static void +update_inlining_context (diagnostic_context *context, + diagnostic_info *diagnostic) +{ + auto &ilocs = diagnostic->m_iinfo.m_ilocs; + + if (context->set_locations_cb) + { + /* Retrieve the locations into which the expression about to be + diagnosed has been inlined, including those of all the callers + all the way down the inlining stack. */ + context->set_locations_cb (context, diagnostic); + location_t loc = diagnostic->m_iinfo.m_ilocs.last (); + if (diagnostic->m_iinfo.m_ao && loc != UNKNOWN_LOCATION) + diagnostic->message.set_location (0, loc, SHOW_RANGE_WITH_CARET); + } + else + { + /* When there's no callback use just the one location provided + by the caller of the diagnostic function. */ + location_t loc = diagnostic_location (diagnostic); + ilocs.safe_push (loc); + diagnostic->m_iinfo.m_allsyslocs = in_system_header_at (loc); + } +} + +/* Update the kind of DIAGNOSTIC based on its location(s), including + any of those in its inlining context, relative to any #pragma GCC diagnostic directives recorded within CONTEXT. - Return the new diag_class of DIAGNOSTIC if it was updated, or - DK_UNSPECIFIED otherwise. */ + Return the new kind of DIAGNOSTIC if it was updated, or DK_UNSPECIFIED + otherwise. */ static diagnostic_t update_effective_level_from_pragmas (diagnostic_context *context, diagnostic_info *diagnostic) { - diagnostic_t diag_class = DK_UNSPECIFIED; - - if (context->n_classification_history > 0) + if (diagnostic->m_iinfo.m_allsyslocs && !context->dc_warn_system_headers) { - location_t location = diagnostic_location (diagnostic); + /* Ignore the diagnostic if all the inlined locations are + in system headers and -Wno-system-headers is in effect. */ + diagnostic->kind = DK_IGNORED; + return DK_IGNORED; + } + if (context->n_classification_history <= 0) + return DK_UNSPECIFIED; + + /* Iterate over the locations, checking the diagnostic disposition + for the diagnostic at each. If it's explicitly set as opposed + to unspecified, update the disposition for this instance of + the diagnostic and return it. */ + for (location_t loc: diagnostic->m_iinfo.m_ilocs) + { /* FIXME: Stupid search. Optimize later. */ for (int i = context->n_classification_history - 1; i >= 0; i --) { - if (linemap_location_before_p - (line_table, - context->classification_history[i].location, - location)) + const diagnostic_classification_change_t &hist + = context->classification_history[i]; + + location_t pragloc = hist.location; + if (!linemap_location_before_p (line_table, pragloc, loc)) + continue; + + if (hist.kind == (int) DK_POP) { - if (context->classification_history[i].kind == (int) DK_POP) - { - i = context->classification_history[i].option; - continue; - } - int option = context->classification_history[i].option; - /* The option 0 is for all the diagnostics. */ - if (option == 0 || option == diagnostic->option_index) - { - diag_class = context->classification_history[i].kind; - if (diag_class != DK_UNSPECIFIED) - diagnostic->kind = diag_class; - break; - } + /* Move on to the next region. */ + i = hist.option; + continue; + } + + int option = hist.option; + /* The option 0 is for all the diagnostics. */ + if (option == 0 || option == diagnostic->option_index) + { + diagnostic_t kind = hist.kind; + if (kind != DK_UNSPECIFIED) + diagnostic->kind = kind; + return kind; } } } - return diag_class; + return DK_UNSPECIFIED; } /* Generate a URL string describing CWE. The caller is responsible for @@ -1129,6 +1171,9 @@ static bool diagnostic_enabled (diagnostic_context *context, diagnostic_info *diagnostic) { + /* Update the inlining context for this diagnostic. */ + update_inlining_context (context, diagnostic); + /* Diagnostics with no option or -fpermissive are always enabled. */ if (!diagnostic->option_index || diagnostic->option_index == permissive_error_option (context)) @@ -1194,9 +1239,17 @@ diagnostic_report_diagnostic (diagnostic_context *context, /* Give preference to being able to inhibit warnings, before they get reclassified to something else. */ - if ((diagnostic->kind == DK_WARNING || diagnostic->kind == DK_PEDWARN) - && !diagnostic_report_warnings_p (context, location)) - return false; + bool report_warning_p = true; + if (diagnostic->kind == DK_WARNING || diagnostic->kind == DK_PEDWARN) + { + if (context->dc_inhibit_warnings) + return false; + /* Remember the result of the overall system header warning setting + but proceed to also check the inlining context. */ + report_warning_p = diagnostic_report_warnings_p (context, location); + if (!report_warning_p && diagnostic->kind == DK_PEDWARN) + return false; + } if (diagnostic->kind == DK_PEDWARN) { @@ -1204,7 +1257,7 @@ diagnostic_report_diagnostic (diagnostic_context *context, /* We do this to avoid giving the message for -pedantic-errors. */ orig_diag_kind = diagnostic->kind; } - + if (diagnostic->kind == DK_NOTE && context->inhibit_notes_p) return false; @@ -1228,9 +1281,19 @@ diagnostic_report_diagnostic (diagnostic_context *context, && diagnostic->kind == DK_WARNING) diagnostic->kind = DK_ERROR; + diagnostic->message.x_data = &diagnostic->x_data; + + /* Check to see if the diagnostic is enabled at the location and + not disabled by #pragma GCC diagnostic anywhere along the inlining + stack. . */ if (!diagnostic_enabled (context, diagnostic)) return false; + if (!report_warning_p && diagnostic->m_iinfo.m_allsyslocs) + /* Bail if the warning is not to be reported because all locations + in the inlining stack (if there is one) are in system headers. */ + return false; + if (diagnostic->kind != DK_NOTE && diagnostic->kind != DK_ICE) diagnostic_check_max_errors (context); @@ -1270,8 +1333,6 @@ diagnostic_report_diagnostic (diagnostic_context *context, } context->diagnostic_group_emission_count++; - diagnostic->message.x_data = &diagnostic->x_data; - diagnostic->x_data = NULL; pp_format (context->printer, &diagnostic->message); (*diagnostic_starter (context)) (context, diagnostic); pp_output_formatted_text (context->printer); diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 1b9d6b1f64d..9d02615fb7a 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -87,6 +87,11 @@ enum diagnostics_extra_output_kind list in diagnostic.def. */ struct diagnostic_info { + diagnostic_info () + : message (), richloc (), metadata (), x_data (), kind (), option_index (), + m_iinfo () + { } + /* Text to be formatted. */ text_info message; @@ -103,6 +108,18 @@ struct diagnostic_info diagnostic_t kind; /* Which OPT_* directly controls this diagnostic. */ int option_index; + + /* Inlining context containing locations for each call site along + the inlining stack. */ + struct inlining_info + { + /* Locations along the inlining stack. */ + auto_vec m_ilocs; + /* The abstract origin of the location. */ + void *m_ao; + /* Set if every M_ILOCS element is in a system header. */ + bool m_allsyslocs; + } m_iinfo; }; /* Each time a diagnostic's classification is changed with a pragma, @@ -343,6 +360,12 @@ struct diagnostic_context /* Callback for final cleanup. */ void (*final_cb) (diagnostic_context *context); + + /* Callback to set the locations of call sites along the inlining + stack corresponding to a diagnostic location. Needed to traverse + the BLOCK_SUPERCONTEXT() chain hanging off the LOCATION_BLOCK() + of a diagnostic's location. */ + void (*set_locations_cb)(diagnostic_context *, diagnostic_info *); }; static inline void diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c index 95b8ef30070..1b636d72a29 100644 --- a/gcc/tree-diagnostic.c +++ b/gcc/tree-diagnostic.c @@ -305,6 +305,73 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec, return true; } +/* Set the locations of call sites along the inlining stack corresponding + to the DIAGNOSTIC location. */ + +static void +set_inlining_locations (diagnostic_context *, + diagnostic_info *diagnostic) +{ + location_t loc = diagnostic_location (diagnostic); + tree block = LOCATION_BLOCK (loc); + + /* Count the number of locations in system headers. When all are, + warnings are suppressed by -Wno-system-headers. Otherwise, they + involve some user code, possibly inlined into a function in a system + header, and are not treated as coming from system headers. */ + unsigned nsyslocs = 0; + + /* Use a reference to the vector of locations for convenience. */ + auto &ilocs = diagnostic->m_iinfo.m_ilocs; + + while (block && TREE_CODE (block) == BLOCK + && BLOCK_ABSTRACT_ORIGIN (block)) + { + tree ao = BLOCK_ABSTRACT_ORIGIN (block); + if (TREE_CODE (ao) == FUNCTION_DECL) + { + if (!diagnostic->m_iinfo.m_ao) + diagnostic->m_iinfo.m_ao = block; + + location_t bsloc = BLOCK_SOURCE_LOCATION (block); + ilocs.safe_push (bsloc); + if (in_system_header_at (bsloc)) + ++nsyslocs; + } + else if (TREE_CODE (ao) != BLOCK) + break; + + block = BLOCK_SUPERCONTEXT (block); + } + + if (ilocs.length ()) + { + /* When there is an inlining context use the macro expansion + location for the original location and bump up NSYSLOCS if + it's in a system header since it's not counted above. */ + location_t sysloc = expansion_point_location_if_in_system_header (loc); + if (sysloc != loc) + { + loc = sysloc; + ++nsyslocs; + } + } + else + { + /* When there's no inlining context use the original location + and set NSYSLOCS accordingly. */ + nsyslocs = in_system_header_at (loc) != 0; + } + + ilocs.safe_push (loc); + + /* Set if all locations are in a system header. */ + diagnostic->m_iinfo.m_allsyslocs = nsyslocs == ilocs.length (); + + if (tree *ao = pp_ti_abstract_origin (&diagnostic->message)) + *ao = (tree)diagnostic->m_iinfo.m_ao; +} + /* Sets CONTEXT to use language independent diagnostics. */ void tree_diagnostics_defaults (diagnostic_context *context) @@ -314,4 +381,5 @@ tree_diagnostics_defaults (diagnostic_context *context) diagnostic_format_decoder (context) = default_tree_printer; context->print_path = default_tree_diagnostic_path_printer; context->make_json_for_path = default_tree_make_json_for_path; + context->set_locations_cb = set_inlining_locations; }