/* A C++ wrapper API around libgdiagnostics.h 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 . */ #ifndef LIBGDIAGNOSTICSPP_H #define LIBGDIAGNOSTICSPP_H #include "libgdiagnostics.h" namespace libgdiagnostics { typedef diagnostic_line_num_t line_num_t; typedef diagnostic_column_num_t column_num_t; class file; class physical_location; class logical_location; class execution_path; class group; class manager; class diagnostic; class graph; class node; class edge; class message_buffer; /* Wrapper around a borrowed diagnostic_text_sink *. */ class text_sink { public: text_sink (diagnostic_text_sink *inner) : m_inner (inner) { } void set_source_printing_enabled (int value) { diagnostic_text_sink_set_source_printing_enabled (m_inner, value); } void set_colorize (enum diagnostic_colorize colorize) { diagnostic_text_sink_set_colorize (m_inner, colorize); } void set_labelled_source_colorization_enabled (int value) { diagnostic_text_sink_set_labelled_source_colorization_enabled (m_inner, value); } diagnostic_text_sink *m_inner; }; /* Wrapper around a diagnostic_file *. */ class file { public: file () : m_inner (nullptr) {} file (diagnostic_file *file) : m_inner (file) {} file (const file &other) : m_inner (other.m_inner) {} file &operator= (const file &other) { m_inner = other.m_inner; return *this; } void set_buffered_content (const char *data, size_t sz); diagnostic_file * m_inner; }; /* Wrapper around a const diagnostic_physical_location *. */ class physical_location { public: physical_location () : m_inner (nullptr) {} physical_location (const diagnostic_physical_location *location) : m_inner (location) {} file get_file () const; const diagnostic_physical_location *m_inner; }; /* Wrapper around a const diagnostic_logical_location *. */ class logical_location { public: logical_location () : m_inner (nullptr) {} logical_location (const diagnostic_logical_location *logical_loc) : m_inner (logical_loc) {} operator bool() { return m_inner != nullptr; } // Various accessors enum diagnostic_logical_location_kind_t get_kind () const; logical_location get_parent () const; const char *get_short_name () const; const char *get_fully_qualified_name () const; const char *get_decorated_name () const; bool operator== (const logical_location &other) const { return m_inner == other.m_inner; } bool operator!= (const logical_location &other) const { return m_inner != other.m_inner; } const diagnostic_logical_location *m_inner; }; /* Wrapper around a diagnostic_message_buffer *, with ownership. */ class message_buffer { public: message_buffer () : m_inner (nullptr) {} message_buffer (diagnostic_message_buffer *inner) : m_inner (inner) {} ~message_buffer () { if (m_inner) diagnostic_message_buffer_release (m_inner); } message_buffer (const message_buffer &) = delete; message_buffer (message_buffer &&other) { m_inner = other.m_inner; other.m_inner = nullptr; } message_buffer& operator= (const message_buffer &) = delete; message_buffer& operator= (message_buffer &&other) { if (m_inner) diagnostic_message_buffer_release (m_inner); m_inner = other.m_inner; other.m_inner = nullptr; return *this; } message_buffer& operator+= (const char *str) { diagnostic_message_buffer_append_str (m_inner, str); return *this; } message_buffer& operator+= (char ch) { diagnostic_message_buffer_append_byte (m_inner, ch); return *this; } message_buffer & begin_url (const char *url) { diagnostic_message_buffer_begin_url (m_inner, url); return *this; } message_buffer & end_url () { diagnostic_message_buffer_end_url (m_inner); return *this; } diagnostic_message_buffer *m_inner; }; /* RAII class around a diagnostic_execution_path *. */ class execution_path { public: execution_path () : m_inner (nullptr), m_owned (false) {} execution_path (diagnostic_execution_path *path) : m_inner (path), m_owned (true) {} execution_path (const diagnostic_execution_path *path) : m_inner (const_cast (path)), m_owned (false) {} execution_path (const execution_path &other) = delete; execution_path &operator= (const execution_path &other) = delete; execution_path (execution_path &&other) : m_inner (other.m_inner), m_owned (other.m_owned) { other.m_inner = nullptr; other.m_owned = false; } execution_path &operator= (execution_path &&other) { m_inner = other.m_inner; m_owned = other.m_owned; other.m_inner = nullptr; other.m_owned = false; return *this; } ~execution_path () { if (m_owned) diagnostic_execution_path_release (m_inner); } diagnostic_event_id add_event (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, const char *fmt, ...) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 6); diagnostic_event_id add_event_va (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, const char *fmt, va_list *args) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 0); diagnostic_event_id add_event_via_msg_buf (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, message_buffer &&msg_buf); diagnostic_execution_path *m_inner; bool m_owned; }; /* RAII class for starting/ending a group within a diagnostic_manager. */ class group { public: group (manager &mgr); ~group (); private: manager &m_mgr; }; /* Wrapper around a diagnostic *. */ class diagnostic { public: diagnostic (::diagnostic *d) : m_inner (d) {} void set_cwe (unsigned cwe_id); void add_rule (const char *title, const char *url); void set_location (physical_location loc); void add_location (physical_location loc); void add_location_with_label (physical_location loc, const char *text); void add_location_with_label (physical_location loc, message_buffer &&text); void set_logical_location (logical_location loc); void add_fix_it_hint_insert_before (physical_location loc, const char *addition); void add_fix_it_hint_insert_after (physical_location loc, const char *addition); void add_fix_it_hint_replace (physical_location loc, const char *replacement); void add_fix_it_hint_delete (physical_location loc); void take_execution_path (execution_path path); void take_graph (graph g); void finish (const char *fmt, ...) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3); void finish_va (const char *fmt, va_list *args) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0); void finish_via_msg_buf (message_buffer &&msg_buf); ::diagnostic * const m_inner; }; /* Wrapper around a diagnostic_manager *, possibly with ownership. */ class manager { public: manager () : m_inner (diagnostic_manager_new ()), m_owned (true) { } manager (diagnostic_manager *inner, bool owned) : m_inner (inner), m_owned (owned) { } ~manager () { if (m_owned) diagnostic_manager_release (m_inner); } manager (const manager &other) = delete; manager (manager &&other) : m_inner (other.m_inner), m_owned (other.m_owned) { other.m_inner = nullptr; } void set_tool_name (const char *value) { diagnostic_manager_set_tool_name (m_inner, value); } void set_full_name (const char *value) { diagnostic_manager_set_full_name (m_inner, value); } void set_version_string (const char *value) { diagnostic_manager_set_version_string (m_inner, value); } void set_version_url (const char *value) { diagnostic_manager_set_version_url (m_inner, value); } text_sink add_text_sink (FILE *dst_stream, enum diagnostic_colorize colorize) { return text_sink (diagnostic_manager_add_text_sink (m_inner, dst_stream, colorize)); } void add_sarif_sink (FILE *dst_stream, file main_input_file, enum diagnostic_sarif_version version) { diagnostic_manager_add_sarif_sink (m_inner, dst_stream, main_input_file.m_inner, version); } bool add_sink_from_spec (const char *option_name, const char *spec, manager control_mgr) { return diagnostic_manager_add_sink_from_spec (m_inner, option_name, spec, control_mgr.m_inner); } void write_patch (FILE *dst_stream) { diagnostic_manager_write_patch (m_inner, dst_stream); } /* Location management. */ file new_file (const char *name, const char *sarif_source_language) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3); void debug_dump (file f, FILE *out); physical_location new_location_from_file_and_line (file f, diagnostic_line_num_t line_num); physical_location new_location_from_file_line_column (file f, line_num_t line_num, column_num_t column_num); physical_location new_location_from_range (physical_location loc_caret, physical_location loc_start, physical_location loc_end); void debug_dump (physical_location loc, FILE *out); logical_location new_logical_location (enum diagnostic_logical_location_kind_t kind, logical_location parent, const char *short_name, const char *fully_qualified_name, const char *decorated_name); void debug_dump (logical_location loc, FILE *out); execution_path new_execution_path (); diagnostic begin_diagnostic (enum diagnostic_level level); void set_analysis_target (file f); void take_global_graph (graph g); void set_debug_physical_locations (bool value); diagnostic_manager *m_inner; bool m_owned; }; class graph { public: graph () : m_inner (nullptr), m_owned (false) {} graph (diagnostic_graph *graph) : m_inner (graph), m_owned (true) {} graph (const diagnostic_graph *graph) : m_inner (const_cast (graph)), m_owned (false) {} graph (const graph &other) = delete; graph &operator= (const graph &other) = delete; graph (graph &&other) : m_inner (other.m_inner), m_owned (other.m_owned) { other.m_inner = nullptr; other.m_owned = false; } graph &operator= (graph &&other) { m_inner = other.m_inner; m_owned = other.m_owned; other.m_inner = nullptr; other.m_owned = false; return *this; } ~graph () { if (m_owned) diagnostic_graph_release (m_inner); } void set_description (const char *); void set_description (message_buffer &&); node get_node_by_id (const char *id) const; edge get_edge_by_id (const char *id) const; edge add_edge (const char *id, node src_node, node dst_node, const char *label); edge add_edge (const char *id, node src_node, node dst_node, message_buffer &&label); diagnostic_graph *m_inner; bool m_owned; }; // Borrowed pointer to a diagnostic_node. class node { public: node () : m_inner (nullptr) {} node (diagnostic_node *node_) : m_inner (node_) {} void set_label (const char *); void set_label (message_buffer &&); void set_location (physical_location loc); void set_logical_location (logical_location loc); diagnostic_node *m_inner; }; // Borrowed edge to a diagnostic_edge. class edge { public: edge (diagnostic_edge *edge_) : m_inner (edge_) {} diagnostic_edge *m_inner; }; // Implementation // class file inline void file::set_buffered_content (const char *data, size_t sz) { diagnostic_file_set_buffered_content (m_inner, data, sz); } // class physical_location inline file physical_location::get_file () const { return file (diagnostic_physical_location_get_file (m_inner)); } // class logical_location inline enum diagnostic_logical_location_kind_t logical_location::get_kind () const { // m_inner must be non-null return diagnostic_logical_location_get_kind (m_inner); } inline logical_location logical_location::get_parent () const { if (m_inner) return diagnostic_logical_location_get_parent (m_inner); else return nullptr; } inline const char * logical_location::get_short_name () const { if (m_inner) return diagnostic_logical_location_get_short_name (m_inner); else return nullptr; } inline const char * logical_location::get_fully_qualified_name () const { if (m_inner) return diagnostic_logical_location_get_fully_qualified_name (m_inner); else return nullptr; } inline const char * logical_location::get_decorated_name () const { if (m_inner) return diagnostic_logical_location_get_decorated_name (m_inner); else return nullptr; } // class execution_path inline diagnostic_event_id execution_path::add_event (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, const char *fmt, ...) { va_list args; va_start (args, fmt); diagnostic_event_id result = add_event_va (physical_loc, logical_loc, stack_depth, fmt, &args); va_end (args); return result; } inline diagnostic_event_id execution_path::add_event_va (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, const char *fmt, va_list *args) { return diagnostic_execution_path_add_event_va (m_inner, physical_loc.m_inner, logical_loc.m_inner, stack_depth, fmt, args); } inline diagnostic_event_id execution_path::add_event_via_msg_buf (physical_location physical_loc, logical_location logical_loc, unsigned stack_depth, message_buffer &&msg_buf) { diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner; msg_buf.m_inner = nullptr; return diagnostic_execution_path_add_event_via_msg_buf (m_inner, physical_loc.m_inner, logical_loc.m_inner, stack_depth, inner_msg_buf); } // class group inline group::group (manager &mgr) : m_mgr (mgr) { diagnostic_manager_begin_group (m_mgr.m_inner); } inline group::~group () { diagnostic_manager_end_group (m_mgr.m_inner); } // class diagnostic inline void diagnostic::set_cwe (unsigned cwe_id) { diagnostic_set_cwe (m_inner, cwe_id); } inline void diagnostic::add_rule (const char *title, const char *url) { diagnostic_add_rule (m_inner, title, url); } inline void diagnostic::set_location (physical_location loc) { diagnostic_set_location (m_inner, loc.m_inner); } inline void diagnostic::add_location_with_label (physical_location loc, const char *text) { diagnostic_add_location_with_label (m_inner, loc.m_inner, text); } inline void diagnostic::add_location_with_label (physical_location loc, message_buffer &&msg_buf) { diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner; msg_buf.m_inner = nullptr; diagnostic_add_location_with_label_via_msg_buf (m_inner, loc.m_inner, inner_msg_buf); } inline void diagnostic::add_location (physical_location loc) { diagnostic_add_location (m_inner, loc.m_inner); } inline void diagnostic::set_logical_location (logical_location loc) { diagnostic_set_logical_location (m_inner, loc.m_inner); } inline void diagnostic::add_fix_it_hint_insert_before (physical_location loc, const char *addition) { diagnostic_add_fix_it_hint_insert_before (m_inner, loc.m_inner, addition); } inline void diagnostic::add_fix_it_hint_insert_after (physical_location loc, const char *addition) { diagnostic_add_fix_it_hint_insert_after (m_inner, loc.m_inner, addition); } inline void diagnostic::add_fix_it_hint_replace (physical_location loc, const char *replacement) { diagnostic_add_fix_it_hint_replace (m_inner, loc.m_inner, replacement); } inline void diagnostic::add_fix_it_hint_delete (physical_location loc) { diagnostic_add_fix_it_hint_delete (m_inner, loc.m_inner); } inline void diagnostic::take_execution_path (execution_path path) { diagnostic_take_execution_path (m_inner, path.m_inner); path.m_owned = false; } inline void diagnostic::take_graph (graph g) { diagnostic_take_graph (m_inner, g.m_inner); g.m_owned = false; } inline void diagnostic::finish (const char *fmt, ...) { va_list ap; va_start (ap, fmt); diagnostic_finish_va (m_inner, fmt, &ap); va_end (ap); } inline void diagnostic::finish_va (const char *fmt, va_list *args) { diagnostic_finish_va (m_inner, fmt, args); } inline void diagnostic::finish_via_msg_buf (message_buffer &&msg_buf) { diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner; msg_buf.m_inner = nullptr; diagnostic_finish_via_msg_buf (m_inner, inner_msg_buf); } // class manager inline file manager::new_file (const char *name, const char *sarif_source_language) { return file (diagnostic_manager_new_file (m_inner, name, sarif_source_language)); } inline physical_location manager::new_location_from_file_and_line (file f, diagnostic_line_num_t line_num) { return physical_location (diagnostic_manager_new_location_from_file_and_line (m_inner, f.m_inner, line_num)); } inline physical_location manager::new_location_from_file_line_column (file f, line_num_t line_num, column_num_t column_num) { return physical_location (diagnostic_manager_new_location_from_file_line_column (m_inner, f.m_inner, line_num, column_num)); } inline physical_location manager::new_location_from_range (physical_location loc_caret, physical_location loc_start, physical_location loc_end) { return physical_location (diagnostic_manager_new_location_from_range (m_inner, loc_caret.m_inner, loc_start.m_inner, loc_end.m_inner)); } inline void manager::debug_dump (physical_location loc, FILE *out) { diagnostic_manager_debug_dump_location (m_inner, loc.m_inner, out); } inline logical_location manager::new_logical_location (enum diagnostic_logical_location_kind_t kind, logical_location parent, const char *short_name, const char *fully_qualified_name, const char *decorated_name) { return logical_location (diagnostic_manager_new_logical_location (m_inner, kind, parent.m_inner, short_name, fully_qualified_name, decorated_name)); } inline void manager::debug_dump (logical_location loc, FILE *out) { diagnostic_manager_debug_dump_logical_location (m_inner, loc.m_inner, out); } inline execution_path manager::new_execution_path () { return execution_path (diagnostic_manager_new_execution_path (m_inner)); } inline diagnostic manager::begin_diagnostic (enum diagnostic_level level) { return diagnostic (diagnostic_begin (m_inner, level)); } inline void manager::set_analysis_target (file f) { diagnostic_manager_set_analysis_target (m_inner, f.m_inner); } inline void manager::take_global_graph (graph g) { diagnostic_manager_take_global_graph (m_inner, g.m_inner); g.m_owned = false; } inline void manager::set_debug_physical_locations (bool value) { diagnostic_manager_set_debug_physical_locations (m_inner, value ? 1 : 0); } // class graph inline void graph::set_description (const char *desc) { diagnostic_graph_set_description (m_inner, desc); } inline void graph::set_description (message_buffer &&msg_buf) { diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner; msg_buf.m_inner = nullptr; diagnostic_graph_set_description_via_msg_buf (m_inner, inner_msg_buf); } inline node graph::get_node_by_id (const char *id) const { return node (diagnostic_graph_get_node_by_id (m_inner, id)); } inline edge graph::get_edge_by_id (const char *id) const { return edge (diagnostic_graph_get_edge_by_id (m_inner, id)); } inline edge graph::add_edge (const char *id, node src_node, node dst_node, const char *label) { return edge (diagnostic_graph_add_edge (m_inner, id, src_node.m_inner, dst_node.m_inner, label)); } inline edge graph::add_edge (const char *id, node src_node, node dst_node, message_buffer &&label) { diagnostic_message_buffer *inner_label = label.m_inner; label.m_inner = nullptr; return edge (diagnostic_graph_add_edge_via_msg_buf (m_inner, id, src_node.m_inner, dst_node.m_inner, inner_label)); } // class node inline void node::set_label (const char *label) { diagnostic_node_set_label (m_inner, label); } inline void node::set_label (message_buffer &&label) { diagnostic_message_buffer *inner_label = label.m_inner; label.m_inner = nullptr; diagnostic_node_set_label_via_msg_buf (m_inner, inner_label); } inline void node::set_location (physical_location loc) { diagnostic_node_set_location (m_inner, loc.m_inner); } inline void node::set_logical_location (logical_location loc) { diagnostic_node_set_logical_location (m_inner, loc.m_inner); } } // namespace libgdiagnostics #endif // #ifndef LIBGDIAGNOSTICSPP_H