Changed in v3: * Moved the testsuite to a separate patch * Updated copyright year * class text_sink: New. * class file: Add default ctor, copy ctor, move ctor; make m_inner non-const * class physical_location: Add default ctor * class logical_location: Make m_inner non-const * class execution_path: New * class diagnostic: Add member functions: add_rule, take_execution_path, finish_va * class manager: Add alternate ctor; add m_owned bool and use in dtor; delete copy ctor; add move ctor; add member functions set_tool_name, set_full-name, set_version_string, set_version_url, new_execution_path. Add param "main_input_file" to add_sarif_sink.
Blurb from v2: This is new in v2: a C++ wrapper API that provides some syntactic sugar for calling into libdiagnostics.{h,so}. I've been "eating my own dogfood" with this by using it to write a simple client that reads a SARIF file and dumps it using the text sink: https://github.com/davidmalcolm/libdiagnostics-sarif-dump gcc/ChangeLog: * libdiagnostics++.h: New file. --- gcc/libdiagnostics++.h | 595 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 gcc/libdiagnostics++.h diff --git a/gcc/libdiagnostics++.h b/gcc/libdiagnostics++.h new file mode 100644 index 000000000000..14c84934a446 --- /dev/null +++ b/gcc/libdiagnostics++.h @@ -0,0 +1,595 @@ +/* A C++ wrapper API around libdiagnostics.h for emitting diagnostics. + Copyright (C) 2023-2024 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 LIBDIAGNOSTICSPP_H +#define LIBDIAGNOSTICSPP_H + +#include "libdiagnostics.h" + +namespace libdiagnostics { + +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; + +/* 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 const diagnostic_file *. */ + +class file +{ +public: + file () : m_inner (nullptr) {} + file (const 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; } + + const 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) + {} + + 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) + {} + + const diagnostic_logical_location *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<diagnostic_execution_path *> (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, ...) + LIBDIAGNOSTICS_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) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 0); + + 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_with_label (physical_location loc, + const char *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 + finish (const char *fmt, ...) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3); + + void + finish_va (const char *fmt, va_list *args) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0); + + ::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); + } + + 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) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_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); + + + diagnostic_manager *m_inner; + bool m_owned; +}; + +// Implementation + +// 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); +} + +// 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::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::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); +} + +// 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)); +} + +} // namespace libdiagnostics + +#endif // #ifndef LIBDIAGNOSTICSPP_H -- 2.26.3