--- /dev/null
+.. 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`
text-output.rst
sarif.rst
ux.rst
+ compatibility.rst
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
: 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;
};
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
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 ());
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 ();
+}
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
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;
const int line_num = __LINE__ - 2;
#include <assert.h>
+#include <string.h>
int
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
--- /dev/null
+/* 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 "" } */