For followup work I need to be able to get at data from a diagnostic_logical_location after creating it, hence the need to extend libgdiagnostics with accessor entrypoints.
This is the first extension to libgdiagnostics since the initial release. The patch uses symbol versioning to add the new entrypoints in the same way that libgccjit does. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Successful run of analyzer integration tests on x86_64-pc-linux-gnu. Pushed to trunk as r16-412-gbf6d85490a2a95. gcc/ChangeLog: * doc/libgdiagnostics/topics/compatibility.rst: New file, based on gcc/jit/docs/topics/compatibility.rst. * doc/libgdiagnostics/topics/index.rst: Add compatibility.rst. * doc/libgdiagnostics/topics/logical-locations.rst (Accessors): New section. * libgdiagnostics++.h (logical_location::operator bool): New. (logical_location::operator==): New. (logical_location::operator!=): New. (logical_location::get_kind): New. (logical_location::get_parent): New. (logical_location::get_short_name): New. (logical_location::get_fully_qualified_name): New. (logical_location::get_decorated_name): New. * libgdiagnostics.cc (diagnostic_logical_location::get_fully_qualified_name): New. (diagnostic_logical_location_get_kind): New entrypoint. (diagnostic_logical_location_get_parent): New entrypoint. (diagnostic_logical_location_get_short_name): New entrypoint. (diagnostic_logical_location_get_fully_qualified_name): New entrypoint. (diagnostic_logical_location_get_decorated_name): New entrypoint. * libgdiagnostics.h (LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS): New define. (diagnostic_logical_location_get_kind): New entrypoint. (diagnostic_logical_location_get_parent): New entrypoint. (diagnostic_logical_location_get_short_name): New entrypoint. (diagnostic_logical_location_get_fully_qualified_name): New entrypoint. (diagnostic_logical_location_get_decorated_name): New entrypoint. * libgdiagnostics.map (LIBGDIAGNOSTICS_ABI_1): New. gcc/testsuite/ChangeLog: * libgdiagnostics.dg/test-logical-location.c: Include <string.h>. (main): Verify that the accessors work. * libgdiagnostics.dg/test-logical-location.cc: New test. --- .../libgdiagnostics/topics/compatibility.rst | 179 ++++++++++++++++++ gcc/doc/libgdiagnostics/topics/index.rst | 1 + .../topics/logical-locations.rst | 25 +++ gcc/libgdiagnostics++.h | 64 +++++++ gcc/libgdiagnostics.cc | 47 +++++ gcc/libgdiagnostics.h | 26 +++ gcc/libgdiagnostics.map | 10 + .../test-logical-location.c | 12 ++ .../test-logical-location.cc | 91 +++++++++ 9 files changed, 455 insertions(+) create mode 100644 gcc/doc/libgdiagnostics/topics/compatibility.rst create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst new file mode 100644 index 000000000000..4df685001e67 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/compatibility.rst @@ -0,0 +1,179 @@ +.. Copyright (C) 2015-2025 Free Software Foundation, Inc. + Originally contributed by David Malcolm <dmalc...@redhat.com> + + This 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see + <https://www.gnu.org/licenses/>. + +.. default-domain:: c + +ABI and API compatibility +========================= + +The libgdiagnostics developers strive for ABI and API backward-compatibility: +programs built against libgdiagnostics.so stand a good chance of running +without recompilation against newer versions of libgdiagnostics.so, and +ought to recompile without modification against newer versions of +libgdiagnostics.h. + +.. note:: The libgdiagnostics++.h C++ API is more experimental, and less + locked-down at this time. + +API compatibility is achieved by extending the API rather than changing +it. For ABI compatiblity, we avoid bumping the SONAME, and instead use +symbol versioning to tag each symbol, so that a binary linked against +libgdiagnostics.so is tagged according to the symbols that it uses. + +For example, :func:`diagnostic_logical_location_get_kind` was added in +``LIBGDIAGNOSTICS_ABI_1``. If a client program uses it, this can be detected +from metadata by using ``objdump``: + +.. code-block:: bash + + $ objdump -p testsuite/libgdiagnostics/test-logical-location.c.exe | tail -n 7 + + Version References: + required from libc.so.6: + 0x09691a75 0x00 04 GLIBC_2.2.5 + required from libgdiagnostics.so.0: + 0x0ec567d1 0x00 03 LIBGDIAGNOSTICS_ABI_1 + 0x0ec567d0 0x00 02 LIBGDIAGNOSTICS_ABI_0 + +You can see the symbol tags provided by libgdiagnostics.so using ``objdump``: + +.. code-block:: bash + + $ objdump -p libgdiagnostics.so | less + [...snip...] + Version definitions: + 1 0x01 0x099ea4b0 libgdiagnostics.so.0 + 2 0x00 0x0ec567d0 LIBGDIAGNOSTICS_ABI_0 + 3 0x00 0x0ec567d1 LIBGDIAGNOSTICS_ABI_1 + LIBGDIAGNOSTICS_ABI_0 + [...snip...] + +ABI symbol tags +*************** + +.. _LIBGDIAGNOSTICS_ABI_0: + +``LIBGDIAGNOSTICS_ABI_0`` +------------------------- + +All entrypoints in the initial release of libgdiagnostics (in GCC 15) are +tagged with ``LIBGDIAGNOSTICS_ABI_0``; these entrypoints are: + + * :func:`diagnostic_manager_new` + + * :func:`diagnostic_manager_release` + + * :func:`diagnostic_manager_set_tool_name` + + * :func:`diagnostic_manager_set_full_name` + + * :func:`diagnostic_manager_set_version_string` + + * :func:`diagnostic_manager_set_version_url` + + * :func:`diagnostic_manager_add_text_sink` + + * :func:`diagnostic_text_sink_set_source_printing_enabled` + + * :func:`diagnostic_text_sink_set_colorize` + + * :func:`diagnostic_text_sink_set_labelled_source_colorization_enabled` + + * :func:`diagnostic_manager_add_sarif_sink` + + * :func:`diagnostic_manager_write_patch` + + * :func:`diagnostic_manager_new_file` + + * :func:`diagnostic_file_set_buffered_content` + + * :func:`diagnostic_manager_debug_dump_file` + + * :func:`diagnostic_manager_new_location_from_file_and_line` + + * :func:`diagnostic_manager_new_location_from_file_line_column` + + * :func:`diagnostic_manager_new_location_from_range` + + * :func:`diagnostic_manager_debug_dump_location` + + * :func:`diagnostic_manager_new_logical_location` + + * :func:`diagnostic_manager_debug_dump_logical_location` + + * :func:`diagnostic_manager_begin_group` + + * :func:`diagnostic_manager_end_group` + + * :func:`diagnostic_begin` + + * :func:`diagnostic_set_cwe` + + * :func:`diagnostic_add_rule` + + * :func:`diagnostic_set_location` + + * :func:`diagnostic_set_location_with_label` + + * :func:`diagnostic_add_location` + + * :func:`diagnostic_add_location_with_label` + + * :func:`diagnostic_set_logical_location` + + * :func:`diagnostic_add_fix_it_hint_insert_before` + + * :func:`diagnostic_add_fix_it_hint_insert_after` + + * :func:`diagnostic_add_fix_it_hint_replace` + + * :func:`diagnostic_add_fix_it_hint_delete` + + * :func:`diagnostic_add_execution_path` + + * :func:`diagnostic_manager_new_execution_path` + + * :func:`diagnostic_take_execution_path` + + * :func:`diagnostic_execution_path_release` + + * :func:`diagnostic_execution_path_add_event` + + * :func:`diagnostic_execution_path_add_event_va` + + * :func:`diagnostic_finish` + + * :func:`diagnostic_finish_va` + + * :func:`diagnostic_physical_location_get_file` + +.. _LIBGDIAGNOSTICS_ABI_1: + +``LIBGDIAGNOSTICS_ABI_1`` +------------------------- +``LIBGDIAGNOSTICS_ABI_1`` covers the addition of these functions for +acccessing values within a :type:`diagnostic_logical_location`: + + * :func:`diagnostic_logical_location_get_kind` + + * :func:`diagnostic_logical_location_get_parent` + + * :func:`diagnostic_logical_location_get_short_name` + + * :func:`diagnostic_logical_location_get_fully_qualified_name` + + * :func:`diagnostic_logical_location_get_decorated_name` diff --git a/gcc/doc/libgdiagnostics/topics/index.rst b/gcc/doc/libgdiagnostics/topics/index.rst index 6e14c0f52eca..6eb3ed646b09 100644 --- a/gcc/doc/libgdiagnostics/topics/index.rst +++ b/gcc/doc/libgdiagnostics/topics/index.rst @@ -36,3 +36,4 @@ Topic reference text-output.rst sarif.rst ux.rst + compatibility.rst diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst b/gcc/doc/libgdiagnostics/topics/logical-locations.rst index 3fbee8379f9e..85f239d6bb1e 100644 --- a/gcc/doc/libgdiagnostics/topics/logical-locations.rst +++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst @@ -113,3 +113,28 @@ Associating diagnostics with locations Set the logical location of ``diag``. ``diag`` must be non-NULL; ``logical_loc`` can be NULL. + +Accessors +********* + +The following functions can be used to access the data that was passed to a +:type:`diagnostic_logical_location` when it was created. In each case, the +``loc`` parameter must be non-NULL. :type:`const char *` values will point +at copies of the original buffer. + +.. function:: enum diagnostic_logical_location_kind_t diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) + +.. function:: const diagnostic_logical_location *diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) + +The above accessors were added in :ref:`LIBGDIAGNOSTICS_ABI_1`; you can +test for their presence using + + .. code-block:: c + + #ifdef LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h index 39477a0bc4ac..18a88a2472ce 100644 --- a/gcc/libgdiagnostics++.h +++ b/gcc/libgdiagnostics++.h @@ -109,6 +109,25 @@ public: : 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; }; @@ -385,6 +404,51 @@ 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 diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index c2eb9757d181..8a4a159ecb74 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -221,6 +221,11 @@ struct diagnostic_logical_location : public logical_location const diagnostic_logical_location *get_parent () const { return m_parent; } + const char *get_fully_qualified_name () const + { + return m_fully_qualified_name.get_str (); + } + label_text get_name_for_path_output () const { return label_text::borrow (m_short_name.get_str ()); @@ -1788,3 +1793,45 @@ diagnostic_physical_location_get_file (const diagnostic_physical_location *physi 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->get_external_kind (); +} + +const diagnostic_logical_location * +diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->get_parent (); +} + +const char * +diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->get_short_name (); +} + +const char * +diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->get_fully_qualified_name (); +} + +const char * +diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->get_internal_name (); +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h index 2ce0f4c99c87..14567a523eb7 100644 --- a/gcc/libgdiagnostics.h +++ b/gcc/libgdiagnostics.h @@ -487,6 +487,32 @@ diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_m LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); +/* Accessors for logical locations (added in LIBGDIAGNOSTICS_ABI_1; + you can test for their presence using + #ifdef LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS +*/ +#define LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS + +extern enum diagnostic_logical_location_kind_t +diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const diagnostic_logical_location * +diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + /* Diagnostic groups. */ /* Begin a diagnostic group. All diagnostics emitted within diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map index 5958cfe3f129..4233cf86c76b 100644 --- a/gcc/libgdiagnostics.map +++ b/gcc/libgdiagnostics.map @@ -73,3 +73,13 @@ LIBGDIAGNOSTICS_ABI_0 local: *; }; + +# Add accessors for diagnostic_logical_location. +LIBGDIAGNOSTICS_ABI_1 { + global: + diagnostic_logical_location_get_kind; + diagnostic_logical_location_get_parent; + diagnostic_logical_location_get_short_name; + diagnostic_logical_location_get_fully_qualified_name; + diagnostic_logical_location_get_decorated_name; +} LIBGDIAGNOSTICS_ABI_0; diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c index 140891938970..b59ece4f1d73 100644 --- a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c @@ -20,6 +20,7 @@ PRINT "hello world!"; const int line_num = __LINE__ - 2; #include <assert.h> +#include <string.h> int main () @@ -62,6 +63,17 @@ main () diagnostic_finish (d, "can't find %qs", "foo"); /* end quoted source */ + /* Verify that the accessors work. */ + assert (diagnostic_logical_location_get_kind (logical_loc) + == DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION); + assert (!diagnostic_logical_location_get_parent (logical_loc)); + assert (!strcmp (diagnostic_logical_location_get_short_name (logical_loc), + "test_short_name")); + assert (!strcmp (diagnostic_logical_location_get_fully_qualified_name (logical_loc), + "test_qualified_name")); + assert (!strcmp (diagnostic_logical_location_get_decorated_name (logical_loc), + "test_decorated_name")); + /* Verify that creating a diagnostic_logical_location with equal values yields the same instance. */ const diagnostic_logical_location *dup diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc new file mode 100644 index 000000000000..3080adefeae3 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc @@ -0,0 +1,91 @@ +/* C++ example of using a logical location. + + Intended output is similar to: + +In function 'test_qualified_name': +PATH/test-error-with-note.cc:17:8: error: can't find 'foo' + 17 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + + along with the equivalent in SARIF. */ + +#include "libgdiagnostics++.h" + +/* Placeholder source: +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +#include <assert.h> +#include <string.h> + +int +main () +{ + libgdiagnostics::manager mgr; + + auto file = mgr.new_file (__FILE__, "c"); + + mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY); + + auto loc_start = mgr.new_location_from_file_line_column (file, line_num, 8); + auto loc_end = mgr.new_location_from_file_line_column (file, line_num, 19); + auto loc_range = mgr.new_location_from_range (loc_start, loc_start, loc_end); + + auto err (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR)); + err.set_location (loc_range); + + libgdiagnostics::logical_location logical_loc + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + err.set_logical_location (logical_loc); + + err.finish ("can't find %qs", "foo"); + + /* Verify that the accessors work. */ + assert (logical_loc.get_kind () + == DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION); + assert (logical_loc.get_parent ().m_inner == nullptr); + assert (!strcmp (logical_loc.get_short_name (), + "test_short_name")); + assert (!strcmp (logical_loc.get_fully_qualified_name (), + "test_qualified_name")); + assert (!strcmp (logical_loc.get_decorated_name (), + "test_decorated_name")); + + /* Verify that libgdiagnostic::logical_location instances created with + equal values compare as equal. */ + libgdiagnostics::logical_location dup + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + assert (dup == logical_loc); + + /* Verify that libgdiagnostic::logical_location instances created with + differing values compare as non-equal. */ + libgdiagnostics::logical_location other + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "something_else", + NULL, NULL); + assert (other != logical_loc); + + return 0; +} + +/* Check the output from the text sink. */ +/* { dg-begin-multiline-output "" } +In function 'test_qualified_name': + { dg-end-multiline-output "" } */ +/* { dg-regexp "\[^\n\r\]+test-logical-location.cc:17:8: error: can't find 'foo'" } */ +/* { dg-begin-multiline-output "" } + 17 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ -- 2.26.3