.. Copyright (C) 2024-2025 Free Software Foundation, Inc. Originally contributed by David Malcolm 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 . .. default-domain:: c Physical locations ================== A "physical" source location is a location expressed in terms of a specific file, and line(s) and column(s) (as opposed to a :doc:`"logical" location `, which refers to semantic constructs in a programming language). Creating location information ***************************** The :type:`diagnostic_manager` manages various objects relating to locations. .. type:: diagnostic_file A :type:`diagnostic_file` is an opaque type describing a particular input file. .. function:: diagnostic_file * diagnostic_manager_new_file (diagnostic_manager *diag_mgr, \ const char *name, \ const char *sarif_source_language) Create a new :type:`diagnostic_file` for file ``name``. Repeated calls with strings that match ``name`` will return the same object. Both ``diag_mgr`` and ``name`` must be non-NULL. If ``sarif_source_language`` is non-NULL, it specifies a ``sourceLanguage`` value for the file for use when writing :doc:`SARIF ` (`SARIF v2.1.0 ยง3.24.10 `_). See `SARIF v2.1.0 Appendix J `_ for suggested values for various programmming languages. For example, this creates a :type:`diagnostic_file` for ``foo.c`` and identifies it as C source code:: foo_c = diagnostic_manager_new_file (diag_mgr, "foo.c", "c" /* source_language */); .. function:: void diagnostic_manager_debug_dump_file (diagnostic_manager *diag_mgr, \ const diagnostic_file *file, \ FILE *out) Write a representation of ``file`` to ``out``, for debugging. Both ``diag_mgr`` and ``out`` must be non-NULL. `file`` may be NULL. For example:: diagnostic_manager_debug_dump_file (diag_mgr, foo_c, stderr); might lead to this output:: file(name="foo.c", sarif_source_language="c") .. type:: diagnostic_line_num_t A :type:`diagnostic_line_num_t` is used for representing line numbers within text files. libgdiagnostics treats the first line of a text file as line 1. .. type:: diagnostic_column_num_t A :type:`diagnostic_column_num_t` is used for representing column numbers within text files. libgdiagnostics treats the first column of a text line as column 1, **not** column 0. .. note:: Both libgdiagnostics and Emacs number source *lines* starting at 1, but they have differing conventions for *columns*. libgdiagnostics uses a 1-based convention for source columns, whereas Emacs's ``M-x column-number-mode`` uses a 0-based convention. For example, an error in the initial, left-hand column of source line 3 is reported by libgdiagnostics as:: some-file.c:3:1: error: ...etc... On navigating to the location of that error in Emacs (e.g. via ``next-error``), the locus is reported in the Mode Line (assuming ``M-x column-number-mode``) as:: some-file.c 10% (3, 0) i.e. ``3:1:`` in libgdiagnostics corresponds to ``(3, 0)`` in Emacs. .. type:: diagnostic_physical_location A :type:`diagnostic_physical_location` is an opaque type representing a key into a database of source locations within a :type:`diagnostic_manager`. :type:`diagnostic_physical_location` instances are created by various API calls into the :type:`diagnostic_manager` expressing source code points and ranges. They persist until the :type:`diagnostic_manager` is released, which cleans them up. A ``NULL`` value means "unknown", and can be returned by the :type:`diagnostic_manager` as a fallback when a problem occurs (e.g. too many locations). A :type:`diagnostic_physical_location` can be a single point within the source code, such as here (at the the '"' at the start of the string literal):: int i = "foo"; ^ or be a range with a start and finish, and a "caret" location:: a = (foo && bar) ~~~~~^~~~~~~ where the caret here is at the first "&", and the start and finish are at the parentheses. .. function:: const diagnostic_physical_location *diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, \ const diagnostic_file *file, \ diagnostic_line_num_t line_num) Attempt to create a :type:`diagnostic_physical_location` representing ``FILENAME:LINE_NUM``, with no column information (thus representing the whole of the given line. Both ``diag_mgr`` and ``file`` must be non-NULL. .. function:: const diagnostic_physical_location * diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr, \ const diagnostic_file *file, \ diagnostic_line_num_t line_num, \ diagnostic_column_num_t column_num) Attempt to create a :type:`diagnostic_physical_location` for ``FILENAME:LINE_NUM:COLUMN_NUM`` representing a particular point in the source file. Both ``diag_mgr`` and ``file`` must be non-NULL. .. function:: const diagnostic_physical_location *diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr,\ const diagnostic_physical_location *loc_caret,\ const diagnostic_physical_location *loc_start,\ const diagnostic_physical_location *loc_end) Attempt to create a diagnostic_physical_location representing a range within a source file, with a highlighted "caret" location. All must be within the same file, but they can be on different lines. For example, consider the location of the binary expression below:: ...|__________1111111112222222 ...|12345678901234567890123456 ...| 521|int sum (int foo, int bar) 522|{ 523| return foo + bar; ...| ~~~~^~~~~ 524|} The location's caret is at the "+", line 523 column 15, but starts earlier, at the "f" of "foo" at column 11. The finish is at the "r" of "bar" at column 19. ``diag_mgr`` must be non-NULL. .. function:: void diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr,\ const diagnostic_physical_location *loc, \ FILE *out) Write a representation of ``loc`` to ``out``, for debugging. Both ``diag_mgr`` and ``out`` must be non-NULL. `loc`` may be NULL. TODO: example of output .. function:: diagnostic_file *diagnostic_physical_location_get_file (const diagnostic_physical_location *physical_loc) Get the :type:`diagnostic_file` associated with a given physical location. Associating diagnostics with locations ************************************** A :type:`diagnostic` has an optional primary physical location and zero or more secondary physical locations. For example:: a = (foo && bar) ~~~~~^~~~~~~ This diagnostic has a single :type:`diagnostic_physical_location`, with the caret at the first "&", and the start/finish at the parentheses. Contrast with:: a = (foo && bar) ~~~ ^~ ~~~ This diagnostic has three locations * The primary location (at "&&") has its caret and start location at the first "&" and end at the second "&. * The secondary location for "foo" has its start and finish at the "f" and "o" of "foo"; the caret is not displayed, but is perhaps at the "f" of "foo". * Similarly, the other secondary location (for "bar") has its start and finish at the "b" and "r" of "bar"; the caret is not displayed, but is perhaps at the"b" of "bar". .. function:: void diagnostic_set_location (diagnostic *diag, \ const diagnostic_physical_location * loc) Set the primary location of ``diag``. ``diag`` must be non-NULL; ``loc`` can be NULL. .. function:: void diagnostic_set_location_with_label (diagnostic *diag, \ const diagnostic_physical_location *loc, \ const char *fmt, ...) Set the primary location of ``diag``, with a label. The label is formatted as per the rules FIXME ``diag`` and ``fmt`` must be non-NULL; ``loc`` can be NULL. See :doc:`message-formatting` for details of how to use ``fmt``. TODO: example of use .. function:: void diagnostic_add_location (diagnostic *diag, \ const diagnostic_physical_location * loc) Add a secondary location to ``diag``. ``diag`` must be non-NULL; ``loc`` can be NULL. .. function:: void diagnostic_add_location_with_label (diagnostic *diag, \ const diagnostic_physical_location *loc, \ const char *text) Add a secondary location to ``diag``, with a label. The label is formatted as per the rules FIXME ``diag`` and ``fmt`` must be non-NULL; ``loc`` can be NULL. For example, .. literalinclude:: ../../../testsuite/libgdiagnostics.dg/test-labelled-ranges.c :language: c :start-after: /* begin quoted source */ :end-before: /* end quoted source */ might give this text output:: test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char *' 19 | 42 + "foo" | ~~ ^ ~~~~~ | | | | int const char * .. function:: void diagnostic_add_location_with_label_via_msg_buf (diagnostic *diag, \ const diagnostic_physical_location *loc, \ diagnostic_message_buffer *msg_buf) This is equivalent to :func:`diagnostic_add_location_with_label` but using a message buffer rather than a text string. ``diag`` and ``msg_buf`` must both be non-NULL. Calling this function transfers ownership of ``msg_buf`` to the diagnostic - do not call :func:`diagnostic_message_buffer_release` on it. This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`; you can test for its presence using .. code-block:: c #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer .. function:: void diagnostic_manager_set_debug_physical_locations (diagnostic_manager *mgr, \ int value) Calling ``diagnostic_manager_set_debug_physical_locations (mgr, 1);`` will lead to debugging information being printed to ``stderr`` when creating :type:`diagnostic_physical_location` instances. The precise format of these messages is subject to change. This function was added in :ref:`LIBGDIAGNOSTICS_ABI_5`; you can test for its presence using .. code-block:: c #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_debug_physical_locations