diff options
Diffstat (limited to 'gcc/diagnostics')
26 files changed, 1185 insertions, 310 deletions
diff --git a/gcc/diagnostics/buffering.cc b/gcc/diagnostics/buffering.cc index 29f039f..a7747b5 100644 --- a/gcc/diagnostics/buffering.cc +++ b/gcc/diagnostics/buffering.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "diagnostics/buffering.h" #include "diagnostics/sink.h" +#include "diagnostics/dumping.h" namespace diagnostics { @@ -122,12 +123,12 @@ void buffer::dump (FILE *out, int indent) const { m_diagnostic_counters.dump (out, indent + 2); - fprintf (out, "%*sm_per_sink_buffers:\n", indent, ""); + dumping::emit_heading (out, indent, "m_per_sink_buffers"); if (m_per_sink_buffers) for (auto per_sink_buffer_ : *m_per_sink_buffers) per_sink_buffer_->dump (out, indent + 2); else - fprintf (out, "%*s(none)\n", indent + 2, ""); + dumping::emit_none (out, indent + 2); } bool diff --git a/gcc/diagnostics/buffering.h b/gcc/diagnostics/buffering.h index c3ac070..9b86fee 100644 --- a/gcc/diagnostics/buffering.h +++ b/gcc/diagnostics/buffering.h @@ -36,7 +36,7 @@ class sink; A diagnostics::buffer can be: * flushed to the diagnostics::context, which issues - the diagnostics within the buffer to the output format + the diagnostics within the buffer to the output sinks and checks for limits such as -fmax-errors=, or * moved to another diagnostics::buffer, which moves the diagnostics @@ -45,13 +45,13 @@ class sink; source buffer, or * cleared, which discards any diagnostics within the buffer - without issuing them to the output format. + without issuing them to the output sinks. - Since a buffer needs to contain output-format-specific data, - it's not possible to change the output format of the + Since a buffer needs to contain sink-specific data, + it's not possible to change the output sink(s) of the diagnostics::context once any buffers are non-empty. - To simplify implementing output formats, it's not possible + To simplify implementing output sinks, it's not possible to change buffering on a diagnostics::context whilst within a diagnostic group. */ diff --git a/gcc/diagnostics/column-options.h b/gcc/diagnostics/column-options.h new file mode 100644 index 0000000..86296e9 --- /dev/null +++ b/gcc/diagnostics/column-options.h @@ -0,0 +1,44 @@ +/* Options relating to the meaning of column numbers. + Copyright (C) 2000-2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_COLUMN_OPTIONS_H +#define GCC_DIAGNOSTICS_COLUMN_OPTIONS_H + +namespace diagnostics { + +/* A bundle of options relating to the meaning of column numbers. */ + +struct column_options +{ + int convert_column (file_cache &fc, + expanded_location s) const; + + /* What units to use when outputting the column number. */ + enum diagnostics_column_unit m_column_unit; + + /* The origin for the column number (1-based or 0-based typically). */ + int m_column_origin; + + /* The size of the tabstop for tab expansion. */ + int m_tabstop; +}; + +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_COLUMN_OPTIONS_H */ diff --git a/gcc/diagnostics/context.cc b/gcc/diagnostics/context.cc index 85f7d2a..0f8670b 100644 --- a/gcc/diagnostics/context.cc +++ b/gcc/diagnostics/context.cc @@ -50,6 +50,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/logical-locations.h" #include "diagnostics/buffering.h" #include "diagnostics/file-cache.h" +#include "diagnostics/dumping.h" +#include "diagnostics/logging.h" #ifdef HAVE_TERMIOS_H # include <termios.h> @@ -170,18 +172,24 @@ context::initialize (int n_opts) m_text_callbacks.m_html_start_span = default_start_span_fn<to_html>; m_text_callbacks.m_end_diagnostic = default_text_finalizer; - m_option_mgr = nullptr; + m_option_id_mgr = nullptr; m_urlifier_stack = new auto_vec<urlifier_stack_node> (); m_last_location = UNKNOWN_LOCATION; m_client_aux_data = nullptr; m_lock = 0; m_inhibit_notes_p = false; + m_source_printing.colorize_source_p = false; m_source_printing.show_labels_p = false; m_source_printing.show_line_numbers_p = false; m_source_printing.min_margin_width = 0; m_source_printing.show_ruler_p = false; m_source_printing.show_event_links_p = false; + + m_column_options.m_column_unit = DIAGNOSTICS_COLUMN_UNIT_DISPLAY; + m_column_options.m_column_origin = 1; + m_column_options.m_tabstop = 8; + m_report_bug = false; m_extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_none; if (const char *var = getenv ("GCC_EXTRA_DIAGNOSTIC_OUTPUT")) @@ -192,9 +200,6 @@ context::initialize (int n_opts) m_extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2; /* Silently ignore unrecognized values. */ } - m_column_unit = DIAGNOSTICS_COLUMN_UNIT_DISPLAY; - m_column_origin = 1; - m_tabstop = 8; m_escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; m_fixits_change_set = nullptr; m_diagnostic_groups.m_group_nesting_depth = 0; @@ -207,6 +212,7 @@ context::initialize (int n_opts) m_diagrams.m_theme = nullptr; m_original_argv = nullptr; m_diagnostic_buffer = nullptr; + m_logger = nullptr; enum diagnostic_text_art_charset text_art_charset = DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI; @@ -218,6 +224,27 @@ context::initialize (int n_opts) text_art_charset = DIAGNOSTICS_TEXT_ART_CHARSET_ASCII; } set_text_art_charset (text_art_charset); + + if (const char *name = getenv ("GCC_DIAGNOSTICS_LOG")) + { + if (name[0] != '\0') + { + /* Try to write a log to the named path. */ + if (FILE *outfile = fopen (name, "w")) + m_logger = new logging::logger + (output_file (outfile, true, + label_text::take (xstrdup (name)))); + } + else + /* Write a log to stderr. */ + m_logger = new logging::logger + (output_file + (stderr, false, + label_text::borrow ("stderr"))); + } + + if (m_logger) + m_logger->log_printf ("diagnostics::context::initialize"); } /* Maybe initialize the color support. We require clients to do this @@ -284,6 +311,33 @@ context::urls_init (int value) (m_reference_printer->get_url_format ()); } +void +context::set_show_nesting (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_nesting (val); +} + +void +context::set_show_nesting_locations (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_locations_in_nesting (val); +} + +void +context::set_show_nesting_levels (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_nesting_levels (val); +} + /* Create the file_cache, if not already created, and tell it how to translate files on input. */ void @@ -298,6 +352,15 @@ context::initialize_input_context (diagnostic_input_charset_callback ccb, void context::finish () { + if (m_logger) + { + m_logger->log_printf ("diagnostics::context::finish"); + /* We're cleaning up the logger before this function exits, + so we can't use auto_inc_depth here. */ + m_logger->inc_depth (); + dump (m_logger->get_stream (), m_logger->get_indent ()); + } + /* We might be handling a fatal error. Close any active diagnostic groups, which may trigger flushing sinks. */ @@ -337,8 +400,8 @@ context::finish () m_client_data_hooks = nullptr; } - delete m_option_mgr; - m_option_mgr = nullptr; + delete m_option_id_mgr; + m_option_id_mgr = nullptr; if (m_urlifier_stack) { @@ -350,38 +413,48 @@ context::finish () freeargv (m_original_argv); m_original_argv = nullptr; + + delete m_logger; + m_logger = nullptr; } /* Dump state of this diagnostics::context to OUT, for debugging. */ void -context::dump (FILE *out) const +context::dump (FILE *outfile, int indent) const { - fprintf (out, "diagnostics::context:\n"); - m_diagnostic_counters.dump (out, 2); - fprintf (out, " reference printer:\n"); - m_reference_printer->dump (out, 4); - fprintf (out, " output sinks:\n"); + dumping::emit_heading (outfile, indent, "diagnostics::context"); + m_diagnostic_counters.dump (outfile, indent + 2); + dumping::emit_heading (outfile, indent + 2, "reference printer"); + if (m_reference_printer) + m_reference_printer->dump (outfile, indent + 4); + else + dumping::emit_none (outfile, indent + 4); + dumping::emit_heading (outfile, indent + 2, "output sinks"); if (m_sinks.length () > 0) { for (unsigned i = 0; i < m_sinks.length (); ++i) { - fprintf (out, " sink %i:\n", i); - m_sinks[i]->dump (out, 4); + dumping::emit_indent (outfile, indent + 4); + const sink *s = m_sinks[i]; + fprintf (outfile, "sink %i (", i); + s->dump_kind (outfile); + fprintf (outfile, "):\n"); + s->dump (outfile, indent + 6); } } else - fprintf (out, " (none):\n"); - fprintf (out, " diagnostic buffer:\n"); + dumping::emit_none (outfile, indent + 4); + dumping::emit_heading (outfile, indent + 2, "diagnostic buffer"); if (m_diagnostic_buffer) - m_diagnostic_buffer->dump (out, 4); + m_diagnostic_buffer->dump (outfile, indent + 4); else - fprintf (out, " (none):\n"); - fprintf (out, " file cache:\n"); + dumping::emit_none (outfile, indent + 4); + dumping::emit_heading (outfile, indent + 2, "file cache"); if (m_file_cache) - m_file_cache->dump (out, 4); + m_file_cache->dump (outfile, indent + 4); else - fprintf (out, " (none):\n"); + dumping::emit_none (outfile, indent + 4); } /* Return true if sufficiently severe diagnostics have been seen that @@ -407,6 +480,9 @@ context::remove_all_output_sinks () void context::set_sink (std::unique_ptr<sink> sink_) { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_logger, "diagnostics::context::set_sink"); + if (m_logger) + sink_->dump (m_logger->get_stream (), m_logger->get_indent ()); remove_all_output_sinks (); m_sinks.safe_push (sink_.release ()); } @@ -422,6 +498,9 @@ context::get_sink (size_t idx) const void context::add_sink (std::unique_ptr<sink> sink_) { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_logger, "diagnostics::context::add_sink"); + if (m_logger) + sink_->dump (m_logger->get_stream (), m_logger->get_indent ()); m_sinks.safe_push (sink_.release ()); } @@ -465,11 +544,11 @@ context::set_original_argv (unique_argv original_argv) } void -context::set_option_manager (std::unique_ptr<option_manager> mgr, - unsigned lang_mask) +context::set_option_id_manager (std::unique_ptr<option_id_manager> mgr, + unsigned lang_mask) { - delete m_option_mgr; - m_option_mgr = mgr.release (); + delete m_option_id_mgr; + m_option_id_mgr = mgr.release (); m_lang_mask = lang_mask; } @@ -626,6 +705,19 @@ get_text_for_kind (enum kind kind) return diagnostic_kind_text[static_cast<int> (kind)]; } +static const char *const diagnostic_kind_debug_text[] = { +#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (#K), +#include "diagnostics/kinds.def" +#undef DEFINE_DIAGNOSTIC_KIND + "must-not-happen" +}; + +const char * +get_debug_string_for_kind (enum kind kind) +{ + return diagnostic_kind_debug_text[static_cast<int> (kind)]; +} + static const char *const diagnostic_kind_color[] = { #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C), #include "diagnostics/kinds.def" @@ -671,11 +763,22 @@ convert_column_unit (file_cache &fc, } } +/* Given an expanded_location, convert the column (which is in 1-based bytes) + to the requested units and origin. Return -1 if the column is + invalid (<= 0). */ +int +column_options::convert_column (file_cache &fc, + expanded_location s) const +{ + int one_based_col = convert_column_unit (fc, m_column_unit, m_tabstop, s); + if (one_based_col <= 0) + return -1; + return one_based_col + (m_column_origin - 1); +} + column_policy::column_policy (const context &dc) : m_file_cache (dc.get_file_cache ()), - m_column_unit (dc.m_column_unit), - m_column_origin (dc.m_column_origin), - m_tabstop (dc.m_tabstop) + m_column_options (dc.get_column_options ()) { } @@ -685,11 +788,7 @@ column_policy::column_policy (const context &dc) int column_policy::converted_column (expanded_location s) const { - int one_based_col = convert_column_unit (m_file_cache, - m_column_unit, m_tabstop, s); - if (one_based_col <= 0) - return -1; - return one_based_col + (m_column_origin - 1); + return m_column_options.convert_column (m_file_cache, s); } /* Return a string describing a location e.g. "foo.c:42:10". */ @@ -711,7 +810,7 @@ column_policy::get_location_text (const expanded_location &s, col = converted_column (s); } - const char *line_col = maybe_line_and_column (line, col); + const char *line_col = text_sink::maybe_line_and_column (line, col); return label_text::take (build_message_string ("%s%s%s:%s", locus_cs, file, line_col, locus_ce)); } @@ -1098,7 +1197,7 @@ context::get_any_inlining_info (diagnostic_info *diagnostic) /* 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. */ - m_set_locations_cb (this, diagnostic); + m_set_locations_cb (*this, diagnostic); else { /* When there's no callback use just the one location provided @@ -1231,6 +1330,9 @@ context::emit_diagnostic_with_group_va (enum kind kind, bool context::report_diagnostic (diagnostic_info *diagnostic) { + auto logger = get_logger (); + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (logger, "diagnostics::context::report_diagnostic"); + enum kind orig_diag_kind = diagnostic->m_kind; /* Every call to report_diagnostic should be within a @@ -1245,11 +1347,13 @@ context::report_diagnostic (diagnostic_info *diagnostic) if (was_warning && m_inhibit_warnings) { inhibit_notes_in_group (); + if (m_logger) + m_logger->log_printf ("rejecting: inhibiting warnings"); return false; } if (m_adjust_diagnostic_info) - m_adjust_diagnostic_info (this, diagnostic); + m_adjust_diagnostic_info (*this, diagnostic); if (diagnostic->m_kind == kind::pedwarn) { @@ -1260,7 +1364,11 @@ context::report_diagnostic (diagnostic_info *diagnostic) } if (diagnostic->m_kind == kind::note && m_inhibit_notes_p) - return false; + { + if (m_logger) + m_logger->log_printf ("rejecting: inhibiting notes"); + return false; + } /* If the user requested that warnings be treated as errors, so be it. Note that we do this before the next block so that @@ -1277,6 +1385,8 @@ context::report_diagnostic (diagnostic_info *diagnostic) stack. . */ if (!diagnostic_enabled (diagnostic)) { + if (m_logger) + m_logger->log_printf ("rejecting: diagnostic not enabled"); inhibit_notes_in_group (); return false; } @@ -1285,13 +1395,21 @@ context::report_diagnostic (diagnostic_info *diagnostic) && ((!m_warn_system_headers && diagnostic->m_iinfo.m_allsyslocs) || m_inhibit_warnings)) - /* 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; + { + /* Bail if the warning is not to be reported because all locations in the + inlining stack (if there is one) are in system headers. */ + if (m_logger) + m_logger->log_printf ("rejecting: warning in system header"); + return false; + } if (diagnostic->m_kind == kind::note && notes_inhibited_in_group ()) - /* Bail for all the notes in the diagnostic_group that started to inhibit notes. */ - return false; + { + /* Bail for all the notes in the diagnostic_group that started to inhibit notes. */ + if (m_logger) + m_logger->log_printf ("rejecting: notes inhibited within group"); + return false; + } if (diagnostic->m_kind != kind::note && diagnostic->m_kind != kind::ice) check_max_errors (false); @@ -1352,8 +1470,13 @@ context::report_diagnostic (diagnostic_info *diagnostic) /* Is this the initial diagnostic within the stack of groups? */ if (m_diagnostic_groups.m_emission_count == 0) - for (auto sink_ : m_sinks) - sink_->on_begin_group (); + { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 + (get_logger (), + "diagnostics::context: beginning group"); + for (auto sink_ : m_sinks) + sink_->on_begin_group (); + } m_diagnostic_groups.m_emission_count++; va_list *orig_args = diagnostic->m_message.m_args_ptr; @@ -1385,6 +1508,8 @@ context::report_diagnostic (diagnostic_info *diagnostic) sink_->on_report_diagnostic (*diagnostic, orig_diag_kind); } + const int tabstop = get_column_options ().m_tabstop; + switch (m_extra_output_kind) { default: @@ -1393,14 +1518,14 @@ context::report_diagnostic (diagnostic_info *diagnostic) print_parseable_fixits (get_file_cache (), m_reference_printer, diagnostic->m_richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, - m_tabstop); + tabstop); pp_flush (m_reference_printer); break; case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2: print_parseable_fixits (get_file_cache (), m_reference_printer, diagnostic->m_richloc, DIAGNOSTICS_COLUMN_UNIT_DISPLAY, - m_tabstop); + tabstop); pp_flush (m_reference_printer); break; } @@ -1510,6 +1635,13 @@ context::diagnostic_impl (rich_location *richloc, const char *gmsgid, va_list *ap, enum kind kind) { + logging::log_function_params + (m_logger, "diagnostics::context::diagnostic_impl") + .log_param_option_id ("option_id", opt_id) + .log_param_kind ("kind", kind) + .log_param_string ("gmsgid", gmsgid); + logging::auto_inc_depth depth_sentinel (m_logger); + diagnostic_info diagnostic; if (kind == diagnostics::kind::permerror) { @@ -1527,7 +1659,11 @@ context::diagnostic_impl (rich_location *richloc, diagnostic.m_option_id = opt_id; } diagnostic.m_metadata = metadata; - return report_diagnostic (&diagnostic); + + bool ret = report_diagnostic (&diagnostic); + if (m_logger) + m_logger->log_bool_return ("diagnostics::context::diagnostic_impl", ret); + return ret; } /* Implement inform_n, warning_n, and error_n, as documented and @@ -1541,6 +1677,13 @@ context::diagnostic_n_impl (rich_location *richloc, const char *plural_gmsgid, va_list *ap, enum kind kind) { + logging::log_function_params + (m_logger, "diagnostics::context::diagnostic_n_impl") + .log_param_option_id ("option_id", opt_id) + .log_param_kind ("kind", kind) + .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid); + logging::auto_inc_depth depth_sentinel (m_logger); + diagnostic_info diagnostic; unsigned long gtn; @@ -1557,7 +1700,11 @@ context::diagnostic_n_impl (rich_location *richloc, if (kind == diagnostics::kind::warning) diagnostic.m_option_id = opt_id; diagnostic.m_metadata = metadata; - return report_diagnostic (&diagnostic); + + bool ret = report_diagnostic (&diagnostic); + if (m_logger) + m_logger->log_bool_return ("diagnostics::context::diagnostic_n_impl", ret); + return ret; } @@ -1659,8 +1806,13 @@ context::end_group () If any diagnostics were emitted, give the context a chance to do something. */ if (m_diagnostic_groups.m_emission_count > 0) - for (auto sink_ : m_sinks) - sink_->on_end_group (); + { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 + (get_logger (), + "diagnostics::context::end_group: ending group"); + for (auto sink_ : m_sinks) + sink_->on_end_group (); + } m_diagnostic_groups.m_emission_count = 0; } /* We're popping one level, so might need to stop inhibiting notes. */ @@ -1682,9 +1834,15 @@ context::pop_nesting_level () } void +context::set_nesting_level (int new_level) +{ + m_diagnostic_groups.m_diagnostic_nesting_level = new_level; +} + +void sink::dump (FILE *out, int indent) const { - fprintf (out, "%*sprinter:\n", indent, ""); + dumping::emit_heading (out, indent, "printer"); m_printer->dump (out, indent + 2); } @@ -1771,19 +1929,19 @@ counters::counters () void counters::dump (FILE *out, int indent) const { - fprintf (out, "%*scounts:\n", indent, ""); + dumping::emit_heading (out, indent, "counts"); bool none = true; for (int i = 0; i < static_cast<int> (kind::last_diagnostic_kind); i++) if (m_count_for_kind[i] > 0) { - fprintf (out, "%*s%s%i\n", - indent + 2, "", + dumping::emit_indent (out, indent + 2); + fprintf (out, "%s%i\n", get_text_for_kind (static_cast<enum kind> (i)), m_count_for_kind[i]); none = false; } if (none) - fprintf (out, "%*s(none)\n", indent + 2, ""); + dumping::emit_none (out, indent + 2); } void @@ -2009,8 +2167,8 @@ assert_location_text (const char *expected_loc_text, = DIAGNOSTICS_COLUMN_UNIT_BYTE) { diagnostics::selftest::test_context dc; - dc.m_column_unit = column_unit; - dc.m_column_origin = origin; + dc.get_column_options ().m_column_unit = column_unit; + dc.get_column_options ().m_column_origin = origin; expanded_location xloc; xloc.file = filename; @@ -2046,8 +2204,8 @@ test_get_location_text () assert_location_text ("foo.c:42:", "foo.c", 42, 10, false); assert_location_text ("foo.c:", "foo.c", 0, 10, false); - diagnostics::maybe_line_and_column (INT_MAX, INT_MAX); - diagnostics::maybe_line_and_column (INT_MIN, INT_MIN); + diagnostics::text_sink::maybe_line_and_column (INT_MAX, INT_MAX); + diagnostics::text_sink::maybe_line_and_column (INT_MIN, INT_MIN); { /* In order to test display columns vs byte columns, we need to create a diff --git a/gcc/diagnostics/context.h b/gcc/diagnostics/context.h index f47370b..9464f6a 100644 --- a/gcc/diagnostics/context.h +++ b/gcc/diagnostics/context.h @@ -23,7 +23,12 @@ along with GCC; see the file COPYING3. If not see #include "lazily-created.h" #include "unique-argv.h" #include "diagnostics/option-classifier.h" +#include "diagnostics/option-id-manager.h" #include "diagnostics/context-options.h" +#include "diagnostics/source-printing-options.h" +#include "diagnostics/column-options.h" +#include "diagnostics/counters.h" +#include "diagnostics/logging.h" namespace diagnostics { @@ -81,84 +86,6 @@ typedef void (*text_finalizer_fn) (text_sink &, const diagnostic_info *, enum kind); -/* Abstract base class for the diagnostic subsystem to make queries - about command-line options. */ - -class option_manager -{ -public: - virtual ~option_manager () {} - - /* Return 1 if option OPT_ID is enabled, 0 if it is disabled, - or -1 if it isn't a simple on-off switch - (or if the value is unknown, typically set later in target). */ - virtual int option_enabled_p (option_id opt_id) const = 0; - - /* Return malloced memory for the name of the option OPT_ID - which enabled a diagnostic, originally of type ORIG_DIAG_KIND but - possibly converted to DIAG_KIND by options such as -Werror. - May return NULL if no name is to be printed. - May be passed 0 as well as the index of a particular option. */ - virtual char *make_option_name (option_id opt_id, - enum kind orig_diag_kind, - enum kind diag_kind) const = 0; - - /* Return malloced memory for a URL describing the option that controls - a diagnostic. - May return NULL if no URL is available. - May be passed 0 as well as the index of a particular option. */ - virtual char *make_option_url (option_id opt_id) const = 0; -}; - -/* A bundle of options relating to printing the user's source code - (potentially with a margin, underlining, labels, etc). */ - -struct source_printing_options -{ - /* True if we should print the source line with a caret indicating - the location. - Corresponds to -fdiagnostics-show-caret. */ - bool enabled; - - /* Maximum width of the source line printed. */ - int max_width; - - /* Character used at the caret when printing source locations. */ - char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES]; - - /* When printing source code, should the characters at carets and ranges - be colorized? (assuming colorization is on at all). - This should be true for frontends that generate range information - (so that the ranges of code are colorized), - and false for frontends that merely specify points within the - source code (to avoid e.g. colorizing just the first character in - a token, which would look strange). */ - bool colorize_source_p; - - /* When printing source code, should labelled ranges be printed? - Corresponds to -fdiagnostics-show-labels. */ - bool show_labels_p; - - /* When printing source code, should there be a left-hand margin - showing line numbers? - Corresponds to -fdiagnostics-show-line-numbers. */ - bool show_line_numbers_p; - - /* If printing source code, what should the minimum width of the margin - be? Line numbers will be right-aligned, and padded to this width. - Corresponds to -fdiagnostics-minimum-margin-width=VALUE. */ - int min_margin_width; - - /* Usable by plugins; if true, print a debugging ruler above the - source output. */ - bool show_ruler_p; - - /* When printing events in an inline path, should we print lines - visualizing links between related events (e.g. for CFG paths)? - Corresponds to -fdiagnostics-show-event-links. */ - bool show_event_links_p; -}; - /* A bundle of state for determining column numbers in diagnostics (tab stops, whether to start at 0 or 1, etc). Uses a file_cache to handle tabs. */ @@ -174,13 +101,11 @@ public: bool show_column, bool colorize) const; - int get_tabstop () const { return m_tabstop; } + int get_tabstop () const { return m_column_options.m_tabstop; } private: file_cache &m_file_cache; - enum diagnostics_column_unit m_column_unit; - int m_column_origin; - int m_tabstop; + column_options m_column_options; }; /* A bundle of state for printing locations within diagnostics @@ -291,28 +216,6 @@ private: enum diagnostics_escape_format m_escape_format; }; -/* A collection of counters of diagnostics, per-kind - (e.g. "3 errors and 1 warning"), for use by both context - and by diagnostics::buffer. */ - -struct counters -{ - counters (); - - void dump (FILE *out, int indent) const; - void DEBUG_FUNCTION dump () const { dump (stderr, 0); } - - int get_count (enum kind kind) const - { - return m_count_for_kind[static_cast<size_t> (kind)]; - } - - void move_to (counters &dest); - void clear (); - - int m_count_for_kind[static_cast<size_t> (kind::last_diagnostic_kind)]; -}; - /* This class encapsulates the state of the diagnostics subsystem as a whole (either directly, or via owned objects of other classes, to avoid global variables). @@ -334,7 +237,7 @@ struct counters - an optional urlifier to inject URLs into formatted messages - counting the number of diagnostics reported of each kind (class diagnostics::counters) - - calling out to a option_manager to determine if + - calling out to a option_id_manager to determine if a particular warning is enabled or disabled - tracking pragmas that enable/disable warnings in a range of source code @@ -362,7 +265,7 @@ public: friend class text_sink; friend class buffer; - typedef void (*set_locations_callback_t) (context *, + typedef void (*set_locations_callback_t) (const context &, diagnostic_info *); void initialize (int n_opts); @@ -373,8 +276,10 @@ public: void finish (); - void dump (FILE *out) const; - void DEBUG_FUNCTION dump () const { dump (stderr); } + void dump (FILE *out, int indent) const; + void DEBUG_FUNCTION dump () const { dump (stderr, 0); } + + logging::logger *get_logger () { return m_logger; } bool execution_failed_p () const; @@ -398,6 +303,7 @@ public: void push_nesting_level (); void pop_nesting_level (); + void set_nesting_level (int new_level); bool warning_enabled_at (location_t loc, option_id opt_id); @@ -432,18 +338,35 @@ public: enum kind new_kind, location_t where) { + logging::log_function_params + (m_logger, "diagnostics::context::classify_diagnostics") + .log_param_option_id ("option_id", opt_id) + .log_param_kind ("new_kind", new_kind) + .log_param_location_t ("where", where); + logging::auto_inc_depth depth_sentinel (m_logger); + return m_option_classifier.classify_diagnostic (this, opt_id, new_kind, where); } - void push_diagnostics (location_t where ATTRIBUTE_UNUSED) + void push_diagnostics (location_t where) { + logging::log_function_params + (m_logger, "diagnostics::context::push_diagnostics") + .log_param_location_t ("where", where); + logging::auto_inc_depth depth_sentinel (m_logger); + m_option_classifier.push (); } void pop_diagnostics (location_t where) { + logging::log_function_params + (m_logger, "diagnostics::context::pop_diagnostics") + .log_param_location_t ("where", where); + logging::auto_inc_depth depth_sentinel (m_logger); + m_option_classifier.pop (where); } @@ -489,6 +412,9 @@ public: } void set_show_path_depths (bool val) { m_show_path_depths = val; } void set_show_option_requested (bool val) { m_show_option_requested = val; } + void set_show_nesting (bool val); + void set_show_nesting_locations (bool val); + void set_show_nesting_levels (bool val); void set_max_errors (int val) { m_max_errors = val; } void set_escape_format (enum diagnostics_escape_format val) { @@ -546,32 +472,32 @@ public: /* Option-related member functions. */ inline bool option_enabled_p (option_id opt_id) const { - if (!m_option_mgr) + if (!m_option_id_mgr) return true; - return m_option_mgr->option_enabled_p (opt_id); + return m_option_id_mgr->option_enabled_p (opt_id); } inline char *make_option_name (option_id opt_id, enum kind orig_diag_kind, enum kind diag_kind) const { - if (!m_option_mgr) + if (!m_option_id_mgr) return nullptr; - return m_option_mgr->make_option_name (opt_id, - orig_diag_kind, - diag_kind); + return m_option_id_mgr->make_option_name (opt_id, + orig_diag_kind, + diag_kind); } inline char *make_option_url (option_id opt_id) const { - if (!m_option_mgr) + if (!m_option_id_mgr) return nullptr; - return m_option_mgr->make_option_url (opt_id); + return m_option_id_mgr->make_option_url (opt_id); } void - set_option_manager (std::unique_ptr<option_manager> mgr, - unsigned lang_mask); + set_option_id_manager (std::unique_ptr<option_id_manager> option_id_mgr, + unsigned lang_mask); unsigned get_lang_mask () const { @@ -668,7 +594,7 @@ public: } void - set_adjust_diagnostic_info_callback (void (*cb) (context *, + set_adjust_diagnostic_info_callback (void (*cb) (const context &, diagnostic_info *)) { m_adjust_diagnostic_info = cb; @@ -688,6 +614,9 @@ public: return m_source_printing; } + column_options &get_column_options () { return m_column_options; } + const column_options &get_column_options () const { return m_column_options; } + void set_caret_max_width (int value); private: @@ -804,11 +733,11 @@ private: /* Client hook to adjust properties of the given diagnostic that we're about to issue, such as its kind. */ - void (*m_adjust_diagnostic_info)(context *, diagnostic_info *); + void (*m_adjust_diagnostic_info)(const context &, diagnostic_info *); /* Owned by the context; this would be a std::unique_ptr if context had a proper ctor. */ - option_manager *m_option_mgr; + option_id_manager *m_option_id_mgr; unsigned m_lang_mask; /* A stack of optional hooks for adding URLs to quoted text strings in @@ -835,6 +764,7 @@ private: bool m_inhibit_notes_p; source_printing_options m_source_printing; + column_options m_column_options; /* True if -freport-bug option is used. */ bool m_report_bug; @@ -844,17 +774,6 @@ private: -fdiagnostics-parseable-fixits and GCC_EXTRA_DIAGNOSTIC_OUTPUT. */ enum diagnostics_extra_output_kind m_extra_output_kind; -public: - /* What units to use when outputting the column number. */ - enum diagnostics_column_unit m_column_unit; - - /* The origin for the column number (1-based or 0-based typically). */ - int m_column_origin; - - /* The size of the tabstop for tab expansion. */ - int m_tabstop; - -private: /* How should non-ASCII/non-printable bytes be escaped when a diagnostic suggests escaping the source code on output. */ enum diagnostics_escape_format m_escape_format; @@ -927,6 +846,11 @@ private: later (if the buffer is flushed), moved to other buffers, or discarded (if the buffer is cleared). */ buffer *m_diagnostic_buffer; + + /* Owned by the context. + Debugging option: if non-NULL, report information to the logger + on what the context is doing. */ + logging::logger *m_logger; }; /* Client supplied function to announce a diagnostic diff --git a/gcc/diagnostics/counters.h b/gcc/diagnostics/counters.h new file mode 100644 index 0000000..42db3fe --- /dev/null +++ b/gcc/diagnostics/counters.h @@ -0,0 +1,51 @@ +/* Counts of per-kind diagnostics. + Copyright (C) 2000-2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_COUNTERS_H +#define GCC_DIAGNOSTICS_COUNTERS_H + +#include "diagnostics/kinds.h" + +namespace diagnostics { + +/* A collection of counters of diagnostics, per-kind + (e.g. "3 errors and 1 warning"), for use by both diagnostics::context + and by diagnostics::buffer. */ + +struct counters +{ + counters (); + + void dump (FILE *out, int indent) const; + void DEBUG_FUNCTION dump () const { dump (stderr, 0); } + + int get_count (enum kind kind) const + { + return m_count_for_kind[static_cast<size_t> (kind)]; + } + + void move_to (counters &dest); + void clear (); + + int m_count_for_kind[static_cast<size_t> (kind::last_diagnostic_kind)]; +}; + +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_COUNTERS_H */ diff --git a/gcc/diagnostics/dumping.cc b/gcc/diagnostics/dumping.cc new file mode 100644 index 0000000..1dbecf4 --- /dev/null +++ b/gcc/diagnostics/dumping.cc @@ -0,0 +1,102 @@ +/* Utilities for implementing "dump" functions for the diagnostics subsystem. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "diagnostics/dumping.h" + +namespace diagnostics { +namespace dumping { + +/* Emit indentation to OUTFILE for the start of a dump line. */ + +void +emit_indent (FILE *outfile, int indent) +{ + fprintf (outfile, "%*s", indent, ""); +} + +/* Emit an indented line to OUTFILE showing a heading. */ + +void +emit_heading (FILE *outfile, int indent, + const char *text) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s:\n", text); +} + +/* Various functions that emit an indented line to OUTFILE + showing "label: value". */ + +void +emit_string_field (FILE *outfile, int indent, + const char *label, const char *value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %s\n", label, value); +} + +void +emit_bool_field (FILE *outfile, int indent, + const char *label, bool value) +{ + emit_string_field (outfile, indent, label, + value ? "true" : "false"); +} + +void +emit_size_t_field (FILE *outfile, int indent, + const char *label, size_t value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: " HOST_SIZE_T_PRINT_DEC "\n", label, value); +} + +void +emit_int_field (FILE *outfile, int indent, + const char *label, int value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %i\n", label, value); +} + +void +emit_unsigned_field (FILE *outfile, int indent, + const char *label, unsigned value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %u\n", label, value); +} + +/* Emit an indented line to OUTFILE reading "(none)". */ + +void +emit_none (FILE *outfile, int indent) +{ + emit_indent (outfile, indent); + fprintf (outfile, "(none)\n"); +} + + +} // namespace dumping { +} // namespace diagnostics diff --git a/gcc/diagnostics/dumping.h b/gcc/diagnostics/dumping.h new file mode 100644 index 0000000..02f6485 --- /dev/null +++ b/gcc/diagnostics/dumping.h @@ -0,0 +1,50 @@ +/* Utilities for implementing "dump" functions for the diagnostics subsystem. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_DUMP_H +#define GCC_DIAGNOSTICS_DUMP_H + +namespace diagnostics { +namespace dumping { + +extern void emit_indent (FILE *outfile, int indent); +extern void emit_heading (FILE *outfile, int indent, + const char *text); + +extern void emit_string_field (FILE *outfile, int indent, + const char *label, const char *value); +extern void emit_bool_field (FILE *outfile, int indent, + const char *label, bool value); +extern void emit_size_t_field (FILE *outfile, int indent, + const char *label, size_t value); +extern void emit_int_field (FILE *outfile, int indent, + const char *label, int value); +extern void emit_unsigned_field (FILE *outfile, int indent, + const char *label, unsigned value); + +extern void emit_none (FILE *outfile, int indent); + +#define DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD(FLAG) \ + dumping::emit_bool_field (outfile, indent, #FLAG, FLAG) + +} // namespace dumping +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_DUMP_H */ diff --git a/gcc/diagnostics/file-cache.cc b/gcc/diagnostics/file-cache.cc index febeb03..0ec0679 100644 --- a/gcc/diagnostics/file-cache.cc +++ b/gcc/diagnostics/file-cache.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "cpplib.h" #include "diagnostics/file-cache.h" +#include "diagnostics/dumping.h" #include "selftest.h" #ifndef HAVE_ICONV @@ -473,7 +474,8 @@ file_cache::dump (FILE *out, int indent) const { for (size_t i = 0; i < m_num_file_slots; ++i) { - fprintf (out, "%*sslot[%i]:\n", indent, "", (int)i); + dumping::emit_indent (out, indent); + fprintf (out, "slot[%i]:\n", (int)i); m_file_slots[i].dump (out, indent + 2); } } @@ -541,27 +543,38 @@ file_cache_slot::dump (FILE *out, int indent) const { if (!m_file_path) { - fprintf (out, "%*s(unused)\n", indent, ""); + dumping::emit_indent (out, indent); + fprintf (out, "(unused)\n"); return; } - fprintf (out, "%*sfile_path: %s\n", indent, "", m_file_path); - fprintf (out, "%*sfp: %p\n", indent, "", (void *)m_fp); - fprintf (out, "%*sneeds_read_p: %i\n", indent, "", (int)needs_read_p ()); - fprintf (out, "%*sneeds_grow_p: %i\n", indent, "", (int)needs_grow_p ()); - fprintf (out, "%*suse_count: %i\n", indent, "", m_use_count); - fprintf (out, "%*ssize: %zi\n", indent, "", m_size); - fprintf (out, "%*snb_read: %zi\n", indent, "", m_nb_read); - fprintf (out, "%*sstart_line_idx: %zi\n", indent, "", m_line_start_idx); - fprintf (out, "%*sline_num: %zi\n", indent, "", m_line_num); - fprintf (out, "%*smissing_trailing_newline: %i\n", - indent, "", (int)m_missing_trailing_newline); - fprintf (out, "%*sline records (%i):\n", - indent, "", m_line_record.length ()); + dumping::emit_string_field (out, indent, "file_path", m_file_path); + { + dumping::emit_indent (out, indent); + fprintf (out, "fp: %p\n", (void *)m_fp); + } + dumping::emit_bool_field (out, indent, "needs_read_p", needs_read_p ()); + dumping::emit_bool_field (out, indent, "needs_grow_p", needs_grow_p ()); + dumping::emit_unsigned_field (out, indent, "use_count", m_use_count); + dumping::emit_size_t_field (out, indent, "size", m_size); + dumping::emit_size_t_field (out, indent, "nb_read", m_nb_read); + dumping::emit_size_t_field (out, indent, "start_line_idx", m_line_start_idx); + dumping::emit_size_t_field (out, indent, "line_num", m_line_num); + dumping::emit_bool_field (out, indent, "missing_trailing_newline", + m_missing_trailing_newline); + { + dumping::emit_indent (out, indent); + fprintf (out, "line records (%i):\n", m_line_record.length ()); + } int idx = 0; for (auto &line : m_line_record) - fprintf (out, "%*s[%i]: line %zi: byte offsets: %zi-%zi\n", - indent + 2, "", - idx++, line.line_num, line.start_pos, line.end_pos); + { + dumping::emit_indent (out, indent); + fprintf (out, ("[%i]:" + " line " HOST_SIZE_T_PRINT_DEC ":" + " byte offsets: " HOST_SIZE_T_PRINT_DEC + "-" HOST_SIZE_T_PRINT_DEC "\n"), + idx++, line.line_num, line.start_pos, line.end_pos); + } } /* Returns TRUE iff the cache would need to be filled with data coming diff --git a/gcc/diagnostics/html-sink.cc b/gcc/diagnostics/html-sink.cc index 13d6309..64dcefe 100644 --- a/gcc/diagnostics/html-sink.cc +++ b/gcc/diagnostics/html-sink.cc @@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/output-file.h" #include "diagnostics/buffering.h" #include "diagnostics/paths.h" +#include "diagnostics/dumping.h" +#include "diagnostics/logging.h" #include "diagnostics/client-data-hooks.h" #include "selftest.h" #include "diagnostics/selftest-context.h" @@ -61,6 +63,16 @@ html_generation_options::html_generation_options () { } +void +html_generation_options::dump (FILE *outfile, int indent) const +{ + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_css); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_javascript); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_sarif); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src); +} + class html_builder; /* Concrete buffering implementation subclass for HTML output. */ @@ -116,6 +128,8 @@ public: const line_maps *line_maps, const html_generation_options &html_gen_opts); + void dump (FILE *out, int indent) const; + void set_main_input_filename (const char *name); @@ -223,11 +237,12 @@ make_span (std::string class_) void html_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*shtml_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "html_sink_buffer"); int idx = 0; for (auto &result : m_results) { - fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx); + dumping::emit_indent (out, indent + 2); + fprintf (out, "result[%i]:\n", idx); result->dump (out); fprintf (out, "\n"); ++idx; @@ -470,6 +485,13 @@ html_builder::html_builder (context &dc, } void +html_builder::dump (FILE *out, int indent) const +{ + dumping::emit_heading (out, indent, "HTML generation options"); + m_html_gen_opts.dump (out, indent + 2); +} + +void html_builder::set_main_input_filename (const char *name) { gcc_assert (m_title_element); @@ -539,7 +561,7 @@ html_builder::on_report_diagnostic (const diagnostic_info &diagnostic, } } -// For ease of comparison with experimental-nesting-show-levels=yes +// For ease of comparison with show-nesting-levels=yes static void add_nesting_level_attr (xml::element &element, @@ -1018,10 +1040,11 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic, // Add any metadata as a suffix to the message if (diagnostic.m_metadata) - { - xp.add_text (" "); - xp.append (make_element_for_metadata (*diagnostic.m_metadata)); - } + if (auto e = make_element_for_metadata (*diagnostic.m_metadata)) + { + xp.add_text (" "); + xp.append (std::move (e)); + } // Add any option as a suffix to the message @@ -1234,6 +1257,9 @@ html_builder::make_element_for_metadata (const metadata &m) (make_metadata_element (std::move (label), std::move (url))); } + if (span_metadata->m_children.empty ()) + return nullptr; + return span_metadata; } @@ -1295,6 +1321,8 @@ html_builder::end_group () void html_builder::flush_to_file (FILE *outf) { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (), + "diagnostics::html_builder::flush_to_file"); if (m_html_gen_opts.m_javascript) { gcc_assert (m_head_element); @@ -1329,8 +1357,9 @@ public: void dump (FILE *out, int indent) const override { - fprintf (out, "%*shtml_sink\n", indent, ""); sink::dump (out, indent); + dumping::emit_heading (out, indent, "html_builder"); + m_builder.dump (out, indent + 2); } void @@ -1363,6 +1392,9 @@ public: on_report_diagnostic (const diagnostic_info &diagnostic, enum kind orig_diag_kind) final override { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 + (get_logger (), + "diagnostics::html_sink::on_report_diagnostic"); m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer); } void on_diagram (const diagram &d) final override @@ -1435,12 +1467,10 @@ public: { m_builder.flush_to_file (m_output_file.get_open_file ()); } - void dump (FILE *out, int indent) const override + void dump_kind (FILE *out) const override { - fprintf (out, "%*shtml_file_sink: %s\n", - indent, "", + fprintf (out, "html_file_sink: %s", m_output_file.get_filename ()); - sink::dump (out, indent); } bool machine_readable_stderr_p () const final override { @@ -1603,6 +1633,10 @@ private: : html_sink (dc, line_maps, html_gen_opts) { } + void dump_kind (FILE *out) const final override + { + fprintf (out, "html_buffered_sink"); + } bool machine_readable_stderr_p () const final override { return true; diff --git a/gcc/diagnostics/html-sink.h b/gcc/diagnostics/html-sink.h index d86bde8..d25ceea 100644 --- a/gcc/diagnostics/html-sink.h +++ b/gcc/diagnostics/html-sink.h @@ -30,6 +30,8 @@ struct html_generation_options { html_generation_options (); + void dump (FILE *out, int indent) const; + bool m_css; bool m_javascript; diff --git a/gcc/diagnostics/kinds.h b/gcc/diagnostics/kinds.h index 7b4a168..1357be5 100644 --- a/gcc/diagnostics/kinds.h +++ b/gcc/diagnostics/kinds.h @@ -38,6 +38,7 @@ enum class kind }; extern const char *get_text_for_kind (enum diagnostics::kind); +extern const char *get_debug_string_for_kind (enum diagnostics::kind); extern const char *get_color_for_kind (enum diagnostics::kind); } // namespace diagnostics diff --git a/gcc/diagnostics/lazy-paths.cc b/gcc/diagnostics/lazy-paths.cc index 4934651..f246eea 100644 --- a/gcc/diagnostics/lazy-paths.cc +++ b/gcc/diagnostics/lazy-paths.cc @@ -125,12 +125,12 @@ test_intraprocedural_path (pretty_printer *event_pp) "double `free'"); } -/* Implementation of diagnostics::option_manager for which all +/* Implementation of diagnostics::option_id_manager for which all options are disabled, for use in selftests. Note that this is *not* called for option_id (0), which means "always warn" */ -class all_warnings_disabled : public diagnostics::option_manager +class all_warnings_disabled : public diagnostics::option_id_manager { public: int option_enabled_p (diagnostics::option_id) const final override @@ -168,7 +168,7 @@ test_emission (pretty_printer *event_pp) is skipped. */ { test_context dc; - dc.set_option_manager (std::make_unique<all_warnings_disabled> (), 0); + dc.set_option_id_manager (std::make_unique<all_warnings_disabled> (), 0); test_rich_location rich_loc (*event_pp); ASSERT_FALSE (rich_loc.m_path.generated_p ()); diff --git a/gcc/diagnostics/logging.cc b/gcc/diagnostics/logging.cc new file mode 100644 index 0000000..cfe23d5 --- /dev/null +++ b/gcc/diagnostics/logging.cc @@ -0,0 +1,72 @@ +/* Utilities for implementing "dump" functions for the diagnostics subsystem. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "diagnostics/logging.h" + +namespace diagnostics { +namespace logging { + +logger::logger (output_file outfile) +: m_outfile (std::move (outfile)), + m_log_depth (0) +{ +} + +void +logger::log_printf (const char *fmt, ...) +{ + emit_indent (); + + va_list ap; + va_start (ap, fmt); + vfprintf (get_stream (), fmt, ap); + va_end (ap); + + emit_newline (); +} + +void +logger::log_bool_return (const char *function_name, bool retval) +{ + log_printf ("%s <- %s", + retval ? "true" : "false", + function_name); +} + +/* Emit indentation to OUTFILE for the start of a log line. */ + +void +logger::emit_indent () const +{ + fprintf (get_stream (), "%*s", get_indent (), ""); +} + +void +logger::emit_newline () const +{ + fputc ('\n', get_stream ()); +} + +} // namespace logging { +} // namespace diagnostics diff --git a/gcc/diagnostics/logging.h b/gcc/diagnostics/logging.h new file mode 100644 index 0000000..34ca95b --- /dev/null +++ b/gcc/diagnostics/logging.h @@ -0,0 +1,230 @@ +/* Debugging code for logging what the diagnostics subsystem is doing. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_LOGGING_H +#define GCC_DIAGNOSTICS_LOGGING_H + +#include "diagnostics/output-file.h" +#include "diagnostics/option-id.h" +#include "diagnostics/kinds.h" + +namespace diagnostics { + +namespace logging { + +/* A class for emitting a temporal log of what the diagnostics subsystem + is doing, for debugging. + We can't use pretty_printer here as we could potentially be debugging + pretty-printing itself. */ + +class logger +{ +public: + logger (output_file outfile); + + /* High-level functions that emit a line of text. */ + void log_printf (const char *fmt, ...) + __attribute__ ((__format__ (printf, 2, 3))); + void log_bool_return (const char *function_name, bool retval); + + /* Lower-level functions for building up a line of text. */ + void emit_indent () const; + void emit_newline () const; + + FILE *get_stream () const + { + return m_outfile.get_open_file (); + } + + int get_indent () const { return m_log_depth * 2; } + + void inc_depth () { m_log_depth++; } + void dec_depth () { m_log_depth--; } + +private: + output_file m_outfile; + int m_log_depth; +}; + +/* RAII class for pushing/popping depth within a logger. */ + +class auto_inc_depth +{ +public: + auto_inc_depth (logger *log) + : m_logger (log) + { + if (m_logger) + m_logger->inc_depth (); + } + ~auto_inc_depth () + { + if (m_logger) + m_logger->dec_depth (); + } + +private: + logger *m_logger; +}; + +/* Class for debugging function call parameters. */ + +class log_function_params +{ +public: + log_function_params (logger *logger_, const char *name) + : m_logger (logger_), + m_first_param (true) + { + if (m_logger) + { + m_logger->emit_indent (); + fprintf (m_logger->get_stream (), "%s (", name); + } + } + ~log_function_params () + { + if (m_logger) + { + fprintf (m_logger->get_stream (), ")"); + m_logger->emit_newline (); + } + } + + log_function_params & + log_param_string (const char *name, const char *value) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), "%s: \"%s\"", name, value); + } + return *this; + } + + log_function_params & + log_param_location_t (const char *name, location_t value) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), + "%s: " HOST_SIZE_T_PRINT_HEX, + name, (fmt_size_t)value); + } + return *this; + } + + log_function_params & + log_param_rich_location (const char *name, const rich_location *richloc) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), + "%s: %p", + name, const_cast<void *> ((const void *)richloc)); + } + return *this; + } + + log_function_params & + log_param_option_id (const char *name, diagnostics::option_id value) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), "%s: %i", name, value.m_idx); + } + return *this; + } + + log_function_params & + log_param_kind (const char *name, enum diagnostics::kind value) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), "%s: %s", + name, get_debug_string_for_kind (value)); + } + return *this; + } + + log_function_params & + log_param_uhwi (const char *name, unsigned HOST_WIDE_INT value) + { + if (m_logger) + { + add_any_comma (); + fprintf (m_logger->get_stream (), + "%s: " HOST_WIDE_INT_PRINT_DEC, + name, value); + } + return *this; + } + + log_function_params & + log_params_n_gmsgids (unsigned HOST_WIDE_INT n, + const char *singular_gmsgid, + const char *plural_gmsgid) + { + return log_param_uhwi ("n", n) + .log_param_string ("singular_gmsgid", singular_gmsgid) + .log_param_string ("plural_gmsgid", plural_gmsgid); + } + +private: + void + add_any_comma () + { + gcc_assert (m_logger); + if (m_first_param) + m_first_param = false; + else + fprintf (m_logger->get_stream (), ", "); + } + + logger *m_logger; + bool m_first_param; +}; + +} // namespace logging +} // namespace diagnostics + +/* Various macros for logging a formatted line, and indenting + further log messages within a scope. */ + +#define DIAGNOSTICS_LOG_SCOPE_PRINTF0(LOGGER, FMT) \ + if (LOGGER) \ + (LOGGER)->log_printf ((FMT)); \ + diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER); + +#define DIAGNOSTICS_LOG_SCOPE_PRINTF1(LOGGER, FMT, ARG0) \ + if (LOGGER) \ + (LOGGER)->log_printf ((FMT), (ARG0)); \ + diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER); + +#define DIAGNOSTICS_LOG_SCOPE_PRINTF2(LOGGER, FMT, ARG0, ARG1) \ + if (LOGGER) \ + (LOGGER)->log_printf ((FMT), (ARG0), (ARG1)); \ + diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER); + +#endif /* ! GCC_DIAGNOSTICS_LOGGING_H */ diff --git a/gcc/diagnostics/metadata.h b/gcc/diagnostics/metadata.h index c28f982..39291ec 100644 --- a/gcc/diagnostics/metadata.h +++ b/gcc/diagnostics/metadata.h @@ -119,6 +119,8 @@ class metadata const lazy_digraphs *m_lazy_digraphs; }; +extern char *get_cwe_url (int cwe); + } // namespace diagnostics #endif /* ! GCC_DIAGNOSTICS_METADATA_H */ diff --git a/gcc/diagnostics/option-id-manager.h b/gcc/diagnostics/option-id-manager.h new file mode 100644 index 0000000..08add5b --- /dev/null +++ b/gcc/diagnostics/option-id-manager.h @@ -0,0 +1,56 @@ +/* Hooks for giving client-specific meaning to option ids. + Copyright (C) 2000-2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_OPTION_ID_MANAGER_H +#define GCC_DIAGNOSTICS_OPTION_ID_MANAGER_H + +namespace diagnostics { + +/* Abstract base class for the diagnostic subsystem to make queries + about command-line options. */ + +class option_id_manager +{ +public: + virtual ~option_id_manager () {} + + /* Return 1 if option OPT_ID is enabled, 0 if it is disabled, + or -1 if it isn't a simple on-off switch + (or if the value is unknown, typically set later in target). */ + virtual int option_enabled_p (option_id opt_id) const = 0; + + /* Return malloced memory for the name of the option OPT_ID + which enabled a diagnostic, originally of type ORIG_DIAG_KIND but + possibly converted to DIAG_KIND by options such as -Werror. + May return NULL if no name is to be printed. + May be passed 0 as well as the index of a particular option. */ + virtual char *make_option_name (option_id opt_id, + enum kind orig_diag_kind, + enum kind diag_kind) const = 0; + + /* Return malloced memory for a URL describing the option that controls + a diagnostic. + May return NULL if no URL is available. + May be passed 0 as well as the index of a particular option. */ + virtual char *make_option_url (option_id opt_id) const = 0; +}; + +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_OPTION_ID_MANAGER_H */ diff --git a/gcc/diagnostics/output-file.h b/gcc/diagnostics/output-file.h index f936387..827adcd 100644 --- a/gcc/diagnostics/output-file.h +++ b/gcc/diagnostics/output-file.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_DIAGNOSTICS_OUTPUT_FILE_H #define GCC_DIAGNOSTICS_OUTPUT_FILE_H +#include "label-text.h" + namespace diagnostics { /* RAII class for wrapping a FILE * that could be borrowed or owned, diff --git a/gcc/diagnostics/output-spec.cc b/gcc/diagnostics/output-spec.cc index 83f128c..e25d25a 100644 --- a/gcc/diagnostics/output-spec.cc +++ b/gcc/diagnostics/output-spec.cc @@ -182,10 +182,6 @@ public: const scheme_name_and_params &parsed_arg) const final override; private: - static sarif_generation_options - make_sarif_gen_opts (enum sarif_version version, - bool state_graph); - static std::unique_ptr<sarif_serialization_format> make_sarif_serialization_object (enum sarif_serialization_kind); }; @@ -368,7 +364,7 @@ text_scheme_handler::make_sink (const context &ctxt, const scheme_name_and_params &parsed_arg) const { bool show_color = pp_show_color (dc.get_reference_printer ()); - bool show_nesting = false; + bool show_nesting = true; bool show_locations_in_nesting = true; bool show_levels = false; for (auto& iter : parsed_arg.m_kvs) @@ -381,21 +377,21 @@ text_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "experimental-nesting") + if (key == "show-nesting") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_nesting)) return nullptr; continue; } - if (key == "experimental-nesting-show-locations") + if (key == "show-nesting-locations") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_locations_in_nesting)) return nullptr; continue; } - if (key == "experimental-nesting-show-levels") + if (key == "show-nesting-levels") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels)) return nullptr; @@ -405,9 +401,9 @@ text_scheme_handler::make_sink (const context &ctxt, /* Key not found. */ auto_vec<const char *> known_keys; known_keys.safe_push ("color"); - known_keys.safe_push ("experimental-nesting"); - known_keys.safe_push ("experimental-nesting-show-locations"); - known_keys.safe_push ("experimental-nesting-show-levels"); + known_keys.safe_push ("show-nesting"); + known_keys.safe_push ("show-nesting-locations"); + known_keys.safe_push ("show-nesting-levels"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; @@ -431,8 +427,7 @@ sarif_scheme_handler::make_sink (const context &ctxt, label_text filename; enum sarif_serialization_kind serialization_kind = sarif_serialization_kind::json; - enum sarif_version version = sarif_version::v2_1_0; - bool state_graph = false; + sarif_generation_options sarif_gen_opts; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -463,17 +458,18 @@ sarif_scheme_handler::make_sink (const context &ctxt, {{{"2.1", sarif_version::v2_1_0}, {"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}}; - if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg, - key, value, - value_names, - version)) + if (!parse_enum_value<enum sarif_version> + (ctxt, unparsed_arg, + key, value, + value_names, + sarif_gen_opts.m_version)) return nullptr; continue; } if (key == "state-graphs") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - state_graph)) + sarif_gen_opts.m_state_graph)) return nullptr; continue; } @@ -513,8 +509,6 @@ sarif_scheme_handler::make_sink (const context &ctxt, if (!output_file_) return nullptr; - auto sarif_gen_opts = make_sarif_gen_opts (version, state_graph); - auto serialization_obj = make_sarif_serialization_object (serialization_kind); auto sink = make_sarif_sink (dc, @@ -525,16 +519,6 @@ sarif_scheme_handler::make_sink (const context &ctxt, return sink; } -sarif_generation_options -sarif_scheme_handler::make_sarif_gen_opts (enum sarif_version version, - bool state_graph) -{ - sarif_generation_options sarif_gen_opts; - sarif_gen_opts.m_version = version; - sarif_gen_opts.m_state_graph = state_graph; - return sarif_gen_opts; -} - std::unique_ptr<sarif_serialization_format> sarif_scheme_handler:: make_sarif_serialization_object (enum sarif_serialization_kind kind) @@ -557,12 +541,8 @@ html_scheme_handler::make_sink (const context &ctxt, const char *unparsed_arg, const scheme_name_and_params &parsed_arg) const { - bool css = true; label_text filename; - bool javascript = true; - bool show_state_diagrams = false; - bool show_state_diagrams_sarif = false; - bool show_state_diagrams_dot_src = false; + html_generation_options html_gen_opts; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -570,7 +550,7 @@ html_scheme_handler::make_sink (const context &ctxt, if (key == "css") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - css)) + html_gen_opts.m_css)) return nullptr; continue; } @@ -582,28 +562,28 @@ html_scheme_handler::make_sink (const context &ctxt, if (key == "javascript") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - javascript)) + html_gen_opts.m_javascript)) return nullptr; continue; } if (key == "show-state-diagrams") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagrams)) + html_gen_opts.m_show_state_diagrams)) return nullptr; continue; } if (key == "show-state-diagrams-dot-src") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagrams_dot_src)) + html_gen_opts.m_show_state_diagrams_dot_src)) return nullptr; continue; } if (key == "show-state-diagrams-sarif") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagrams_sarif)) + html_gen_opts.m_show_state_diagrams_sarif)) return nullptr; continue; } @@ -645,13 +625,6 @@ html_scheme_handler::make_sink (const context &ctxt, if (!output_file_) return nullptr; - html_generation_options html_gen_opts; - html_gen_opts.m_css = css; - html_gen_opts.m_javascript = javascript; - html_gen_opts.m_show_state_diagrams = show_state_diagrams; - html_gen_opts.m_show_state_diagrams_sarif = show_state_diagrams_sarif; - html_gen_opts.m_show_state_diagrams_dot_src = show_state_diagrams_dot_src; - auto sink = make_html_sink (dc, *ctxt.get_affected_location_mgr (), html_gen_opts, diff --git a/gcc/diagnostics/sarif-sink.cc b/gcc/diagnostics/sarif-sink.cc index 4738ae9..c85a35e 100644 --- a/gcc/diagnostics/sarif-sink.cc +++ b/gcc/diagnostics/sarif-sink.cc @@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/paths.h" #include "diagnostics/sink.h" #include "diagnostics/buffering.h" +#include "diagnostics/dumping.h" +#include "diagnostics/logging.h" #include "json.h" #include "cpplib.h" #include "diagnostics/logical-locations.h" @@ -704,6 +706,14 @@ sarif_serialization_format_json::write_to_file (FILE *outf, fprintf (outf, "\n"); } +void +sarif_serialization_format_json::dump (FILE *outfile, int indent) const +{ + dumping::emit_indent (outfile, indent); + fprintf (outfile, "json\n"); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_formatted); +} + /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr and -fdiagnostics-format=sarif-file). @@ -760,6 +770,8 @@ public: const sarif_generation_options &sarif_gen_opts); ~sarif_builder (); + void dump (FILE *out, int indent) const; + void set_printer (pretty_printer &printer) { m_printer = &printer; @@ -1674,7 +1686,7 @@ sarif_builder::sarif_builder (diagnostics::context &dc, (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()), m_run_graphs (std::make_unique<sarif_array_of_unique<sarif_graph>> ()), - m_tabstop (dc.m_tabstop), + m_tabstop (dc.get_column_options ().m_tabstop), m_serialization_format (std::move (serialization_format)), m_sarif_gen_opts (sarif_gen_opts), m_next_result_idx (0), @@ -1699,6 +1711,15 @@ sarif_builder::~sarif_builder () } } +void +sarif_builder::dump (FILE *out, int indent) const +{ + dumping::emit_heading (out, indent, "serialization format"); + m_serialization_format->dump (out, indent + 2); + dumping::emit_heading (out, indent, "SARIF generation options"); + m_sarif_gen_opts.dump (out, indent + 2); +} + /* Functions at which to stop the backtrace print. It's not particularly helpful to print the callers of these functions. */ @@ -1924,6 +1945,8 @@ report_global_digraph (const lazily_created<digraphs::digraph> &ldg) std::unique_ptr<sarif_log> sarif_builder::flush_to_object () { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (), + "diagnostics::sarif_builder::flush_to_object"); m_invocation_obj->prepare_to_flush (*this); std::unique_ptr<sarif_log> top = make_top_level_object (std::move (m_invocation_obj), @@ -1939,6 +1962,8 @@ sarif_builder::flush_to_object () void sarif_builder::flush_to_file (FILE *outf) { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (), + "diagnostics::sarif_builder::flush_to_file"); std::unique_ptr<sarif_log> top = flush_to_object (); m_serialization_format->write_to_file (outf, *top); } @@ -3803,11 +3828,12 @@ sarif_builder::make_artifact_content_object (const char *text) const void sarif_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*ssarif_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "sarif_sink_buffer"); int idx = 0; for (auto &result : m_results) { - fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx); + dumping::emit_indent (out, indent + 2); + fprintf (out, "result[%i]:\n", idx); result->dump (out, true); fprintf (out, "\n"); ++idx; @@ -3862,8 +3888,9 @@ public: void dump (FILE *out, int indent) const override { - fprintf (out, "%*ssarif_sink\n", indent, ""); sink::dump (out, indent); + dumping::emit_heading (out, indent, "sarif_builder"); + m_builder.dump (out, indent + 2); } void @@ -3918,6 +3945,9 @@ public: on_report_diagnostic (const diagnostic_info &diagnostic, enum kind orig_diag_kind) final override { + DIAGNOSTICS_LOG_SCOPE_PRINTF0 + (get_logger (), + "diagnostics::sarif_sink::on_report_diagnostic"); m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer); } void on_diagram (const diagram &d) final override @@ -3973,6 +4003,10 @@ public: { m_builder.flush_to_file (m_stream); } + void dump_kind (FILE *out) const override + { + fprintf (out, "sarif_stream_sink"); + } bool machine_readable_stderr_p () const final override { return m_stream == stderr; @@ -4001,12 +4035,10 @@ public: { m_builder.flush_to_file (m_output_file.get_open_file ()); } - void dump (FILE *out, int indent) const override + void dump_kind (FILE *out) const override { - fprintf (out, "%*ssarif_file_sink: %s\n", - indent, "", + fprintf (out, "sarif_file_sink: %s", m_output_file.get_filename ()); - sink::dump (out, indent); } bool machine_readable_stderr_p () const final override { @@ -4319,6 +4351,29 @@ sarif_generation_options::sarif_generation_options () { } +static const char * +get_dump_string_for_sarif_version (enum sarif_version version) +{ + switch (version) + { + default: + gcc_unreachable (); + case sarif_version::v2_1_0: + return "v2_1_0"; + case sarif_version::v2_2_prerelease_2024_08_08: + return "v2_2_prerelease_2024_08_08"; + } +} + +void +sarif_generation_options::dump (FILE *outfile, int indent) const +{ + dumping::emit_string_field (outfile, indent, + "m_version", + get_dump_string_for_sarif_version (m_version)); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_state_graph); +} + #if CHECKING_P namespace selftest { @@ -4406,10 +4461,10 @@ public: test_sarif_diagnostic_context (const char *main_input_filename, const sarif_generation_options &sarif_gen_opts) { - auto sink_ = std::make_unique<buffered_sink> (*this, - line_table, - true, - sarif_gen_opts); + auto sink_ = std::make_unique<sarif_buffered_sink> (*this, + line_table, + true, + sarif_gen_opts); m_sink = sink_.get (); // borrowed init_sarif_sink (*this, std::move (sink_)); m_sink->set_main_input_filename (main_input_filename); @@ -4424,10 +4479,10 @@ public: sarif_result &get_result (size_t idx) { return m_sink->get_result (idx); } private: - class buffered_sink : public sarif_sink + class sarif_buffered_sink : public sarif_sink { public: - buffered_sink (context &dc, + sarif_buffered_sink (context &dc, const line_maps *line_maps, bool formatted, const sarif_generation_options &sarif_gen_opts) @@ -4436,6 +4491,10 @@ private: sarif_gen_opts) { } + void dump_kind (FILE *out) const final override + { + fprintf (out, "sarif_buffered_sink"); + } bool machine_readable_stderr_p () const final override { return false; @@ -4446,7 +4505,7 @@ private: } }; - buffered_sink *m_sink; // borrowed + sarif_buffered_sink *m_sink; // borrowed }; /* Test making a sarif_location for a complex rich_location diff --git a/gcc/diagnostics/sarif-sink.h b/gcc/diagnostics/sarif-sink.h index 9f8a73f..e6f897b 100644 --- a/gcc/diagnostics/sarif-sink.h +++ b/gcc/diagnostics/sarif-sink.h @@ -73,6 +73,7 @@ public: virtual ~sarif_serialization_format () {} virtual void write_to_file (FILE *outf, const json::value &top) = 0; + virtual void dump (FILE *out, int indent) const = 0; }; /* Concrete subclass for serializing SARIF as JSON. */ @@ -85,6 +86,7 @@ public: { } void write_to_file (FILE *outf, const json::value &top) final override; + void dump (FILE *out, int indent) const final override; private: bool m_formatted; @@ -108,6 +110,8 @@ struct sarif_generation_options { sarif_generation_options (); + void dump (FILE *out, int indent) const; + enum sarif_version m_version; bool m_state_graph; }; diff --git a/gcc/diagnostics/sink.h b/gcc/diagnostics/sink.h index ac4e0fb64..aaa6c50 100644 --- a/gcc/diagnostics/sink.h +++ b/gcc/diagnostics/sink.h @@ -36,6 +36,9 @@ class sink public: virtual ~sink () {} + virtual text_sink *dyn_cast_text_sink () { return nullptr; } + + virtual void dump_kind (FILE *out) const = 0; virtual void dump (FILE *out, int indent) const; /* Vfunc for notifying this format what the primary input file is, @@ -87,6 +90,8 @@ public: void DEBUG_FUNCTION dump () const { dump (stderr, 0); } + logging::logger *get_logger () { return m_context.get_logger (); } + protected: sink (context &dc) : m_context (dc), diff --git a/gcc/diagnostics/source-printing-options.h b/gcc/diagnostics/source-printing-options.h new file mode 100644 index 0000000..362b691 --- /dev/null +++ b/gcc/diagnostics/source-printing-options.h @@ -0,0 +1,76 @@ +/* Options relating to printing the user's source code. + Copyright (C) 2000-2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTICS_SOURCE_PRINTING_OPTIONS_H +#define GCC_DIAGNOSTICS_SOURCE_PRINTING_OPTIONS_H + +namespace diagnostics { + +/* A bundle of options relating to printing the user's source code + (potentially with a margin, underlining, labels, etc). */ + +struct source_printing_options +{ + /* True if we should print the source line with a caret indicating + the location. + Corresponds to -fdiagnostics-show-caret. */ + bool enabled; + + /* Maximum width of the source line printed. */ + int max_width; + + /* Character used at the caret when printing source locations. */ + char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES]; + + /* When printing source code, should the characters at carets and ranges + be colorized? (assuming colorization is on at all). + This should be true for frontends that generate range information + (so that the ranges of code are colorized), + and false for frontends that merely specify points within the + source code (to avoid e.g. colorizing just the first character in + a token, which would look strange). */ + bool colorize_source_p; + + /* When printing source code, should labelled ranges be printed? + Corresponds to -fdiagnostics-show-labels. */ + bool show_labels_p; + + /* When printing source code, should there be a left-hand margin + showing line numbers? + Corresponds to -fdiagnostics-show-line-numbers. */ + bool show_line_numbers_p; + + /* If printing source code, what should the minimum width of the margin + be? Line numbers will be right-aligned, and padded to this width. + Corresponds to -fdiagnostics-minimum-margin-width=VALUE. */ + int min_margin_width; + + /* Usable by plugins; if true, print a debugging ruler above the + source output. */ + bool show_ruler_p; + + /* When printing events in an inline path, should we print lines + visualizing links between related events (e.g. for CFG paths)? + Corresponds to -fdiagnostics-show-event-links. */ + bool show_event_links_p; +}; + +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_SOURCE_PRINTING_OPTIONS_H */ diff --git a/gcc/diagnostics/source-printing.cc b/gcc/diagnostics/source-printing.cc index 94b1c2d..aeda9ad 100644 --- a/gcc/diagnostics/source-printing.cc +++ b/gcc/diagnostics/source-printing.cc @@ -4412,7 +4412,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; diagnostics::source_print_policy policy (dc); layout test_layout (policy, richloc, nullptr); colorizer col (*dc.get_reference_printer (), @@ -4436,7 +4436,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; static const int small_width = 24; auto &source_printing_opts = dc.get_source_printing_options (); source_printing_opts.max_width = small_width - 4; @@ -6833,7 +6833,7 @@ test_tab_expansion (const line_table_case &case_) everything too. */ { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; rich_location richloc (line_table, linemap_position_for_column (line_table, first_non_ws_byte_col)); @@ -6846,7 +6846,7 @@ test_tab_expansion (const line_table_case &case_) as well. */ { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; rich_location richloc (line_table, linemap_position_for_column (line_table, right_quote_byte_col)); diff --git a/gcc/diagnostics/text-sink.cc b/gcc/diagnostics/text-sink.cc index bcf91cf..d4cfb89 100644 --- a/gcc/diagnostics/text-sink.cc +++ b/gcc/diagnostics/text-sink.cc @@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/diagram.h" #include "diagnostics/text-sink.h" #include "diagnostics/buffering.h" +#include "diagnostics/dumping.h" +#include "diagnostics/logging.h" #include "text-art/theme.h" /* Disable warnings about quoting issues in the pp_xxx calls below @@ -76,7 +78,7 @@ text_sink_buffer::text_sink_buffer (sink &sink_) void text_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*stext_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "text_sink_buffer"); m_output_buffer.dump (out, indent + 2); } @@ -156,18 +158,19 @@ text_sink::~text_sink () } void -text_sink::dump (FILE *out, int indent) const -{ - fprintf (out, "%*stext_sink\n", indent, ""); - fprintf (out, "%*sm_follows_reference_printer: %s\n", - indent, "", - m_follows_reference_printer ? "true" : "false"); - sink::dump (out, indent); - fprintf (out, "%*ssaved_output_buffer:\n", indent + 2, ""); +text_sink::dump (FILE *outfile, int indent) const +{ + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_follows_reference_printer); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_nesting); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_locations_in_nesting); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_nesting_levels); + + sink::dump (outfile, indent); + dumping::emit_heading (outfile, indent, "saved_output_buffer"); if (m_saved_output_buffer) - m_saved_output_buffer->dump (out, indent + 4); + m_saved_output_buffer->dump (outfile, indent + 2); else - fprintf (out, "%*s(none):\n", indent + 4, ""); + dumping::emit_none (outfile, indent + 2); } void @@ -203,6 +206,11 @@ void text_sink::on_report_diagnostic (const diagnostic_info &diagnostic, enum kind orig_diag_kind) { + auto logger = get_logger (); + DIAGNOSTICS_LOG_SCOPE_PRINTF0 + (logger, + "diagnostics::text_sink::on_report_diagnostic"); + pretty_printer *pp = get_printer (); (*text_starter (&m_context)) (*this, &diagnostic); @@ -612,7 +620,7 @@ text_sink::get_location_text (const expanded_location &s) const The result is a statically allocated buffer. */ const char * -maybe_line_and_column (int line, int col) +text_sink::maybe_line_and_column (int line, int col) { static char result[32]; diff --git a/gcc/diagnostics/text-sink.h b/gcc/diagnostics/text-sink.h index 5c60976..f280e72 100644 --- a/gcc/diagnostics/text-sink.h +++ b/gcc/diagnostics/text-sink.h @@ -51,6 +51,12 @@ public: {} ~text_sink (); + text_sink *dyn_cast_text_sink () final override { return this; } + + void dump_kind (FILE *out) const override + { + fprintf (out, "text_sink"); + } void dump (FILE *out, int indent) const override; std::unique_ptr<per_sink_buffer> @@ -127,6 +133,8 @@ public: return m_source_printing; } + static const char *maybe_line_and_column (int line, int col); + protected: void print_any_cwe (const diagnostic_info &diagnostic); void print_any_rules (const diagnostic_info &diagnostic); |