This diff introduces the diagnostic infrastructure changes to support controlling warnings at any call site in the inlining stack and printing the inlining context without the %K and %G directives.
Improve warning suppression for inlined functions.
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_report_diagnostic): Same. * diagnostic.h (struct diagnostic_info): Add ctor. (struct diagnostic_context): Add members. * tree-diagnostic.c (get_inlining_locations): New. (set_inlining_location): New. (tree_diagnostics_defaults): Set new callback pointers. diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index d58586f2526..d1c8c655f7a 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -991,51 +991,92 @@ 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) +{ + context->ictx.reset (); + + auto &ilocs = context->ictx.ilocs; + + if (context->get_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->get_locations_cb (context, diagnostic); + else + { + /* When there's no metadata use just the one location provided + by the caller of the diagnostic function. */ + location_t loc = diagnostic_location (diagnostic); + ilocs.safe_push (loc); + context->ictx.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 (context->ictx.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; + + auto &ilocs = context->ictx.ilocs; + /* 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 (unsigned idx = 0; idx < ilocs.length (); ++idx) + { /* 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, ilocs[idx])) + 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 +1170,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 +1238,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 +1256,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 +1280,17 @@ diagnostic_report_diagnostic (diagnostic_context *context, && diagnostic->kind == DK_WARNING) diagnostic->kind = DK_ERROR; + /* 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 && context->ictx.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 +1330,14 @@ diagnostic_report_diagnostic (diagnostic_context *context, } context->diagnostic_group_emission_count++; + /* Move X_DATA into DIAGNOSTIC->MESSAGE before setting inlining context + abstract origin and location. It uses X_DATA. */ diagnostic->message.x_data = &diagnostic->x_data; diagnostic->x_data = NULL; + + if (context->set_location_cb) + context->set_location_cb (context, diagnostic); + 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..b95ee23dda0 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -87,6 +87,10 @@ enum diagnostics_extra_output_kind list in diagnostic.def. */ struct diagnostic_info { + diagnostic_info () + : message (), richloc (), metadata (), x_data (), kind (), option_index () + { } + /* Text to be formatted. */ text_info message; @@ -343,6 +347,32 @@ struct diagnostic_context /* Callback for final cleanup. */ void (*final_cb) (diagnostic_context *context); + + /* The inlining context of the diagnostic (may have just one + element if a diagnostic is not for an inlined expression). */ + struct inlining_ctx + { + void reset () + { + ilocs.release (); + loc = UNKNOWN_LOCATION; + ao = NULL; + allsyslocs = false; + } + + /* Locations along the inlining stack. */ + auto_vec<location_t, 8> ilocs; + /* The locus of the diagnostic. */ + location_t loc; + /* The abstract origin of the location. */ + void *ao; + /* Set of every ILOCS element is in a system header. */ + bool allsyslocs; + } ictx; + + /* Callbacks to get and set the inlining context. */ + void (*get_locations_cb)(diagnostic_context *, diagnostic_info *); + void (*set_location_cb)(const diagnostic_context *, diagnostic_info *); }; static inline void diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c index 95b8ef30070..a8c5484849a 100644 --- a/gcc/tree-diagnostic.c +++ b/gcc/tree-diagnostic.c @@ -305,6 +305,84 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec, return true; } +/* Get the inlining stack corresponding to the DIAGNOSTIC location. */ + +static void +get_inlining_locations (diagnostic_context *context, + diagnostic_info *diagnostic) +{ + context->ictx.reset (); + + 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; + + while (block && TREE_CODE (block) == BLOCK + && BLOCK_ABSTRACT_ORIGIN (block)) + { + tree ao = BLOCK_ABSTRACT_ORIGIN (block); + if (TREE_CODE (ao) == FUNCTION_DECL) + { + if (!context->ictx.ao) + context->ictx.ao = block; + + location_t loc = BLOCK_SOURCE_LOCATION (block); + context->ictx.ilocs.safe_push (loc); + if (in_system_header_at (loc)) + ++nsyslocs; + } + else if (TREE_CODE (ao) != BLOCK) + break; + + block = BLOCK_SUPERCONTEXT (block); + } + + if (context->ictx.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. */ + context->ictx.loc = expansion_point_location_if_in_system_header (loc); + if (context->ictx.loc != loc) + ++nsyslocs; + } + else + { + /* When there's no inlining context use the original location + and set NSYSLOCS accordingly. */ + context->ictx.loc = loc; + nsyslocs = in_system_header_at (loc) != 0; + } + + context->ictx.ilocs.safe_push (context->ictx.loc); + + /* Set if all locations are in a system header. */ + context->ictx.allsyslocs = nsyslocs == context->ictx.ilocs.length ();; +} + +/* Set the inlining location for to the DIAGNOSTIC based on the saved + inlining context. */ + +static void +set_inlining_location (const diagnostic_context *context, + diagnostic_info *diagnostic) +{ + if (!pp_ti_abstract_origin (&diagnostic->message) + || !context->ictx.ao + || context->ictx.loc == UNKNOWN_LOCATION) + /* Do nothing when there's no inlining context. */ + return; + + *pp_ti_abstract_origin (&diagnostic->message) = (tree)context->ictx.ao; + diagnostic->message.set_location (0, context->ictx.loc, + SHOW_RANGE_WITH_CARET); +} + /* Sets CONTEXT to use language independent diagnostics. */ void tree_diagnostics_defaults (diagnostic_context *context) @@ -314,4 +392,6 @@ 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->get_locations_cb = get_inlining_locations; + context->set_location_cb = set_inlining_location; }