]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libgdiagnostics: add accessors for diagnostic_logical_location [LIBGDIAGNOSTICS_ABI_1]
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 6 May 2025 13:26:17 +0000 (09:26 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 6 May 2025 13:26:17 +0000 (09:26 -0400)
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.

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.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
gcc/doc/libgdiagnostics/topics/compatibility.rst [new file with mode: 0644]
gcc/doc/libgdiagnostics/topics/index.rst
gcc/doc/libgdiagnostics/topics/logical-locations.rst
gcc/libgdiagnostics++.h
gcc/libgdiagnostics.cc
gcc/libgdiagnostics.h
gcc/libgdiagnostics.map
gcc/testsuite/libgdiagnostics.dg/test-logical-location.c
gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc [new file with mode: 0644]

diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst
new file mode 100644 (file)
index 0000000..4df6850
--- /dev/null
@@ -0,0 +1,179 @@
+.. Copyright (C) 2015-2025 Free Software Foundation, Inc.
+   Originally contributed by David Malcolm <dmalcolm@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`
index 6e14c0f52ecad9f5620d8d446d4c6689d0671336..6eb3ed646b09be22f3d88fa780d589347d65aea8 100644 (file)
@@ -36,3 +36,4 @@ Topic reference
    text-output.rst
    sarif.rst
    ux.rst
+   compatibility.rst
index 3fbee8379f9e5b636835f7fe3cfa3441065e479f..85f239d6bb1e62dec6e8e3b930fd5186db76caa4 100644 (file)
@@ -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
index 39477a0bc4ac5d4a5b6a337447a71606646c822d..18a88a2472ce310fbb8a8bfb0e454af61aae6759 100644 (file)
@@ -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
index c2eb9757d181ca0e6ef63df85dc70e93d6232669..8a4a159ecb7458a7b68925d325fd3aea82766e59 100644 (file)
@@ -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 ();
+}
index 2ce0f4c99c87123b9ad055273cfdd38d942c25de..14567a523eb728a63dbe094d9d257365b9e168c4 100644 (file)
@@ -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
index 5958cfe3f129083d75acb21dd929eb8aa6bf5839..4233cf86c76bba62a0ed30d1503362b0d5d4482c 100644 (file)
@@ -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;
index 140891938970fa126a9f777cdf4dc544d47fd4e7..b59ece4f1d738703aaf2b2ee75524fa33d598d79 100644 (file)
@@ -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 (file)
index 0000000..3080ade
--- /dev/null
@@ -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 "" } */