/* C++ implementation of a pure C API for emitting diagnostics. Copyright (C) 2023-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 . */ #include "config.h" #define INCLUDE_MAP #define INCLUDE_STRING #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "intl.h" #include "diagnostic.h" #include "diagnostics/color.h" #include "diagnostics/file-cache.h" #include "diagnostics/url.h" #include "diagnostics/metadata.h" #include "diagnostics/paths.h" #include "diagnostics/client-data-hooks.h" #include "diagnostics/sarif-sink.h" #include "diagnostics/text-sink.h" #include "diagnostics/output-spec.h" #include "diagnostics/digraphs.h" #include "diagnostics/state-graphs.h" #include "diagnostics/logical-locations.h" #include "diagnostics/changes.h" #include "libgdiagnostics.h" #include "libgdiagnostics-private.h" #include "pretty-print-format-impl.h" #include "pretty-print-markup.h" #include "auto-obstack.h" class owned_nullable_string { public: owned_nullable_string () : m_str (nullptr) {} owned_nullable_string (const char *str) : m_str (str ? ::xstrdup (str) : nullptr) { } owned_nullable_string (const owned_nullable_string &other) : m_str (other.xstrdup ()) { } owned_nullable_string (owned_nullable_string &&other) { m_str = other.m_str; other.m_str = nullptr; } ~owned_nullable_string () { free (m_str); } void set (const char *str) { free (m_str); m_str = str ? ::xstrdup (str) : nullptr; } const char *get_str () const { return m_str; } char *xstrdup () const { return m_str ? ::xstrdup (m_str) : nullptr; } bool operator< (const owned_nullable_string &other) const { if (m_str && other.m_str) return strcmp (m_str, other.m_str) < 0; if (m_str == nullptr && other.m_str != nullptr) return true; return false; } private: char *m_str; }; class content_buffer { public: content_buffer (const char *data, size_t sz) : m_data (xmalloc (sz)), m_sz (sz) { memcpy (m_data, data, sz); } ~content_buffer () { free (m_data); } void *m_data; size_t m_sz; }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_file { diagnostic_file (diagnostic_manager &mgr, const char *name, const char *sarif_source_language) : m_mgr (mgr), m_name (name), m_sarif_source_language (sarif_source_language) { } const char *get_name () const { return m_name.get_str (); } const char *get_sarif_source_language () const { return m_sarif_source_language.get_str (); } const content_buffer * get_content () const { return m_content.get (); } void set_buffered_content (const char *buf, size_t sz); private: diagnostic_manager &m_mgr; owned_nullable_string m_name; owned_nullable_string m_sarif_source_language; std::unique_ptr m_content; }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_physical_location { diagnostic_physical_location (diagnostic_manager *mgr, location_t inner) : m_mgr (mgr), m_inner (inner) {} diagnostic_file *get_file () const; diagnostic_manager *m_mgr; location_t m_inner; }; static location_t as_location_t (const diagnostic_physical_location *loc) { if (!loc) return UNKNOWN_LOCATION; return loc->m_inner; } /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_logical_location { diagnostic_logical_location (enum diagnostic_logical_location_kind_t kind, const diagnostic_logical_location *parent, const char *short_name, const char *fully_qualified_name, const char *decorated_name) : m_kind (kind), m_parent (parent), m_short_name (short_name), m_fully_qualified_name (fully_qualified_name), m_decorated_name (decorated_name) { } bool operator< (const diagnostic_logical_location &other) const { if (m_kind < other.m_kind) return true; if (m_parent < other.m_parent) return true; if (m_short_name < other.m_short_name) return true; if (m_fully_qualified_name < other.m_fully_qualified_name) return true; if (m_decorated_name < other.m_decorated_name) return true; return false; } enum diagnostic_logical_location_kind_t m_kind; const diagnostic_logical_location *m_parent; owned_nullable_string m_short_name; owned_nullable_string m_fully_qualified_name; owned_nullable_string m_decorated_name; }; static diagnostic_event_id as_diagnostic_event_id (diagnostics::paths::event_id_t id) { return id.zero_based (); } class sink { protected: sink (diagnostic_manager &mgr) : m_mgr (mgr) {} diagnostic_manager &m_mgr; }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_text_sink : public sink { public: diagnostic_text_sink (diagnostic_manager &mgr, FILE *dst_stream, enum diagnostic_colorize colorize); diagnostics::source_printing_options &get_source_printing_options () { return m_source_printing; } void set_colorize (enum diagnostic_colorize colorize); static void text_starter (diagnostics::text_sink &text_output, const diagnostics::diagnostic_info *diagnostic); private: diagnostics::text_sink *m_inner_sink; // borrowed from dc diagnostics::source_printing_options m_source_printing; }; /* A token_printer that makes a deep copy of the pp_token_list into another obstack. */ class copying_token_printer : public token_printer { public: copying_token_printer (obstack &dst_obstack, pp_token_list &dst_token_list) : m_dst_obstack (dst_obstack), m_dst_token_list (dst_token_list) { } void print_tokens (pretty_printer *, const pp_token_list &tokens) final override { for (auto iter = tokens.m_first; iter; iter = iter->m_next) switch (iter->m_kind) { default: gcc_unreachable (); case pp_token::kind::text: { const pp_token_text *sub = as_a (iter); /* Copy the text, with null terminator. */ obstack_grow (&m_dst_obstack, sub->m_value.get (), strlen (sub->m_value.get ()) + 1); m_dst_token_list.push_back_text (label_text::borrow (XOBFINISH (&m_dst_obstack, const char *))); } break; case pp_token::kind::begin_color: { pp_token_begin_color *sub = as_a (iter); /* Copy the color, with null terminator. */ obstack_grow (&m_dst_obstack, sub->m_value.get (), strlen (sub->m_value.get ()) + 1); m_dst_token_list.push_back (label_text::borrow (XOBFINISH (&m_dst_obstack, const char *))); } break; case pp_token::kind::end_color: m_dst_token_list.push_back (); break; case pp_token::kind::begin_quote: m_dst_token_list.push_back (); break; case pp_token::kind::end_quote: m_dst_token_list.push_back (); break; case pp_token::kind::begin_url: { pp_token_begin_url *sub = as_a (iter); /* Copy the URL, with null terminator. */ obstack_grow (&m_dst_obstack, sub->m_value.get (), strlen (sub->m_value.get ()) + 1); m_dst_token_list.push_back (label_text::borrow (XOBFINISH (&m_dst_obstack, const char *))); } break; case pp_token::kind::end_url: m_dst_token_list.push_back (); break; case pp_token::kind::event_id: { pp_token_event_id *sub = as_a (iter); m_dst_token_list.push_back (sub->m_event_id); } break; case pp_token::kind::custom_data: /* These should have been eliminated by replace_custom_tokens. */ gcc_unreachable (); break; } } private: obstack &m_dst_obstack; pp_token_list &m_dst_token_list; }; class sarif_sink : public sink { public: sarif_sink (diagnostic_manager &mgr, FILE *dst_stream, const diagnostic_file *main_input_file, const diagnostics::sarif_generation_options &sarif_gen_opts); }; struct diagnostic_message_buffer { diagnostic_message_buffer () : m_tokens (m_obstack) { } diagnostic_message_buffer (const char *gmsgid, va_list *args) : m_tokens (m_obstack) { text_info text (gmsgid, args, errno); pretty_printer pp; pp.set_output_stream (nullptr); copying_token_printer tok_printer (m_obstack, m_tokens); pp.set_token_printer (&tok_printer); pp_format (&pp, &text); pp_output_formatted_text (&pp, nullptr); } std::string to_string () const; auto_obstack m_obstack; pp_token_list m_tokens; }; /* A pp_element subclass that replays the saved tokens in a diagnostic_message_buffer. */ class pp_element_message_buffer : public pp_element { public: pp_element_message_buffer (diagnostic_message_buffer &msg_buf) : m_msg_buf (msg_buf) { } void add_to_phase_2 (pp_markup::context &ctxt) final override { /* Convert to text, possibly with colorization, URLs, etc. */ for (auto iter = m_msg_buf.m_tokens.m_first; iter; iter = iter->m_next) switch (iter->m_kind) { default: gcc_unreachable (); case pp_token::kind::text: { pp_token_text *sub = as_a (iter); pp_string (&ctxt.m_pp, sub->m_value.get ()); ctxt.push_back_any_text (); } break; case pp_token::kind::begin_color: { pp_token_begin_color *sub = as_a (iter); ctxt.begin_highlight_color (sub->m_value.get ()); } break; case pp_token::kind::end_color: ctxt.end_highlight_color (); break; case pp_token::kind::begin_quote: ctxt.begin_quote (); break; case pp_token::kind::end_quote: ctxt.end_quote (); break; case pp_token::kind::begin_url: { pp_token_begin_url *sub = as_a (iter); ctxt.begin_url (sub->m_value.get ()); } break; case pp_token::kind::end_url: ctxt.end_url (); break; case pp_token::kind::event_id: { pp_token_event_id *sub = as_a (iter); gcc_assert (sub->m_event_id.known_p ()); ctxt.add_event_id (sub->m_event_id); } break; case pp_token::kind::custom_data: /* We don't have a way of handling custom_data tokens here. */ gcc_unreachable (); break; } } private: diagnostic_message_buffer &m_msg_buf; }; /* Helper for the linemap code. */ static size_t round_alloc_size (size_t s) { return s; } class impl_logical_location_manager : public diagnostics::logical_locations::manager { public: using key = diagnostics::logical_locations::key; using kind = diagnostics::logical_locations::kind; static const diagnostic_logical_location * ptr_from_key (key k) { return k.cast_to (); } static key key_from_ptr (const diagnostic_logical_location *ptr) { return key::from_ptr (ptr); } const char *get_short_name (key k) const final override { if (auto loc = ptr_from_key (k)) return loc->m_short_name.get_str (); else return nullptr; } const char *get_name_with_scope (key k) const final override { if (auto loc = ptr_from_key (k)) return loc->m_fully_qualified_name.get_str (); else return nullptr; } const char *get_internal_name (key k) const final override { if (auto loc = ptr_from_key (k)) return loc->m_decorated_name.get_str (); else return nullptr; } kind get_kind (key k) const final override { auto loc = ptr_from_key (k); gcc_assert (loc); switch (loc->m_kind) { default: gcc_unreachable (); case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: return kind::function; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: return kind::member; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: return kind::module_; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: return kind::namespace_; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: return kind::type; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: return kind::return_type; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: return kind::parameter; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: return kind::variable; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT: return kind::element; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE: return kind::attribute; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT: return kind::text; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT: return kind::comment; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: return kind::processing_instruction; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD: return kind::dtd; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION: return kind::declaration; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT: return kind::object; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY: return kind::array; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY: return kind::property; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE: return kind::value; } } label_text get_name_for_path_output (key k) const final override { auto loc = ptr_from_key (k); gcc_assert (loc); return label_text::borrow (loc->m_short_name.get_str ()); } key get_parent (key k) const final override { auto loc = ptr_from_key (k); gcc_assert (loc); return key_from_ptr (loc->m_parent); } }; class impl_diagnostic_client_data_hooks : public diagnostics::client_data_hooks { public: impl_diagnostic_client_data_hooks (diagnostic_manager &mgr) : m_mgr (mgr) {} const diagnostics::client_version_info * get_any_version_info () const final override; const diagnostics::logical_locations::manager * get_logical_location_manager () const final override { return &m_logical_location_manager; } diagnostics::logical_locations::key get_current_logical_location () const final override; const char * maybe_get_sarif_source_language (const char *filename) const final override; void add_sarif_invocation_properties (diagnostics::sarif_object &invocation_obj) const final override; private: diagnostic_manager &m_mgr; impl_logical_location_manager m_logical_location_manager; }; class impl_client_version_info : public diagnostics::client_version_info { public: const char *get_tool_name () const final override { return m_name.get_str (); } char *maybe_make_full_name () const final override { return m_full_name.xstrdup (); } const char *get_version_string () const final override { return m_version.get_str (); } char *maybe_make_version_url () const final override { return m_version_url.xstrdup (); } void for_each_plugin (plugin_visitor &) const final override { // No-op. } owned_nullable_string m_name; owned_nullable_string m_full_name; owned_nullable_string m_version; owned_nullable_string m_version_url; }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_manager { public: diagnostic_manager () : m_current_diag (nullptr), m_prev_diag_logical_loc (nullptr) { linemap_init (&m_line_table, BUILTINS_LOCATION); m_line_table.m_reallocator = xrealloc; m_line_table.m_round_alloc_size = round_alloc_size; m_line_table.default_range_bits = line_map_suggested_range_bits; diagnostic_initialize (&m_dc, 0); m_dc.remove_all_output_sinks (); /* Get defaults from environemt. These might be overridden by individual sinks. */ diagnostic_color_init (&m_dc, DIAGNOSTICS_COLOR_AUTO); diagnostic_urls_init (&m_dc); m_dc.set_show_cwe (true); m_dc.set_show_rules (true); m_dc.m_show_column = true; auto &source_printing_opts = m_dc.get_source_printing_options (); source_printing_opts.enabled = true; source_printing_opts.colorize_source_p = true; /* We don't currently expose a way for clients to manipulate the following. */ source_printing_opts.show_labels_p = true; source_printing_opts.show_line_numbers_p = true; source_printing_opts.min_margin_width = 6; m_dc.set_path_format (DPF_INLINE_EVENTS); m_dc.m_client_aux_data = this; m_dc.set_client_data_hooks (std::make_unique (*this)); diagnostics::text_starter (&m_dc) = diagnostic_text_sink::text_starter; m_change_set = std::make_unique (m_dc.get_file_cache ()); } ~diagnostic_manager () { diagnostic_finish (&m_dc); for (size_t i = 0; i < m_sinks.size (); i++) m_sinks[i] = nullptr; for (auto iter : m_str_to_file_map) delete iter.second; for (auto iter :m_location_t_map) delete iter.second; free (m_line_table.m_location_adhoc_data_map.data); free (m_line_table.info_ordinary.maps); } line_maps *get_line_table () { return &m_line_table; } diagnostics::context &get_dc () { return m_dc; } const diagnostics::logical_locations::manager & get_logical_location_manager () const { auto mgr = m_dc.get_logical_location_manager (); gcc_assert (mgr); return *mgr; } void write_patch (FILE *dst_stream); void add_sink (std::unique_ptr sink) { m_sinks.push_back (std::move (sink)); } void emit_va (diagnostic &diag, const char *msgid, va_list *args) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0); void emit (diagnostic &diag, const char *msgid, ...) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 4); void emit_msg_buf (diagnostic &diag, diagnostic_message_buffer &msg_buf); diagnostic_file * new_file (const char *name, const char *sarif_source_language) { if (diagnostic_file **slot = m_str_to_file_map.get (name)) return *slot; diagnostic_file *file = new diagnostic_file (*this, name, sarif_source_language); m_str_to_file_map.put (file->get_name (), file); return file; } diagnostic_file * get_file_by_name (const char *name) { if (diagnostic_file **slot = m_str_to_file_map.get (name)) return *slot; return nullptr; } const diagnostic_physical_location * new_location_from_file_and_line (const diagnostic_file *file, diagnostic_line_num_t line_num) { ensure_linemap_for_file_and_line (file, line_num); location_t loc = linemap_position_for_column (&m_line_table, 0); return new_location (loc); } const diagnostic_physical_location * new_location_from_file_line_column (const diagnostic_file *file, diagnostic_line_num_t line_num, diagnostic_column_num_t column_num) { ensure_linemap_for_file_and_line (file, line_num); location_t loc = linemap_position_for_column (&m_line_table, column_num); return new_location (loc); } const diagnostic_physical_location * new_location_from_range (const diagnostic_physical_location *loc_caret, const diagnostic_physical_location *loc_start, const diagnostic_physical_location *loc_end) { return new_location (m_line_table.make_location (as_location_t (loc_caret), as_location_t (loc_start), as_location_t (loc_end))); } const diagnostic_logical_location * new_logical_location (enum diagnostic_logical_location_kind_t kind, const diagnostic_logical_location *parent, const char *short_name, const char *fully_qualified_name, const char *decorated_name) { /* Use m_logical_locs to "uniquify" instances. */ diagnostic_logical_location key (kind, parent, short_name, fully_qualified_name, decorated_name); auto iter = m_logical_locs.find (key); if (iter != m_logical_locs.end ()) return (*iter).second.get (); std::unique_ptr logical_loc = std::make_unique (kind, parent, short_name, fully_qualified_name, decorated_name); const diagnostic_logical_location *result = logical_loc.get (); m_logical_locs.insert (logical_locs_map_t::value_type (std::move (key), std::move (logical_loc))); return result; } diagnostic_execution_path * new_execution_path (); void begin_group () { m_dc.begin_group (); } void end_group () { m_dc.end_group (); } const char * maybe_get_sarif_source_language (const char *filename) { if (diagnostic_file **slot = m_str_to_file_map.get (filename)) { gcc_assert (*slot); return (*slot)->get_sarif_source_language (); } return nullptr; } const diagnostic *get_current_diag () { return m_current_diag; } const diagnostics::client_version_info * get_client_version_info () const { return &m_client_version_info; } impl_client_version_info *get_client_version_info () { return &m_client_version_info; } void assert_valid_diagnostic_physical_location (const diagnostic_physical_location *loc) const { if (!loc) return; gcc_assert (loc->m_mgr == this); } /* TODO: Various things still use the "line_table" global variable. Set it to be this diagnostic_manager's m_line_table. Ideally we should eliminate this global (and this function). */ void set_line_table_global () const { line_table = const_cast (&m_line_table); } const diagnostic_logical_location * get_prev_diag_logical_loc () const { return m_prev_diag_logical_loc; } void take_global_graph (std::unique_ptr graph); private: void ensure_linemap_for_file_and_line (const diagnostic_file *file, diagnostic_line_num_t linenum) { /* Build a simple linemap describing some locations. */ if (LINEMAPS_ORDINARY_USED (&m_line_table) == 0) linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); else { line_map *map = const_cast (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, file->get_name (), 0)); ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; } linemap_line_start (&m_line_table, linenum, 100); } const diagnostic_physical_location * new_location (location_t loc) { if (loc == UNKNOWN_LOCATION) return nullptr; if (diagnostic_physical_location **slot = m_location_t_map.get (loc)) return *slot; diagnostic_physical_location *phys_loc = new diagnostic_physical_location (this, loc); m_location_t_map.put (loc, phys_loc); return phys_loc; } diagnostics::context m_dc; line_maps m_line_table; impl_client_version_info m_client_version_info; std::vector> m_sinks; hash_map m_str_to_file_map; hash_map, diagnostic_physical_location *> m_location_t_map; typedef std::map> logical_locs_map_t; logical_locs_map_t m_logical_locs; const diagnostic *m_current_diag; const diagnostic_logical_location *m_prev_diag_logical_loc; std::unique_ptr m_change_set; }; class impl_rich_location : public rich_location { public: impl_rich_location (line_maps *set) : rich_location (set, UNKNOWN_LOCATION) {} }; class impl_range_label : public range_label { public: impl_range_label (const char *text) : m_text (xstrdup (text)) {} ~impl_range_label () { free (m_text); } label_text get_text (unsigned) const final override { return label_text::borrow (m_text); } private: char *m_text; }; class impl_rule : public diagnostics::metadata::rule { public: impl_rule (const char *title, const char *url) : m_title (title), m_url (url) { } virtual ~impl_rule () {} char *make_description () const final override { return m_title.xstrdup (); } char *make_url () const final override { return m_url.xstrdup (); } private: owned_nullable_string m_title; owned_nullable_string m_url; }; struct diagnostic_graph : public diagnostics::digraphs::digraph { diagnostic_graph (diagnostic_manager &) {} diagnostic_node * add_node_with_id (std::string node_id, diagnostic_node *parent_node); diagnostic_edge * add_edge_with_label (const char *edge_id, diagnostic_node &src_node, diagnostic_node &dst_node, const char *label); }; struct diagnostic_node : public diagnostics::digraphs::node { diagnostic_node (diagnostic_graph &g, std::string id) : node (g, std::move (id)) { } }; struct diagnostic_edge : public diagnostics::digraphs::edge { diagnostic_edge (diagnostic_graph &g, const char *id, diagnostic_node &src_node, diagnostic_node &dst_node) : edge (g, id, src_node, dst_node) { } }; class libgdiagnostics_path_event : public diagnostics::paths::event { public: libgdiagnostics_path_event (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, std::unique_ptr state_graph, std::unique_ptr msg_buf) : m_physical_loc (physical_loc), m_logical_loc (logical_loc), m_stack_depth (stack_depth), m_state_graph (std::move (state_graph)), m_msg_buf (std::move (msg_buf)) { gcc_assert (m_msg_buf); } /* diagnostics::paths::event vfunc implementations. */ location_t get_location () const final override { return as_location_t (m_physical_loc); } int get_stack_depth () const final override { return m_stack_depth; } void print_desc (pretty_printer &pp) const final override { if (m_msg_buf) { pp_element_message_buffer e_msg_buf (*m_msg_buf); pp_printf (&pp, "%e", &e_msg_buf); } } diagnostics::logical_locations::key get_logical_location () const final override { return impl_logical_location_manager::key_from_ptr (m_logical_loc); } meaning get_meaning () const final override { return meaning (); } bool connect_to_next_event_p () const final override { return false; // TODO } diagnostics::paths::thread_id_t get_thread_id () const final override { return 0; } std::unique_ptr maybe_make_diagnostic_state_graph (bool) const final override { if (!m_state_graph) return nullptr; return m_state_graph->clone (); } private: static label_text make_desc (const char *gmsgid, va_list *args, bool colorize) { va_list copy_of_args; va_copy (copy_of_args, *args); // TODO: when should localization happen? text_info text (gmsgid, ©_of_args, errno); pretty_printer pp; pp_show_color (&pp) = colorize; pp.set_output_stream (nullptr); pp_format (&pp, &text); pp_output_formatted_text (&pp, nullptr); label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); va_end (copy_of_args); return result; } const diagnostic_physical_location *m_physical_loc; const diagnostic_logical_location *m_logical_loc; unsigned m_stack_depth; std::unique_ptr m_state_graph; std::unique_ptr m_msg_buf; }; class libgdiagnostics_path_thread : public diagnostics::paths::thread { public: libgdiagnostics_path_thread (const char *name) : m_name (name) {} label_text get_name (bool) const final override { return label_text::borrow (m_name); } private: const char *m_name; // has been i18n-ed and formatted }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic_execution_path : public diagnostics::paths::path { diagnostic_execution_path (const diagnostics::logical_locations::manager &logical_loc_mgr) : diagnostics::paths::path (logical_loc_mgr), m_thread ("") { } diagnostics::paths::event_id_t add_event_va (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, std::unique_ptr state_graph, const char *gmsgid, va_list *args) { auto msg_buf = std::make_unique (gmsgid, args); m_events.push_back (std::make_unique (physical_loc, logical_loc, stack_depth, std::move (state_graph), std::move (msg_buf))); return m_events.size () - 1; } diagnostic_event_id_t add_event_via_msg_buf (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, std::unique_ptr state_graph, std::unique_ptr msg_buf) { m_events.push_back (std::make_unique (physical_loc, logical_loc, stack_depth, std::move (state_graph), std::move (msg_buf))); return m_events.size () - 1; } /* diagnostics::paths::path vfunc implementations. */ unsigned num_events () const final override { return m_events.size (); } const diagnostics::paths::event & get_event (int idx) const final override { return *m_events[idx]; } unsigned num_threads () const final override { return 1; } const diagnostics::paths::thread & get_thread (diagnostics::paths::thread_id_t) const final override { return m_thread; } bool same_function_p (int event_idx_a, int event_idx_b) const final override { using logical_location = diagnostics::logical_locations::key; logical_location logical_loc_a = m_events[event_idx_a]->get_logical_location (); logical_location logical_loc_b = m_events[event_idx_b]->get_logical_location (); /* Pointer equality, as we uniqify logical location instances. */ return logical_loc_a == logical_loc_b; } private: libgdiagnostics_path_thread m_thread; std::vector> m_events; }; class prebuilt_digraphs : public lazily_created>> { public: using digraph = diagnostics::digraphs::digraph; std::unique_ptr>> create_object () const final override { return std::make_unique>> (std::move (m_digraphs)); } void take_graph (std::unique_ptr graph) { m_digraphs.push_back (std::move (graph)); } private: mutable std::vector> m_digraphs; }; /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic { public: diagnostic (diagnostic_manager &diag_mgr, enum diagnostic_level level) : m_diag_mgr (diag_mgr), m_level (level), m_rich_loc (diag_mgr.get_line_table ()), m_logical_loc (nullptr), m_path (nullptr) { m_metadata.set_lazy_digraphs (&m_graphs); } diagnostic_manager &get_manager () const { return m_diag_mgr; } enum diagnostic_level get_level () const { return m_level; } rich_location *get_rich_location () { return &m_rich_loc; } const diagnostics::metadata *get_metadata () { return &m_metadata; } void set_cwe (unsigned cwe_id) { m_metadata.add_cwe (cwe_id); } void add_rule (const char *title, const char *url) { std::unique_ptr rule = std::make_unique (title, url); m_metadata.add_rule (*rule.get ()); m_rules.push_back (std::move (rule)); } void set_location (const diagnostic_physical_location *loc) { m_rich_loc.set_range (0, as_location_t (loc), SHOW_RANGE_WITH_CARET); } void add_location (const diagnostic_physical_location *loc) { m_rich_loc.add_range (as_location_t (loc), SHOW_RANGE_WITHOUT_CARET); } void add_location_with_label (const diagnostic_physical_location *loc, const char *text) { std::unique_ptr label = std::make_unique (text); m_rich_loc.add_range (as_location_t (loc), SHOW_RANGE_WITHOUT_CARET, label.get ()); m_labels.push_back (std::move (label)); } void add_location_with_label (const diagnostic_physical_location *loc, std::unique_ptr msg_buf) { std::string str = msg_buf->to_string (); std::unique_ptr label = std::make_unique (str.c_str ()); m_rich_loc.add_range (as_location_t (loc), SHOW_RANGE_WITHOUT_CARET, label.get ()); m_labels.push_back (std::move (label)); } void set_logical_location (const diagnostic_logical_location *logical_loc) { m_logical_loc = logical_loc; } const diagnostic_logical_location *get_logical_location () const { return m_logical_loc; } diagnostic_execution_path * add_execution_path () { m_path = std::make_unique (m_diag_mgr.get_logical_location_manager ()); m_rich_loc.set_path (m_path.get ()); return m_path.get (); } void take_execution_path (diagnostic_execution_path *path) { m_path = std::unique_ptr (path); m_rich_loc.set_path (path); } void take_graph (std::unique_ptr graph) { m_graphs.take_graph (std::move (graph)); } const prebuilt_digraphs & get_graphs () const { return m_graphs; } private: diagnostic_manager &m_diag_mgr; enum diagnostic_level m_level; impl_rich_location m_rich_loc; const diagnostic_logical_location *m_logical_loc; diagnostics::metadata m_metadata; prebuilt_digraphs m_graphs; std::vector> m_labels; std::vector> m_rules; std::unique_ptr m_path; }; static enum diagnostics::kind diagnostics_kind_from_diagnostic_level (enum diagnostic_level level) { switch (level) { default: gcc_unreachable (); case DIAGNOSTIC_LEVEL_ERROR: return diagnostics::kind::error; case DIAGNOSTIC_LEVEL_WARNING: return diagnostics::kind::warning; case DIAGNOSTIC_LEVEL_NOTE: return diagnostics::kind::note; case DIAGNOSTIC_LEVEL_SORRY: return diagnostics::kind::sorry; } } void diagnostic_file::set_buffered_content (const char *buf, size_t sz) { m_content = std::make_unique (buf, sz); // Populate file_cache: diagnostics::file_cache &fc = m_mgr.get_dc ().get_file_cache (); fc.add_buffered_content (m_name.get_str (), buf, sz); } // struct diagnostic_physical_location diagnostic_file * diagnostic_physical_location::get_file () const { m_mgr->set_line_table_global (); const char *filename = LOCATION_FILE (m_inner); if (!filename) return nullptr; return m_mgr->get_file_by_name (filename); } /* class impl_diagnostic_client_data_hooks. */ const diagnostics::client_version_info * impl_diagnostic_client_data_hooks::get_any_version_info () const { return m_mgr.get_client_version_info (); } diagnostics::logical_locations::key impl_diagnostic_client_data_hooks::get_current_logical_location () const { gcc_assert (m_mgr.get_current_diag ()); return impl_logical_location_manager::key_from_ptr (m_mgr.get_current_diag ()->get_logical_location ()); } const char * impl_diagnostic_client_data_hooks:: maybe_get_sarif_source_language (const char *filename) const { return m_mgr.maybe_get_sarif_source_language (filename); } void impl_diagnostic_client_data_hooks:: add_sarif_invocation_properties (diagnostics::sarif_object &) const { // No-op. } /* struct diagnostic_text_sink : public sink. */ diagnostic_text_sink::diagnostic_text_sink (diagnostic_manager &mgr, FILE *dst_stream, enum diagnostic_colorize colorize) : sink (mgr), m_source_printing (mgr.get_dc ().get_source_printing_options ()) { auto inner_sink = std::make_unique (mgr.get_dc (), &m_source_printing); inner_sink->get_printer ()->set_output_stream (dst_stream); m_inner_sink = inner_sink.get (); set_colorize (colorize); mgr.get_dc ().add_sink (std::move (inner_sink)); } void diagnostic_text_sink::set_colorize (enum diagnostic_colorize colorize) { pretty_printer *const pp = m_inner_sink->get_printer (); switch (colorize) { default: gcc_unreachable (); case DIAGNOSTIC_COLORIZE_IF_TTY: pp_show_color (pp) = pp_show_color (m_mgr.get_dc ().get_reference_printer ()); break; case DIAGNOSTIC_COLORIZE_NO: pp_show_color (pp) = false; break; case DIAGNOSTIC_COLORIZE_YES: pp_show_color (pp) = true; break; } } void diagnostic_text_sink::text_starter (diagnostics::text_sink &text_output, const diagnostics::diagnostic_info *info) { gcc_assert (info->m_x_data); const diagnostic &diag = *static_cast (info->m_x_data); pretty_printer *pp = text_output.get_printer (); const diagnostic_logical_location *diag_logical_loc = diag.get_logical_location (); diagnostic_manager &mgr = diag.get_manager (); if (diag_logical_loc && diag_logical_loc != mgr.get_prev_diag_logical_loc ()) { pp_set_prefix (pp, nullptr); /* This macro is used to ensure that all format strings are visible to gettext and checked at compile time. */ #define CASE(KIND, MSGID) \ case KIND: \ if (const char *name \ = diag_logical_loc->m_fully_qualified_name.get_str ()) \ { \ pp_printf (pp, (MSGID), name); \ pp_character (pp, ':'); \ pp_newline (pp); \ } \ break; switch (diag_logical_loc->m_kind) { default: break; /* Kinds within executable code. */ CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, _("In function %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER, _("In member %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE, _("In module %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE, _("In namespace %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE, _("In type %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE, _("In return type %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER, _("In parameter %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE, _("In variable %qs")) /* Kinds within XML or HTML documents. */ CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT, _("In element %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE, _("In attribute %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT, _("In text %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT, _("In comment %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION, _("In processing instruction %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD, _("In DTD %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION, _("In declaration %qs")) /* Kinds within JSON documents. */ CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT, _("In JSON object %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, _("In JSON array %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY, _("In JSON property %qs")) CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, _("In JSON value %qs")) } #undef CASE } pp_set_prefix (pp, text_output.build_prefix (*info)); } /* class sarif_sink : public sink. */ sarif_sink:: sarif_sink (diagnostic_manager &mgr, FILE *dst_stream, const diagnostic_file *main_input_file, const diagnostics::sarif_generation_options &sarif_gen_opts) : sink (mgr) { diagnostics::output_file output_file (dst_stream, false, label_text::borrow ("sarif_sink")); auto serialization = std::make_unique (true); auto inner_sink = make_sarif_sink (mgr.get_dc (), *mgr.get_line_table (), std::move (serialization), sarif_gen_opts, std::move (output_file)); inner_sink->set_main_input_filename (main_input_file->get_name ()); mgr.get_dc ().add_sink (std::move (inner_sink)); } // struct diagnostic_message_buffer std::string diagnostic_message_buffer::to_string () const { std::string result; /* Convert to text, dropping colorization, URLs, etc. */ for (auto iter = m_tokens.m_first; iter; iter = iter->m_next) switch (iter->m_kind) { default: gcc_unreachable (); case pp_token::kind::text: { pp_token_text *sub = as_a (iter); result += sub->m_value.get (); } break; case pp_token::kind::begin_color: case pp_token::kind::end_color: // Skip break; case pp_token::kind::begin_quote: result += open_quote; break; case pp_token::kind::end_quote: result += close_quote; break; case pp_token::kind::begin_url: case pp_token::kind::end_url: // Skip break; case pp_token::kind::event_id: { pp_token_event_id *sub = as_a (iter); gcc_assert (sub->m_event_id.known_p ()); result += '('; result += std::to_string (sub->m_event_id.one_based ()); result += ')'; } break; case pp_token::kind::custom_data: /* We don't have a way of handling custom_data tokens here. */ gcc_unreachable (); break; } return result; } /* struct diagnostic_manager. */ void diagnostic_manager::write_patch (FILE *dst_stream) { pretty_printer pp; pp.set_output_stream (dst_stream); m_change_set->print_diff (&pp, true); pp_flush (&pp); } void diagnostic_manager::emit_va (diagnostic &diag, const char *msgid, va_list *args) { set_line_table_global (); m_current_diag = &diag; { m_dc.begin_group (); diagnostics::diagnostic_info info; GCC_DIAGNOSTIC_PUSH_IGNORED(-Wsuggest-attribute=format) diagnostic_set_info (&info, msgid, args, diag.get_rich_location (), diagnostics_kind_from_diagnostic_level (diag.get_level ())); GCC_DIAGNOSTIC_POP info.m_metadata = diag.get_metadata (); info.m_x_data = &diag; diagnostic_report_diagnostic (&m_dc, &info); m_dc.end_group (); } rich_location *rich_loc = diag.get_rich_location (); if (rich_loc->fixits_can_be_auto_applied_p ()) m_change_set->add_fixits (rich_loc); m_prev_diag_logical_loc = diag.get_logical_location (); m_current_diag = nullptr; } void diagnostic_manager::emit (diagnostic &diag, const char *msgid, ...) { va_list args; va_start (args, msgid); emit_va (diag, msgid, &args); va_end (args); } void diagnostic_manager::emit_msg_buf (diagnostic &diag, diagnostic_message_buffer &msg_buf) { pp_element_message_buffer e_msg_buf (msg_buf); emit (diag, "%e", &e_msg_buf); } diagnostic_execution_path * diagnostic_manager::new_execution_path () { auto mgr = m_dc.get_logical_location_manager (); gcc_assert (mgr); return new diagnostic_execution_path (*mgr); } void diagnostic_manager::take_global_graph (std::unique_ptr graph) { class prebuilt_lazy_digraph : public lazily_created { public: prebuilt_lazy_digraph (std::unique_ptr graph) : m_graph (std::move (graph)) { } std::unique_ptr create_object () const final override { return std::move (m_graph); } private: mutable std::unique_ptr m_graph; }; m_dc.report_global_digraph (prebuilt_lazy_digraph (std::move (graph))); } /* Error-checking at the API boundary. */ #define FAIL_IF_NULL(PTR_ARG) \ do { \ volatile const void *ptr_arg = (PTR_ARG); \ if (!ptr_arg) { \ fprintf (stderr, "%s: %s must be non-NULL\n", \ __func__, #PTR_ARG); \ abort (); \ } \ } while (0) /* Public entrypoints. */ /* Public entrypoint for clients to acquire a diagnostic_manager. */ diagnostic_manager * diagnostic_manager_new (void) { return new diagnostic_manager (); } /* Public entrypoint for clients to release a diagnostic_manager. */ void diagnostic_manager_release (diagnostic_manager *diag_mgr) { delete diag_mgr; } /* Public entrypoint. */ void diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, const char *value) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (value); diag_mgr->get_client_version_info ()->m_name.set (value); } /* Public entrypoint. */ void diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, const char *value) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (value); diag_mgr->get_client_version_info ()->m_full_name.set (value); } /* Public entrypoint. */ void diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, const char *value) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (value); diag_mgr->get_client_version_info ()->m_version.set (value); } /* Public entrypoint. */ void diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, const char *value) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (value); diag_mgr->get_client_version_info ()->m_version_url.set (value); } /* Public entrypoint. */ diagnostic_text_sink * diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, FILE *dst_stream, enum diagnostic_colorize colorize) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (dst_stream); diagnostic_text_sink *result = new diagnostic_text_sink (*diag_mgr, dst_stream, colorize); diag_mgr->add_sink (std::unique_ptr (result)); return result; } /* Public entrypoint. */ void diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, int value) { FAIL_IF_NULL (text_sink); text_sink->get_source_printing_options ().enabled = value; } /* Public entrypoint. */ void diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink, enum diagnostic_colorize colorize) { FAIL_IF_NULL (text_sink); text_sink->set_colorize (colorize); } /* Public entrypoint. */ void diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, int value) { FAIL_IF_NULL (text_sink); text_sink->get_source_printing_options ().colorize_source_p = value; } /* Public entrypoint. */ void diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, FILE *dst_stream, const diagnostic_file *main_input_file, enum diagnostic_sarif_version version) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (dst_stream); FAIL_IF_NULL (main_input_file); diagnostics::sarif_generation_options sarif_gen_opts; switch (version) { default: fprintf (stderr, "%s: unrecognized value for version: %i\n", __func__, (int)version); abort (); case DIAGNOSTIC_SARIF_VERSION_2_1_0: sarif_gen_opts.m_version = diagnostics::sarif_version::v2_1_0; break; case DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE: sarif_gen_opts.m_version = diagnostics::sarif_version::v2_2_prerelease_2024_08_08; break; } diag_mgr->add_sink (std::make_unique (*diag_mgr, dst_stream, main_input_file, sarif_gen_opts)); } /* Public entrypoint. */ void diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, FILE *dst_stream) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (dst_stream); diag_mgr->write_patch (dst_stream); } /* Public entrypoint. */ diagnostic_file * diagnostic_manager_new_file (diagnostic_manager *diag_mgr, const char *name, const char *sarif_source_language) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (name); return diag_mgr->new_file (name, sarif_source_language); } /* Public entrypoint. */ void diagnostic_file_set_buffered_content (diagnostic_file *file, const char *buf, size_t sz) { FAIL_IF_NULL (file); FAIL_IF_NULL (buf); file->set_buffered_content (buf, sz); } void diagnostic_manager_debug_dump_file (diagnostic_manager *, const diagnostic_file *file, FILE *out) { FAIL_IF_NULL (out); if (file) { fprintf (out, "file(name=\"%s\"", file->get_name ()); if (file->get_sarif_source_language ()) fprintf (out, ", sarif_source_language=\"%s\"", file->get_sarif_source_language ()); if (const content_buffer *buf = file->get_content ()) fprintf (out, ", content=(size=%zi)", buf->m_sz); fprintf (out, ")"); } else fprintf (out, "(null)"); } /* Public entrypoint. */ const diagnostic_physical_location * diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, const diagnostic_file *file, diagnostic_line_num_t linenum) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (file); return diag_mgr->new_location_from_file_and_line (file, linenum); } /* Public entrypoint. */ const diagnostic_physical_location * diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr, const diagnostic_file *file, diagnostic_line_num_t line_num, diagnostic_column_num_t column_num) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (file); return diag_mgr->new_location_from_file_line_column (file, line_num, column_num); } /* Public entrypoint. */ const diagnostic_physical_location * diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr, const diagnostic_physical_location *loc_caret, const diagnostic_physical_location *loc_start, const diagnostic_physical_location *loc_end) { FAIL_IF_NULL (diag_mgr); return diag_mgr->new_location_from_range (loc_caret, loc_start, loc_end); } /* Public entrypoint. */ void diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr, const diagnostic_physical_location *loc, FILE *out) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (out); if (loc) { const location_t cpplib_loc = as_location_t (loc); diag_mgr->set_line_table_global (); const expanded_location exp_loc (expand_location (cpplib_loc)); diagnostics::context dc; diagnostic_initialize (&dc, 0); dc.m_show_column = true; diagnostics::text_sink text_output (dc); label_text loc_text = text_output.get_location_text (exp_loc); fprintf (out, "%s", loc_text.get ()); diagnostic_finish (&dc); } else fprintf (out, "(null)"); } /* Public entrypoint. */ const diagnostic_logical_location * diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr, enum diagnostic_logical_location_kind_t kind, const diagnostic_logical_location *parent, const char *short_name, const char *fully_qualified_name, const char *decorated_name) { FAIL_IF_NULL (diag_mgr); return diag_mgr->new_logical_location (kind, parent, short_name, fully_qualified_name, decorated_name); } void diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, const diagnostic_logical_location *loc, FILE *out) { FAIL_IF_NULL (diag_mgr); FAIL_IF_NULL (out); if (loc) { fprintf (out, "logical_location(kind="); switch (loc->m_kind) { default: gcc_unreachable (); case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: fprintf (out, "function"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: fprintf (out, "member"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: fprintf (out, "module"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: fprintf (out, "namespace"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: fprintf (out, "file"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: fprintf (out, "return_type"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: fprintf (out, "parameter"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: fprintf (out, "variable"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT: fprintf (out, "element"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE: fprintf (out, "attribute"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT: fprintf (out, "text"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT: fprintf (out, "comment"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: fprintf (out, "processing_instruction"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD: fprintf (out, "dtd"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION: fprintf (out, "declaration"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT: fprintf (out, "object"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY: fprintf (out, "array"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY: fprintf (out, "property"); break; case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE: fprintf (out, "value"); break; } if (auto parent = loc->m_parent) { fprintf (out, ", parent="); diagnostic_manager_debug_dump_logical_location (diag_mgr, parent, out); } if (const char *val = loc->m_short_name.get_str ()) fprintf (out, ", short_name=\"%s\"", val); if (const char *val = loc->m_fully_qualified_name.get_str ()) fprintf (out, ", fully_qualified_name=\"%s\"", val); if (const char *val = loc->m_decorated_name.get_str ()) fprintf (out, ", decorated_name=\"%s\"", val); fprintf (out, ")"); } else fprintf (out, "(null)"); } /* Public entrypoint. */ void diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) { FAIL_IF_NULL (diag_mgr); diag_mgr->begin_group (); } /* Public entrypoint. */ extern void diagnostic_manager_end_group (diagnostic_manager *diag_mgr) { FAIL_IF_NULL (diag_mgr); diag_mgr->end_group (); } /* Public entrypoint. */ diagnostic * diagnostic_begin (diagnostic_manager *diag_mgr, enum diagnostic_level level) { FAIL_IF_NULL (diag_mgr); return new diagnostic (*diag_mgr, level); } /* Public entrypoint. */ void diagnostic_set_cwe (diagnostic *diag, unsigned cwe_id) { FAIL_IF_NULL (diag); diag->set_cwe (cwe_id); } /* Public entrypoint. */ void diagnostic_add_rule (diagnostic *diag, const char *title, const char *url) { FAIL_IF_NULL (diag); diag->add_rule (title, url); } /* Public entrypoint. */ void diagnostic_set_location (diagnostic *diag, const diagnostic_physical_location *loc) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); diag->set_location (loc); } /* Public entrypoint. */ void diagnostic_add_location (diagnostic *diag, const diagnostic_physical_location *loc) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); diag->add_location (loc); } /* Public entrypoint. */ void diagnostic_add_location_with_label (diagnostic *diag, const diagnostic_physical_location *loc, const char *text) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); FAIL_IF_NULL (text); diag->add_location_with_label (loc, text); } /* Public entrypoint. */ void diagnostic_set_logical_location (diagnostic *diag, const diagnostic_logical_location *logical_loc) { FAIL_IF_NULL (diag); diag->set_logical_location (logical_loc); } /* Public entrypoint. */ void diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, const diagnostic_physical_location *loc, const char *addition) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); FAIL_IF_NULL (addition); diag->get_manager ().set_line_table_global (); diag->get_rich_location ()->add_fixit_insert_before (as_location_t (loc), addition); } /* Public entrypoint. */ void diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, const diagnostic_physical_location *loc, const char *addition) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); FAIL_IF_NULL (addition); diag->get_manager ().set_line_table_global (); diag->get_rich_location ()->add_fixit_insert_after (as_location_t (loc), addition); } /* Public entrypoint. */ void diagnostic_add_fix_it_hint_replace (diagnostic *diag, const diagnostic_physical_location *loc, const char *replacement) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); FAIL_IF_NULL (replacement); diag->get_manager ().set_line_table_global (); diag->get_rich_location ()->add_fixit_replace (as_location_t (loc), replacement); } /* Public entrypoint. */ void diagnostic_add_fix_it_hint_delete (diagnostic *diag, const diagnostic_physical_location *loc) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); diag->get_manager ().set_line_table_global (); diag->get_rich_location ()->add_fixit_remove (as_location_t (loc)); } /* Public entrypoint. */ diagnostic_execution_path * diagnostic_add_execution_path (diagnostic *diag) { FAIL_IF_NULL (diag); return diag->add_execution_path (); } /* Public entrypoint. */ diagnostic_execution_path * diagnostic_manager_new_execution_path (diagnostic_manager *manager) { FAIL_IF_NULL (manager); return manager->new_execution_path (); } /* Public entrypoint. */ extern void diagnostic_take_execution_path (diagnostic *diag, diagnostic_execution_path *path) { FAIL_IF_NULL (diag); FAIL_IF_NULL (path); return diag->take_execution_path (path); } /* Public entrypoint. */ void diagnostic_execution_path_release (diagnostic_execution_path *path) { delete path; } /* Public entrypoint. */ diagnostic_event_id diagnostic_execution_path_add_event (diagnostic_execution_path *path, const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, const char *gmsgid, ...) { FAIL_IF_NULL (path); FAIL_IF_NULL (gmsgid); va_list args; va_start (args, gmsgid); diagnostics::paths::event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, nullptr, gmsgid, &args); va_end (args); return as_diagnostic_event_id (result); } /* Public entrypoint. */ diagnostic_event_id diagnostic_execution_path_add_event_va (diagnostic_execution_path *path, const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, const char *gmsgid, va_list *args) { FAIL_IF_NULL (path); FAIL_IF_NULL (gmsgid); diagnostics::paths::event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, nullptr, gmsgid, args); return as_diagnostic_event_id (result); } /* Public entrypoint. */ void diagnostic_finish (diagnostic *diag, const char *gmsgid, ...) { FAIL_IF_NULL (diag); va_list args; va_start (args, gmsgid); diagnostic_finish_va (diag, gmsgid, &args); va_end (args); } /* Public entrypoint. */ void diagnostic_finish_va (diagnostic *diag, const char *gmsgid, va_list *args) { FAIL_IF_NULL (diag); if (const char *tool_name = diag->get_manager ().get_client_version_info ()->m_name.get_str ()) progname = tool_name; else progname = "progname"; auto_diagnostic_group d; diag->get_manager ().emit_va (*diag, gmsgid, args); delete diag; } /* Public entrypoint. */ diagnostic_file * diagnostic_physical_location_get_file (const diagnostic_physical_location *physical_loc) { if (!physical_loc) return nullptr; return physical_loc->get_file (); } /* Public entrypoints for accessing logical location data. */ enum diagnostic_logical_location_kind_t diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) { FAIL_IF_NULL (loc); return loc->m_kind; } const diagnostic_logical_location * diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) { FAIL_IF_NULL (loc); return loc->m_parent; } const char * diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) { FAIL_IF_NULL (loc); return loc->m_short_name.get_str (); } const char * diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) { FAIL_IF_NULL (loc); return loc->m_fully_qualified_name.get_str (); } const char * diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) { FAIL_IF_NULL (loc); return loc->m_decorated_name.get_str (); } namespace { struct spec_context : public diagnostics::output_spec::context { public: spec_context (const char *option_name, diagnostic_manager &affected_mgr, diagnostic_manager &control_mgr) : context (option_name, affected_mgr.get_line_table ()), m_control_mgr (control_mgr) {} void report_error_va (const char *gmsgid, va_list *ap) const final override { diagnostic *diag = diagnostic_begin (&m_control_mgr, DIAGNOSTIC_LEVEL_ERROR); diagnostic_finish_va (diag, gmsgid, ap); } const char * get_base_filename () const final override { return nullptr; } private: diagnostic_manager &m_control_mgr; }; } // anon namespace /* Public entrypoint. */ int diagnostic_manager_add_sink_from_spec (diagnostic_manager *affected_mgr, const char *option_name, const char *spec, diagnostic_manager *control_mgr) { FAIL_IF_NULL (affected_mgr); FAIL_IF_NULL (option_name); FAIL_IF_NULL (spec); FAIL_IF_NULL (control_mgr); spec_context ctxt (option_name, *affected_mgr, *control_mgr); auto inner_sink = ctxt.parse_and_make_sink (spec, affected_mgr->get_dc ()); if (!inner_sink) return -1; affected_mgr->get_dc ().add_sink (std::move (inner_sink)); return 0; } /* Public entrypoint. */ void diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, const diagnostic_file *file) { FAIL_IF_NULL (mgr); FAIL_IF_NULL (file); mgr->get_dc ().set_main_input_filename (file->get_name ()); } /* Public entrypoint. */ diagnostic_node * diagnostic_graph::add_node_with_id (std::string node_id, diagnostic_node *parent_node) { auto node_up = std::make_unique (*this, std::move (node_id)); diagnostic_node *new_node = node_up.get (); if (parent_node) parent_node->add_child (std::move (node_up)); else add_node (std::move (node_up)); return new_node; } /* Public entrypoint. */ diagnostic_edge * diagnostic_graph::add_edge_with_label (const char *edge_id, diagnostic_node &src_node, diagnostic_node &dst_node, const char *label) { auto edge_up = std::make_unique (*this, edge_id, src_node, dst_node); diagnostic_edge *new_edge = edge_up.get (); if (label) new_edge->set_label (label); add_edge (std::move (edge_up)); return new_edge; } /* Public entrypoint. */ diagnostic_graph * diagnostic_manager_new_graph (diagnostic_manager *manager) { FAIL_IF_NULL (manager); return new diagnostic_graph (*manager); } /* Public entrypoint. */ void diagnostic_manager_take_global_graph (diagnostic_manager *manager, diagnostic_graph *graph) { FAIL_IF_NULL (manager); FAIL_IF_NULL (graph); manager->take_global_graph (std::unique_ptr (graph)); } void diagnostic_take_graph (diagnostic *diag, diagnostic_graph *graph) { FAIL_IF_NULL (diag); FAIL_IF_NULL (graph); diag->take_graph (std::unique_ptr (graph)); } /* Public entrypoint. */ void diagnostic_graph_release (diagnostic_graph *graph) { delete graph; } /* Public entrypoint. */ void diagnostic_graph_set_description (diagnostic_graph *graph, const char *desc) { FAIL_IF_NULL (graph); graph->set_description (desc); } /* Public entrypoint. */ diagnostic_node * diagnostic_graph_add_node (diagnostic_graph *graph, const char *node_id, diagnostic_node *parent_node) { FAIL_IF_NULL (graph); FAIL_IF_NULL (node_id); return graph->add_node_with_id (node_id, parent_node); } /* Public entrypoint. */ diagnostic_edge * diagnostic_graph_add_edge (diagnostic_graph *graph, const char *edge_id, diagnostic_node *src_node, diagnostic_node *dst_node, const char *label) { FAIL_IF_NULL (graph); FAIL_IF_NULL (src_node); FAIL_IF_NULL (dst_node); return graph->add_edge_with_label (edge_id, *src_node, *dst_node, label); } /* Public entrypoint. */ diagnostic_node * diagnostic_graph_get_node_by_id (diagnostic_graph *graph, const char *node_id) { FAIL_IF_NULL (graph); FAIL_IF_NULL (node_id); return static_cast (graph->get_node_by_id (node_id)); } /* Public entrypoint. */ diagnostic_edge * diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, const char *edge_id) { FAIL_IF_NULL (graph); FAIL_IF_NULL (edge_id); return static_cast (graph->get_edge_by_id (edge_id)); } /* Public entrypoint. */ void diagnostic_node_set_location (diagnostic_node *node, const diagnostic_physical_location *loc) { FAIL_IF_NULL (node); node->set_physical_loc (loc ? loc->m_inner : UNKNOWN_LOCATION); } /* Public entrypoint. */ void diagnostic_node_set_label (diagnostic_node *node, const char *label) { FAIL_IF_NULL (node); node->set_label (label); } /* Public entrypoint. */ void diagnostic_node_set_logical_location (diagnostic_node *node, const diagnostic_logical_location *logical_loc) { FAIL_IF_NULL (node); node->set_logical_loc (impl_logical_location_manager::key_from_ptr (logical_loc)); } /* Private entrypoint. */ void private_diagnostic_graph_set_property_bag (diagnostic_graph &graph, std::unique_ptr properties) { graph.set_property_bag (std::move (properties)); } /* Private entrypoint. */ void private_diagnostic_node_set_property_bag (diagnostic_node &node, std::unique_ptr properties) { node.set_property_bag (std::move (properties)); } /* Private entrypoint. */ void private_diagnostic_edge_set_property_bag (diagnostic_edge &edge, std::unique_ptr properties) { edge.set_property_bag (std::move (properties)); } /* Public entrypoint. */ diagnostic_message_buffer * diagnostic_message_buffer_new () { return new diagnostic_message_buffer (); } /* Public entrypoint. */ void diagnostic_message_buffer_release (diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (msg_buf); delete msg_buf; } void diagnostic_message_buffer_append_str (diagnostic_message_buffer *msg_buf, const char *p) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (p); msg_buf->m_tokens.push_back_text (label_text::take (xstrdup (p))); } /* Public entrypoint. */ void diagnostic_message_buffer_append_text (diagnostic_message_buffer *msg_buf, const char *p, size_t len) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (p); msg_buf->m_tokens.push_back_text (label_text::take (xstrndup (p, len))); } /* Public entrypoint. */ void diagnostic_message_buffer_append_byte (diagnostic_message_buffer *msg_buf, char ch) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back_byte (ch); } /* Public entrypoint. */ void diagnostic_message_buffer_append_printf (diagnostic_message_buffer *msg_buf, const char *fmt, ...) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (fmt); va_list args; va_start (args, fmt); char *formatted_buf = xvasprintf (fmt, args); va_end (args); msg_buf->m_tokens.push_back_text (label_text::take (formatted_buf)); } /* Public entrypoint. */ void diagnostic_message_buffer_append_event_id (diagnostic_message_buffer *msg_buf, diagnostic_event_id event_id) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back (event_id); } /* Public entrypoint. */ void diagnostic_message_buffer_begin_url (diagnostic_message_buffer *msg_buf, const char *url) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (url); msg_buf->m_tokens.push_back (label_text::take (xstrdup (url))); } /* Public entrypoint. */ void diagnostic_message_buffer_end_url (diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back (); } /* Public entrypoint. */ void diagnostic_message_buffer_begin_quote (diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back (); } /* Public entrypoint. */ void diagnostic_message_buffer_end_quote (diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back (); } /* Public entrypoint. */ void diagnostic_message_buffer_begin_color (diagnostic_message_buffer *msg_buf, const char *color) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (color); msg_buf->m_tokens.push_back (label_text::take (xstrdup (color))); } /* Public entrypoint. */ void diagnostic_message_buffer_end_color (diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (msg_buf); msg_buf->m_tokens.push_back (); } /* Public entrypoint. */ void diagnostic_message_buffer_dump (const diagnostic_message_buffer *msg_buf, FILE *outf) { FAIL_IF_NULL (msg_buf); FAIL_IF_NULL (outf); msg_buf->m_tokens.dump (outf); } /* Public entrypoint. */ void diagnostic_finish_via_msg_buf (diagnostic *diag, diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (diag); FAIL_IF_NULL (msg_buf); if (const char *tool_name = diag->get_manager ().get_client_version_info ()->m_name.get_str ()) progname = tool_name; else progname = "progname"; auto_diagnostic_group d; diag->get_manager ().emit_msg_buf (*diag, *msg_buf); delete diag; delete msg_buf; } /* Public entrypoint. */ void diagnostic_add_location_with_label_via_msg_buf (diagnostic *diag, const diagnostic_physical_location *loc, diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (diag); diag->get_manager ().assert_valid_diagnostic_physical_location (loc); FAIL_IF_NULL (msg_buf); std::unique_ptr msg_buf_up (msg_buf); diag->add_location_with_label (loc, std::move (msg_buf_up)); } /* Public entrypoint. */ diagnostic_event_id diagnostic_execution_path_add_event_via_msg_buf (diagnostic_execution_path *path, const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (path); FAIL_IF_NULL (msg_buf); std::unique_ptr msg_buf_up (msg_buf); diagnostic_event_id_t result = path->add_event_via_msg_buf (physical_loc, logical_loc, stack_depth, nullptr, std::move (msg_buf_up)); return as_diagnostic_event_id (result); } /* Public entrypoint. */ void diagnostic_graph_set_description_via_msg_buf (diagnostic_graph *graph, diagnostic_message_buffer *desc) { FAIL_IF_NULL (graph); if (desc) graph->set_description (desc->to_string ()); else graph->set_description (nullptr); } /* Public entrypoint. */ diagnostic_edge * diagnostic_graph_add_edge_via_msg_buf (diagnostic_graph *graph, const char *edge_id, diagnostic_node *src_node, diagnostic_node *dst_node, diagnostic_message_buffer *label) { FAIL_IF_NULL (graph); FAIL_IF_NULL (src_node); FAIL_IF_NULL (dst_node); if (label) { std::string label_str (label->to_string ()); return graph->add_edge_with_label (edge_id, *src_node, *dst_node, label_str.c_str ()); } else return graph->add_edge_with_label (edge_id, *src_node, *dst_node, nullptr); } /* Public entrypoint. */ void diagnostic_node_set_label_via_msg_buf (diagnostic_node *node, diagnostic_message_buffer *label) { FAIL_IF_NULL (node); if (label) node->set_label (label->to_string ()); else node->set_label (nullptr); } /* Private entrypoint. */ diagnostic_event_id private_diagnostic_execution_path_add_event_3 (diagnostic_execution_path *path, const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, diagnostic_graph *state_graph, diagnostic_message_buffer *msg_buf) { FAIL_IF_NULL (path); FAIL_IF_NULL (msg_buf); diagnostic_event_id_t result = path->add_event_via_msg_buf (physical_loc, logical_loc, stack_depth, std::unique_ptr (state_graph), std::unique_ptr (msg_buf)); return as_diagnostic_event_id (result); }