gcc/ChangeLog:
        * doc/libdiagnostics/Makefile: New file.
        * doc/libdiagnostics/conf.py: New file.
        * doc/libdiagnostics/index.rst: New file.
        * doc/libdiagnostics/make.bat: New file.
        * doc/libdiagnostics/topics/diagnostic-manager.rst: New file.
        * doc/libdiagnostics/topics/diagnostics.rst: New file.
        * doc/libdiagnostics/topics/execution-paths.rst: New file.
        * doc/libdiagnostics/topics/fix-it-hints.rst: New file.
        * doc/libdiagnostics/topics/index.rst: New file.
        * doc/libdiagnostics/topics/logical-locations.rst: New file.
        * doc/libdiagnostics/topics/message-formatting.rst: New file.
        * doc/libdiagnostics/topics/metadata.rst: New file.
        * doc/libdiagnostics/topics/physical-locations.rst: New file.
        * doc/libdiagnostics/topics/retrofitting.rst: New file.
        * doc/libdiagnostics/topics/sarif.rst: New file.
        * doc/libdiagnostics/topics/text-output.rst: New file.
        * doc/libdiagnostics/topics/ux.rst: New file.
        * doc/libdiagnostics/tutorial/01-hello-world.rst: New file.
        * doc/libdiagnostics/tutorial/02-physical-locations.rst: New file.
        * doc/libdiagnostics/tutorial/03-logical-locations.rst: New file.
        * doc/libdiagnostics/tutorial/04-notes.rst: New file.
        * doc/libdiagnostics/tutorial/05-warnings.rst: New file.
        * doc/libdiagnostics/tutorial/06-fix-it-hints.rst: New file.
        * doc/libdiagnostics/tutorial/07-execution-paths.rst: New file.
        * doc/libdiagnostics/tutorial/index.rst: New file.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/doc/libdiagnostics/Makefile               |  20 ++
 gcc/doc/libdiagnostics/conf.py                |  27 ++
 gcc/doc/libdiagnostics/index.rst              | 113 +++++++
 gcc/doc/libdiagnostics/make.bat               |  35 +++
 .../topics/diagnostic-manager.rst             |  58 ++++
 gcc/doc/libdiagnostics/topics/diagnostics.rst | 127 ++++++++
 .../libdiagnostics/topics/execution-paths.rst |  93 ++++++
 .../libdiagnostics/topics/fix-it-hints.rst    | 135 +++++++++
 gcc/doc/libdiagnostics/topics/index.rst       |  38 +++
 .../topics/logical-locations.rst              | 109 +++++++
 .../topics/message-formatting.rst             | 224 ++++++++++++++
 gcc/doc/libdiagnostics/topics/metadata.rst    | 149 ++++++++++
 .../topics/physical-locations.rst             | 281 ++++++++++++++++++
 .../libdiagnostics/topics/retrofitting.rst    |  23 ++
 gcc/doc/libdiagnostics/topics/sarif.rst       |  51 ++++
 gcc/doc/libdiagnostics/topics/text-output.rst |  87 ++++++
 gcc/doc/libdiagnostics/topics/ux.rst          |  26 ++
 .../tutorial/01-hello-world.rst               | 173 +++++++++++
 .../tutorial/02-physical-locations.rst        | 260 ++++++++++++++++
 .../tutorial/03-logical-locations.rst         |  60 ++++
 gcc/doc/libdiagnostics/tutorial/04-notes.rst  |  66 ++++
 .../libdiagnostics/tutorial/05-warnings.rst   |  44 +++
 .../tutorial/06-fix-it-hints.rst              |  61 ++++
 .../tutorial/07-execution-paths.rst           | 141 +++++++++
 gcc/doc/libdiagnostics/tutorial/example-1.png | Bin 0 -> 5646 bytes
 gcc/doc/libdiagnostics/tutorial/index.rst     |  32 ++
 26 files changed, 2433 insertions(+)
 create mode 100644 gcc/doc/libdiagnostics/Makefile
 create mode 100644 gcc/doc/libdiagnostics/conf.py
 create mode 100644 gcc/doc/libdiagnostics/index.rst
 create mode 100644 gcc/doc/libdiagnostics/make.bat
 create mode 100644 gcc/doc/libdiagnostics/topics/diagnostic-manager.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/diagnostics.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/execution-paths.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/fix-it-hints.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/index.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/logical-locations.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/message-formatting.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/metadata.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/physical-locations.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/retrofitting.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/sarif.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/text-output.rst
 create mode 100644 gcc/doc/libdiagnostics/topics/ux.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/01-hello-world.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/04-notes.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/05-warnings.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst
 create mode 100644 gcc/doc/libdiagnostics/tutorial/example-1.png
 create mode 100644 gcc/doc/libdiagnostics/tutorial/index.rst

diff --git a/gcc/doc/libdiagnostics/Makefile b/gcc/doc/libdiagnostics/Makefile
new file mode 100644
index 000000000000..d4bb2cbb9edd
--- /dev/null
+++ b/gcc/doc/libdiagnostics/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+       @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+       @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/gcc/doc/libdiagnostics/conf.py b/gcc/doc/libdiagnostics/conf.py
new file mode 100644
index 000000000000..1ff7552411d0
--- /dev/null
+++ b/gcc/doc/libdiagnostics/conf.py
@@ -0,0 +1,27 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# 
https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'libdiagnostics'
+copyright = '2024, David Malcolm'
+author = 'David Malcolm'
+
+# -- General configuration ---------------------------------------------------
+# 
https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = []
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# 
https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'alabaster'
+html_static_path = ['_static']
diff --git a/gcc/doc/libdiagnostics/index.rst b/gcc/doc/libdiagnostics/index.rst
new file mode 100644
index 000000000000..a05eb4ef261d
--- /dev/null
+++ b/gcc/doc/libdiagnostics/index.rst
@@ -0,0 +1,113 @@
+.. Copyright (C) 2024 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/>.
+
+libdiagnostics
+==============
+
+This document describes `libdiagnostics 
<https://gcc.gnu.org/wiki/libdiagnostics>`_,
+an API for programs to use to emit diagnostics (such as for "lint"-style 
checker
+tools), supporting:
+
+* text output similar to GCC's errors and warnings::
+
+    test-typo.c:19:13: error: unknown field 'colour'
+       19 |   return p->colour;
+          |             ^~~~~~
+
+  quoting pertinent source code (with a cache), and underlining
+  :doc:`points and ranges in the files being tested 
<tutorial/02-physical-locations>`,
+  possibly with labels::
+
+   test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char 
*'
+      19 |   42 + "foo"
+         |   ~~ ^ ~~~~~
+         |   |    |
+         |   int  const char *
+
+* emitting :doc:`fix-it hints <tutorial/06-fix-it-hints>`::
+
+   test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 
'color'
+      19 |   return p->colour;
+         |             ^~~~~~
+         |             color
+
+  and generating patches from them::
+
+   @@ -16,7 +16,7 @@
+    struct rgb
+    get_color (struct object *p)
+    {
+   -  return p->colour;
+   +  return p->color;
+    }
+
+* capturing :doc:`execution paths<tutorial/07-execution-paths>` through code::
+
+   In function 'make_a_list_of_random_ints_badly':
+   test-warning-with-path.c:30:5: warning: passing NULL as argument 1 to 
'PyList_Append' which requires a non-NULL parameter"
+      30 |     PyList_Append(list, item);
+         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
+   make_a_list_of_random_ints_badly': events 1-3
+      26 |   list = PyList_New(0);
+         |          ^~~~~~~~~~~~~
+         |          |
+         |          (1) when 'PyList_New' fails, returning NULL
+      27 |
+      28 |   for (i = 0; i < count; i++) {
+         |               ~~~~~~~~~
+         |               |
+         |               (2) when 'i < count'
+      29 |     item = PyLong_FromLong(random());
+      30 |     PyList_Append(list, item);
+         |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+         |     |
+         |     (3) when calling 'PyList_Append', passing NULL from (1) as 
argument 1
+
+* support for emitting machine-readable representations of the above
+  using the :doc:`SARIF file format <topics/sarif>`
+
+There are actually two APIs for the library:
+
+* a pure C API: ``libdiagnostics.h``
+
+* a C++ wrapper API: ``libdiagnostics+.h``.  This is a header-only
+  collection of wrapper classes around the C API to give a less
+  verbose API.
+
+This documentation covers the C API.
+
+Contents
+********
+
+.. toctree::
+   :maxdepth: 2
+
+   tutorial/index.rst
+   topics/index.rst
+
+libdiagnostics 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.
+
+
+Indices and tables
+******************
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/gcc/doc/libdiagnostics/make.bat b/gcc/doc/libdiagnostics/make.bat
new file mode 100644
index 000000000000..954237b9b9f2
--- /dev/null
+++ b/gcc/doc/libdiagnostics/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+       set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+       echo.
+       echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+       echo.installed, then set the SPHINXBUILD environment variable to point
+       echo.to the full path of the 'sphinx-build' executable. Alternatively 
you
+       echo.may add the Sphinx directory to PATH.
+       echo.
+       echo.If you don't have Sphinx installed, grab it from
+       echo.https://www.sphinx-doc.org/
+       exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst 
b/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst
new file mode 100644
index 000000000000..7f86f6bba613
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst
@@ -0,0 +1,58 @@
+.. Copyright (C) 2024 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
+
+Diagnostic Managers
+===================
+
+.. type:: diagnostic_manager;
+
+A :type:`diagnostic_manager` is an opaque bundle of state for a client of
+libdiagnostics.
+
+It has zero of more "output sinks" to which diagnostics are emitted.
+
+Responsibilities include:
+
+* location-management
+
+* caching of source file content
+
+* patch generation
+
+.. function:: diagnostic_manager *diagnostic_manager_new (void)
+
+   Create a new diagnostic_manager.
+   The caller will need to call :func:`diagnostic_release_manager`
+   on it at some point.
+
+   .. note:: No output sinks are created by default; so you will want
+      to create one with something like:
+
+      .. code-block::
+
+       diagnostic_manager_add_text_sink (diag_mgr, stderr,
+                                          DIAGNOSTIC_COLORIZE_IF_TTY);
+
+.. function::  void diagnostic_manager_release (diagnostic_manager *diag_mgr)
+
+   Release a diagnostic_manager.
+
+   This will flush output to all of the output sinks, and clean up.
+
+   The parameter must be non-NULL.
diff --git a/gcc/doc/libdiagnostics/topics/diagnostics.rst 
b/gcc/doc/libdiagnostics/topics/diagnostics.rst
new file mode 100644
index 000000000000..66f0a25143cb
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/diagnostics.rst
@@ -0,0 +1,127 @@
+.. Copyright (C) 2024 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
+
+Diagnostics
+===========
+
+.. type:: diagnostic
+
+A :type:`diagnostic` is an opaque bundle of state for a particular
+diagnostic that is being constructed in memory.
+
+
+Lifecycle of a diagnostic
+*************************
+
+Diagnostics are
+
+* *created* from a :type:`diagnostic_manager` by using
+  :func:`diagnostic_begin`, then
+
+* *populated* with data, such as physical locations, logical locations,
+  metadata, execution paths, or fix-it hints, then
+
+* *finished*, in which a formatting string and arguments are given,
+  via a call to :func:`diagnostic_finish` or :func:`diagnostic_finish_va`.
+  The :type:`diagnostic_manager` will emit the diagnostic to all of the
+  manager's output sinks (either immediately, or at some later time,
+  depending on the sink).
+
+  Once a :type:`diagnostic` has had one of these "finish" functions called
+  on it, it is freed, and is no longer valid for use.
+
+  The formatting strings use their own syntax; see :doc:`message-formatting`.
+
+.. function::  diagnostic *diagnostic_begin (diagnostic_manager *diag_mgr, \
+                                             enum diagnostic_level level)
+
+   Create a new :type:`diagnostic` associated with the given
+   :type:`diagnostic_manager`.
+
+   The parameter ``diag_mgr`` must be non-NULL.
+
+   The parameter ``level`` describes the severity of the diagnostic.
+
+.. enum:: diagnostic_level
+
+   This enum describes the severity of a particular diagnostic.
+
+   .. macro:: DIAGNOSTIC_LEVEL_ERROR
+
+      A problem sufficiently severe that the program cannot successfully
+      complete, or where the input being analyzed is definitely wrong
+      (e.g. malformed).
+
+   .. macro:: DIAGNOSTIC_LEVEL_WARNING
+
+      A problem where the input is technically correct, but is likely
+      not what the user intended, such as common mistakes, or other
+      unusual conditions that *may* indicate trouble, such as use of
+      obsolete features.
+
+   .. macro:: DIAGNOSTIC_LEVEL_NOTE
+
+      A supplementary message added to another :type:`diagnostic`, giving
+      extra information that may help the user understand it.
+
+   .. macro:: DIAGNOSTIC_LEVEL_SORRY
+
+      A problem where the input is valid, but the tool isn't
+      able to handle it.
+
+.. function:: void diagnostic_finish (diagnostic *diag, const char *fmt, ...)
+
+   Emit ``diag`` to all sinks of its manager, and release ``diag``.  It is not
+   valid to use ``diag`` after this call.
+
+   Use parameter ``fmt`` for the message.
+   Note that this uses gcc's pretty-print format, which is *not* printf.
+   See :doc:`message-formatting`.
+
+   Both ``diag`` and ``fmt`` must be non-NULL.
+
+   TODO: who is responsible for putting FMT through gettext?
+
+.. function:: void diagnostic_finish_va (diagnostic *diag, const char *fmt, 
va_list *args)
+
+   This is equivalent to :func:`diagnostic_finish`, but using a
+   :type:`va_list` rather than directly taking variadic arguments.
+
+   All three parameters must be non-NULL.
+
+
+Diagnostic groups
+*****************
+
+See :doc:`the "adding notes" section of the tutorial <../tutorial/04-notes>`
+for an example of a diagnostic group.
+
+.. function:: void diagnostic_manager_begin_group (diagnostic_manager 
*diag_mgr)
+
+  Begin a diagnostic group.  All diagnostics emitted within
+  ``diag_mgr`` after the first one will be treated as additional information
+  relating to the initial diagnostic.
+
+  The parameter ``diag_mgr`` must be non-NULL.
+
+.. function:: void diagnostic_manager_end_group (diagnostic_manager *diag_mgr)
+
+   Finish a diagnostic group.
+
+   The parameter ``diag_mgr`` must be non-NULL.
diff --git a/gcc/doc/libdiagnostics/topics/execution-paths.rst 
b/gcc/doc/libdiagnostics/topics/execution-paths.rst
new file mode 100644
index 000000000000..3f4109ce4f33
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/execution-paths.rst
@@ -0,0 +1,93 @@
+.. Copyright (C) 2024 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
+
+Execution paths
+===============
+
+.. type:: diagnostic_execution_path
+
+A :type:`diagnostic` can optionally contain a :type:`diagnostic_execution_path`
+describing a path of execution through code.
+
+.. function:: diagnostic_execution_path * diagnostic_add_execution_path 
(diagnostic *diag)
+
+   Create and borrow a pointer to an execution path for ``diag``.
+
+   The path is automatically cleaned up when ``diag`` is finished.
+
+   ``diag`` must be non-NULL.
+
+.. function:: diagnostic_execution_path * 
diagnostic_manager_new_execution_path (diagnostic_manager *diag_mgr)
+
+   Create a new execution path. This is owned by the caller and must have 
either
+   :func:`diagnostic_take_execution_path` or
+   :func:`diagnostic_execution_path_release` called on it.
+
+   ``diag_mgr`` must be non-NULL.
+
+.. function:: void diagnostic_take_execution_path (diagnostic *diag, 
diagnostic_execution_path *path)
+
+   Set ``diag`` to use ``path`` as its execution path, taking ownership of 
``path``.
+
+   Both parameters must be non-NULL.
+
+.. function:: void diagnostic_execution_path_release 
(diagnostic_execution_path *path)
+
+   Release ownership of ``path``, which must not have been taken by a 
diagnostic.
+
+.. type:: diagnostic_event_id
+
+A :type:`diagnostic_event_id` identifies a particular event within a
+:type:`diagnostic_execution_path` and can be used for expressing
+cross-references between events.  In particular FIXME
+
+.. function:: diagnostic_event_id diagnostic_execution_path_add_event 
(diagnostic_execution_path *path, \
+                                                                       const 
diagnostic_physical_location *physical_loc, \
+                                                                       const 
diagnostic_logical_location *logical_loc, \
+                                                                       
unsigned stack_depth, \
+                                                                       const 
char *fmt, ...)
+
+   Append an event to the end of ``path``, which must be non-NULL.
+
+   ``physical_loc`` can be NULL, or non-NULL to associate the event
+   with a :type:`diagnostic_physical_location`.
+
+   ``logical_loc`` can be NULL, or non-NULL to associate the event
+   with a :type:`diagnostic_logical_location`.
+
+   ``stack_depth`` is for use in interprocedural paths and identifies the
+   depth of the stack at the event.  Purely intraprocedural paths should
+   use a stack depth of 1 for their events
+
+   ``fmt`` must be non-NULL.  See :doc:`message-formatting` for details of
+   how to use it.
+
+.. function:: diagnostic_event_id diagnostic_execution_path_add_event_va 
(diagnostic_execution_path *path, \
+                                                                          
const diagnostic_physical_location *physical_loc, \
+                                                                          
const diagnostic_logical_location *logical_loc, \
+                                                                          
unsigned stack_depth, \
+                                                                          
const char *fmt, \
+                                                                          
va_list *args)
+
+   Equivalent to :func:`diagnostic_execution_path_add_event`, but using a
+   :type:`va_list` rather than directly taking variadic arguments.
+
+Paths are printed to text sinks, and for SARIF sinks each path is added as
+a ``codeFlow`` object (see SARIF 2.1.0
+`3.36 codeFlow object 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790990>`_).
diff --git a/gcc/doc/libdiagnostics/topics/fix-it-hints.rst 
b/gcc/doc/libdiagnostics/topics/fix-it-hints.rst
new file mode 100644
index 000000000000..08acb7119a14
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/fix-it-hints.rst
@@ -0,0 +1,135 @@
+.. Copyright (C) 2024 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
+
+Fix-it hints
+============
+
+Adding fix-it hints to a diagnostic
+***********************************
+
+A :type:`diagnostic` can contain "fix-it hints", giving suggestions
+for the user on how to edit their code to fix a problem.  These
+can be expressed as insertions, replacements, and removals of text.
+
+There is only limited support for newline characters in fix-it hints:
+only hints with newlines which insert an entire new line are permitted,
+inserting at the start of a line, and finishing with a newline
+(with no interior newline characters).  Other attempts to add
+fix-it hints containing newline characters will fail.
+Similarly, attempts to delete or replace a range *affecting* multiple
+lines will fail.
+
+The API handles these failures gracefully, so that diagnostics can attempt
+to add fix-it hints without each needing extensive checking.
+
+Fix-it hints are printed to text sinks, and are emitted by SARIF sinks
+as ``fix`` objects (see SARIF 2.1.0
+`3.55 fix object 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141791131>`_).
+
+Fix-it hints within a :type:`diagnostic` are "atomic": if any hints can't
+be applied, none of them will be, and no fix-its hints will be displayed
+for that diagnostic.  This implies that diagnostic messages need to be worded
+in such a way that they make sense whether or not the fix-it hints
+are displayed.
+
+All fix-it hints within one :type:`diagnostic` must affect the same
+:type:`diagnostic_file`.
+
+.. function:: void diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, 
\
+                                                             const 
diagnostic_physical_location *loc, \
+                                                             const char 
*addition)
+
+   Add a fix-it hint to ``diag`` suggesting the insertion of the string
+   ``addition`` before ``LOC``.
+
+   For example::
+
+     ptr = arr[0];
+           ^~~~~~
+           &
+
+   This :type:`diagnostic` has a single location covering ``arr[0]``,
+   with the caret at the start.  It has a single insertion fix-it hint,
+   inserting ``&`` before the start of ``loc``.
+
+.. function:: void diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, \
+                                                            const 
diagnostic_physical_location *loc, \
+                                                            const char 
*addition)
+
+   Add a fix-it hint to ``diag`` suggesting the insertion of the string
+   ``addition`` after the end of ``LOC``.
+
+   For example, in::
+
+      #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
+                                      ^~~~  ^~~~  ^~~~
+                                      (   ) (   ) (   )
+
+
+   the :type:`diagnostic` has three physical locations, covering ``ARG0``,
+   ``ARG1``, and ``ARG2``, and 6 insertion fix-it hints: each arg
+   has a pair of insertion fix-it hints, suggesting wrapping
+   them with parentheses: one a '(' inserted before,
+   the other a ')' inserted after.
+
+.. function:: void diagnostic_add_fix_it_hint_replace (diagnostic *diag, \
+                                                       const 
diagnostic_physical_location *loc, \
+                                                       const char *replacement)
+
+   Add a fix-it hint to ``diag`` suggesting the replacement of the text
+   at ``LOC`` with the string ``replacement``.
+
+   For example, in::
+
+      c = s.colour;
+           ^~~~~~
+           color
+
+   This :type:`diagnostic` has a single physical location covering ``colour``,
+   and a single "replace" fix-it hint, covering the same range, suggesting
+   replacing it with ``color``.
+
+.. function:: void diagnostic_add_fix_it_hint_delete (diagnostic *diag, \
+                                                      const 
diagnostic_physical_location *loc)
+
+   Add a fix-it hint to ``diag`` suggesting the deletion of the text
+   at ``LOC``.
+
+
+   For example, in::
+
+     struct s {int i};;
+                     ^
+                     -
+
+   This :type:`diagnostic` has a single physical location at the stray
+   trailing semicolon, along with a single removal fix-it hint, covering
+   the same location.
+
+
+Generating patches
+******************
+
+.. function:: void diagnostic_manager_write_patch (diagnostic_manager 
*diag_mgr, \
+                                                   FILE *dst_stream)
+
+   Write a patch to ``dst_stream`` consisting of the effect of all fix-it hints
+   on all diagnostics that have been finished on ``diag_mgr``.
+
+   Both parameters must be non-NULL.
diff --git a/gcc/doc/libdiagnostics/topics/index.rst 
b/gcc/doc/libdiagnostics/topics/index.rst
new file mode 100644
index 000000000000..064340b98ba7
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/index.rst
@@ -0,0 +1,38 @@
+.. Copyright (C) 2024 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
+
+
+Topic reference
+===============
+
+.. toctree::
+   :maxdepth: 2
+
+   retrofitting.rst
+   diagnostic-manager.rst
+   diagnostics.rst
+   message-formatting.rst
+   physical-locations.rst
+   logical-locations.rst
+   metadata.rst
+   fix-it-hints.rst
+   execution-paths.rst
+   text-output.rst
+   sarif.rst
+   ux.rst
diff --git a/gcc/doc/libdiagnostics/topics/logical-locations.rst 
b/gcc/doc/libdiagnostics/topics/logical-locations.rst
new file mode 100644
index 000000000000..85900b6344f2
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/logical-locations.rst
@@ -0,0 +1,109 @@
+.. Copyright (C) 2024 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
+
+Logical locations
+=================
+
+A "logical" location is a location expressed in terms of
+construct in a programming language, such as ``within function 'foo'``
+(as opposed to a :doc:`"physical" location <physical-locations>`, which
+refers to a specific file, and line(s) and/or column(s))
+
+Creating location information
+*****************************
+
+.. type:: diagnostic_logical_location
+
+A :type:`diagnostic_logical_location` is an opaque type describing a "logical"
+source location
+
+.. function:: const diagnostic_logical_location * 
diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr, \
+                                                                               
            enum diagnostic_logical_location_kind_t kind, \
+                                                                               
            const diagnostic_logical_location *parent, \
+                                                                               
            const char *short_name, \
+                                                                               
            const char *fully_qualified_name, \
+                                                                               
            const char *decorated_name)
+
+   Create a :type:`diagnostic_logical_location`.
+
+   ``diag_mgr`` must be non-NULL.
+
+   ``kind`` describes the kind of logical location:
+
+   .. enum:: diagnostic_logical_location_kind_t
+
+      This roughly corresponds to the ``kind`` property in SARIF v2.1.0
+      (`§3.33.7 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790976>`_).
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
+
+   ``parent`` can be NULL; if non-NULL it can be used to express tree-like
+   nesting of logical locations, such as in::
+
+     namespace foo { namespace bar { class baz { baz (); }; } }
+
+   where a diagnostic within ``baz``'s constructor could be reported
+   as being within ``foo::bar::baz::baz`` where the logical locations
+   are two namespaces, a type, and a member, respectively.
+
+   ``short_name`` can be NULL, or else a string suitable for use by
+   the SARIF logicalLocation ``name`` property
+   (SARIF v2.1.0 `§3.33.4 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790973>`_).
+
+   ``fully_qualified_name`` can be NULL or else a string  suitable for use by
+   the SARIF logicalLocation ``fullyQualifiedName`` property
+   (SARIF v2.1.0 `§3.33.5 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790974>`_).
+
+   ``decorated_name`` can be NULL or else a string suitable for use by
+   the SARIF logicalLocation ``decoratedName`` property
+   (SARIF v2.1.0 `§3.33.6 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790975>`_).
+
+.. function:: void diagnostic_manager_debug_dump_logical_location (const 
diagnostic_manager *diag_mgr, \
+                                                                   const 
diagnostic_logical_location *loc, \
+                                                                   FILE *out)
+
+   Write a representation of ``file`` to ``out``, for debugging.
+   Both ``diag_mgr`` and ``out`` must be non-NULL.
+   ``file`` may be NULL.
+
+   TODO: example of output
+
+Associating diagnostics with locations
+**************************************
+
+.. function:: void diagnostic_set_logical_location (diagnostic *diag, \
+                                                    const 
diagnostic_logical_location *logical_loc)
+
+    Set the logical location of ``diag``.
+
+    ``diag`` must be non-NULL; ``logical_loc`` can be NULL.
diff --git a/gcc/doc/libdiagnostics/topics/message-formatting.rst 
b/gcc/doc/libdiagnostics/topics/message-formatting.rst
new file mode 100644
index 000000000000..9d42f8937d0d
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/message-formatting.rst
@@ -0,0 +1,224 @@
+.. Copyright (C) 2024 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
+
+Message formatting
+==================
+
+Various libdiagnostics entrypoints take a format string and
+variadic arguments.
+
+The format strings take codes prefixed by ``%``, or ``%q`` to put
+the result in quotes.  For example::
+
+   "hello %s", "world"
+
+would print::
+
+   hello world
+
+whereas::
+
+   "hello %qs", "world"
+
+would print::
+
+   hello `world'
+
+where ```world'`` would be displayed in bold if colorization were enabled
+in the terminal.
+
+The following format specifiers are accepted:
+
+
+Numbers
+*******
+
+``d`` and ``i`` (``signed int``), ``u`` (``unsigned int``)
+   ``%d``, ``%i``, and ``%u`` print integers in base ten.  For example::
+
+     "the answer is %i", 42
+
+   would print::
+
+     the answer is 42
+
+``o`` (``unsigned int``)
+   Print the integer in base eight
+
+``x`` (``unsigned int``)
+   Print the integer in base sixteen
+
+The above can be prefixed with ``l`` and ``ll`` prefixes to take
+``long`` and ``long long`` values of the appropriate signedness.
+
+For example::
+
+   "address: %lx", (unsigned long)0x108b516
+
+would print::
+
+   address: 108b516
+
+Similarly, the prefix ``z`` can be used for ``size_t``::
+
+  "size: %zd", sizeof(struct foo)
+  size: 32
+
+and ``t`` for ptrdiff_t.
+  
+``f`` (``double``)
+   ``%f`` prints a floating-point value.  For example::
+
+     "value: %f", 1.0
+
+   might print::
+
+     value: 1.000000
+
+
+Strings
+*******
+
+``c`` (``char``)
+   ``%c`` prints a single character.
+
+``s`` (``const char *``)
+   ``%s`` prints a string.
+
+   Note that if the string refers to something that might
+   appear in the input file (such as the name of a function), it's better
+   to quote the value; for example::
+
+     "unrecognized identifier: %qs", "foo"
+
+   might print::
+
+     unrecognized identifier: `foo'
+
+``m`` (no argument)
+   Prints ``strerror(errno)``, for example::
+
+     "can't open %qs: %m"
+
+   might print::
+
+     can't open `foo.txt': No such file or directory
+
+``%`` (no argument)
+   ``%%`` prints a `%` character, for example::
+
+     "8%% of 75 is 75%% of 8, and is thus 6"
+
+   prints::
+
+     8% of 75 is 75% of 8, and is thus 6
+
+``'`` (no argument)
+  ``%'`` prints an apostrophe.  This should only be used in untranslated 
messages;
+  translations should use appropriate punctuation directly.
+
+
+Other format specifiers
+***********************
+
+``p`` (pointer)
+   ``%p`` prints a pointer, although the precise format is
+   implementation-defined.
+
+``r`` (``const char *``)
+   ``%r`` starts colorization on suitable text sinks, where the argument
+   specifies the name of the kind of entity to be colored, such as ``error``.
+
+``R`` (no argument)
+   ``%R`` stops colorization
+
+``<`` and ``>`` (no arguments)
+   ``%<`` adds an opening quote and ``%>`` a closing quote, such as::
+
+     "missing element %<%s:%s%>", ns, name
+
+   which might be printed as::
+
+     missing element `xhtml:head'
+
+   If the thing to be quoted can be handled with another format specifier,
+   then it's simpler to use ``q`` with it.  For example, it's much
+   simpler to print a ``const char *`` in quotes via::
+
+      "%qs", str
+
+   rather than the error-prone::
+     
+      "%<%s%>", str
+
+``{`` (``const char *``)
+   ``%{`` starts a link; the argument is the URL.  This will be displayed
+   in a suitably-capable terminal if a text sink is directly connected to
+   a tty, and will be captured in SARIF output.
+
+``}`` (no argument)
+   ``%}`` stops a link started with ``%{``.
+
+   For example::
+
+      "for more information see %{the documentation%}", "https://example.com";
+
+   would be printed as::
+
+      for more information see the documentation
+
+   with the URL emitted in suitable output sinks.
+
+``@`` (``diagnostic_event_id *``)
+   ``%@`` prints a reference to an event in a
+   :type:`diagnostic_execution_path`, where the :type:`diagnostic_event_id`
+   is passed by pointer.
+
+   For example, if ``event_id`` refers to the first event in a path, then::
+
+      "double-%qs of %qs; first %qs was at %@",
+      function, ptr, function, &event_id
+
+   might print::
+
+     double-`free' of `p'; first `free` was at (1)
+
+.. :
+
+   TODO:
+
+   %.*s: a substring the length of which is specified by an argument
+        integer.
+   %Ns: likewise, but length specified as constant in the format string.
+   %Z: Requires two arguments - array of int, and len. Prints elements
+   of the array.
+
+   %e: Consumes a pp_element * argument.
+
+   Arguments can be used sequentially, or through %N$ resp. *N$
+   notation Nth argument after the format string.  If %N$ / *N$
+   notation is used, it must be used for all arguments, except %m, %%,
+   %<, %>, %} and %', which may not have a number, as they do not consume
+   an argument.  When %M$.*N$s is used, M must be N + 1.  (This may
+   also be written %M$.*s, provided N is not otherwise used.)  The
+   format string must have conversion specifiers with argument numbers
+   1 up to highest argument; each argument may only be used once.
+   A format string can have at most 30 arguments.  */
+
+
diff --git a/gcc/doc/libdiagnostics/topics/metadata.rst 
b/gcc/doc/libdiagnostics/topics/metadata.rst
new file mode 100644
index 000000000000..c62792a5b763
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/metadata.rst
@@ -0,0 +1,149 @@
+.. Copyright (C) 2024 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
+
+Adding metadata
+===============
+
+Tool metadata
+*************
+
+It's possible to set up various metadata on the :type:`diagnostic_manager`
+as a whole, describing the program creating the diagnostics.
+
+.. note::
+
+   It's not required to set up any of this up on a
+   :type:`diagnostic_manager`.  However, if you are doing
+   :doc:`SARIF output <sarif>`, then you need to at least call
+   :func:`diagnostic_manager_set_tool_name` or the generated ``.sarif``
+   file will not validate against the schema.
+
+.. function:: void diagnostic_manager_set_tool_name (diagnostic_manager 
*diag_mgr, \
+                                                     const char *value)
+
+   Set a string for the name of the tool emitting the diagnostics.
+
+   Both parameters must be non-NULL.
+
+   If set, this string will be used
+
+   * by :doc:`text output sinks <text-output>` as a prefix for output
+     when no physical location is available, replacing ``progname``
+     in the following:
+
+     .. code-block:: console
+
+       $ ./tut01-hello-world
+       progname: error: I'm sorry Dave, I'm afraid I can't do that
+
+   * by :doc:`SARIF output sinks <sarif>` as the value for the
+     ``name`` property of the ``driver``
+     (`SARIF v2.1.0 §3.19.8 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790791>`_).
+
+.. function:: void diagnostic_manager_set_full_name (diagnostic_manager 
*diag_mgr, \
+                                                      const char *value)
+
+   Set a string giving the name of the tool along with the its version and
+   other useful information::
+
+     diagnostic_manager_set_full_name (diag_mgr, "FooChecker 0.1 (en_US)");
+
+   If set, this string will be used by :doc:`SARIF output sinks <sarif>` as
+   the value for the ``fullName`` property of the ``driver``
+   (`SARIF v2.1.0 §3.19.9 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790792>`_).
+
+   Both parameters must be non-NULL.
+
+.. function:: void diagnostic_manager_set_version_string (diagnostic_manager 
*diag_mgr, \
+                                                          const char *value)
+
+   Set a string suitable for use as the value of the SARIF ``version`` property
+   of the ``driver``.
+   (`SARIF v2.1.0 §3.19.13 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790796>`_)::
+
+     diagnostic_manager_set_version_string (diag_mgr, "0.1");
+
+   Both parameters must be non-NULL.
+
+.. function:: void diagnostic_manager_set_version_url (diagnostic_manager 
*diag_mgr, \
+                                                       const char *value)
+
+   Set a string suitable for use as the value of the SARIF ``informationUri``
+   property of the ``driver``.
+   (`SARIF v2.1.0 §3.19.17 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790800>`_)::
+
+     diagnostic_manager_set_version_url (diag_mgr,
+                                         
"https://www.example.com/foo-checker/releases/0.1/";);
+
+  Both parameters must be non-NULL.
+
+Adding metadata to a diagnostic
+*******************************
+
+.. function:: void diagnostic_set_cwe (diagnostic *diag, \
+                                       unsigned cwe_id)
+
+   Associate ``diag`` with the given ID within
+   the `Common Weakness Enumeration <https://cwe.mitre.org/>`_::
+
+     /* CWE-242: Use of Inherently Dangerous Function.  */
+     diagnostic_set_cwe (d, 242);
+
+   ``diag`` must be non-NULL.
+
+   The CWE value will be printed by text sinks after the message::
+
+     test-metadata.c:21:3: warning: never use 'gets' [CWE-242]
+
+   and in a sufficiently-capable terminal will be a link to
+   documentation about the CWE.
+
+.. function:: void diagnostic_add_rule (diagnostic *diag, \
+                                        const char *title, \
+                                        const char *url)
+
+   Associate this :type:`diagnostic` with a particular rule that has been
+   violated (such as in a coding standard, or within a specification).
+
+   A diagnostic can be associated with zero or more rules.
+
+   ``diag`` must be non-NULL.  The rule must have at least one of a
+   title and a URL, but these can be NULL.
+
+   For example, given::
+
+     diagnostic_add_rule (d,
+                          "MSC24-C",
+                          
"https://wiki.sei.cmu.edu/confluence/display/c/MSC24-C.+Do+not+use+deprecated+or+obsolescent+functions";);
+
+   the rule name will be printed by text sinks after the message::
+
+     test-metadata.c:21:3: warning: never use 'gets' [MSC24-C]
+      21 |   gets (buf);
+         |   ^~~~~~~~~~
+
+   and if so, the URL will be available in a sufficiently capable
+   terminal.
+
+   This can be used in conjunction with :func:`diagnostic_set_cwe`,
+   giving output like this::
+
+     test-metadata.c:21:3: warning: never use 'gets' [CWE-242] [MSC24-C]
+      21 |   gets (buf);
+         |   ^~~~~~~~~~
diff --git a/gcc/doc/libdiagnostics/topics/physical-locations.rst 
b/gcc/doc/libdiagnostics/topics/physical-locations.rst
new file mode 100644
index 000000000000..bad2b8db27fd
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/physical-locations.rst
@@ -0,0 +1,281 @@
+.. Copyright (C) 2024 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
+
+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 <logical-locations>`,
+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:: const 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>`
+   (`SARIF v2.1.0 §3.24.10 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790871>`_).
+   See
+   `SARIF v2.1.0 Appendix J 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141791197>`_
+   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.  libdiagnostics 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.  libdiagnostics treats the first column of a text line
+as column 1, **not** column 0.
+
+.. note::
+
+   Both libdiagnostics and Emacs number source *lines* starting at 1, but
+   they have differing conventions for *columns*.
+
+   libdiagnostics 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 libdiagnostics 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 libdiagnostics 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
+
+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/libdiagnostics.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 *
diff --git a/gcc/doc/libdiagnostics/topics/retrofitting.rst 
b/gcc/doc/libdiagnostics/topics/retrofitting.rst
new file mode 100644
index 000000000000..d0340570733e
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/retrofitting.rst
@@ -0,0 +1,23 @@
+.. Copyright (C) 2024 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
+
+Adding libdiagnostics to an existing project
+============================================
+
+TODO
diff --git a/gcc/doc/libdiagnostics/topics/sarif.rst 
b/gcc/doc/libdiagnostics/topics/sarif.rst
new file mode 100644
index 000000000000..3fd75ed21c64
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/sarif.rst
@@ -0,0 +1,51 @@
+.. Copyright (C) 2024 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
+
+SARIF support
+=============
+
+`SARIF <https://www.sarif.info/>`_ is a machine-readable format, originally
+designed for the output of static analysis tools, but which can be used
+for diagnostics in general.
+
+.. function:: void diagnostic_manager_add_sarif_sink (diagnostic_manager 
*diag_mgr, \
+                                                      FILE *dst_stream, \
+                                                      const diagnostic_file 
*main_input_file, \
+                                                      enum 
diagnostic_sarif_version version)
+
+   Add a new output sink to ``diag_mgr``, which writes SARIF of the given
+   version to ``dst_stream``.
+
+   The output is not written until ``diag_mgr`` is released.
+
+   ``dst_stream`` is borrowed, and must outlive ``diag_mgr``.
+
+   For the result to be a valid SARIF file according to the schema,
+   ``diag_mgr`` must have had :func:`diagnostic_manager_set_tool_name`
+   called on it.
+
+   ``diag_mgr``, ``dst_stream``, and ``main_input_file`` must all be non-NULL.
+
+  .. enum:: diagnostic_sarif_version
+
+     An enum for choosing the SARIF version for a SARIF output sink.
+
+     .. macro:: DIAGNOSTIC_SARIF_VERSION_2_1_0
+
+     .. macro:: DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE
diff --git a/gcc/doc/libdiagnostics/topics/text-output.rst 
b/gcc/doc/libdiagnostics/topics/text-output.rst
new file mode 100644
index 000000000000..32b2a54f18a3
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/text-output.rst
@@ -0,0 +1,87 @@
+.. Copyright (C) 2024 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
+
+Text output
+===========
+
+.. type:: diagnostic_text_sink
+
+.. function:: diagnostic_text_sink * diagnostic_manager_add_text_sink 
(diagnostic_manager *diag_mgr,\
+                                                                       FILE 
*dst_stream, \
+                                                                       enum 
diagnostic_colorize colorize)
+
+   Add a new output sink to ``diag_mgr``, which writes GCC-style diagnostics
+   to ``dst_stream``.
+   Return a borrowed pointer to the sink, which is cleaned up when ``diag_mgr``
+   is released.
+
+   ``diag_mgr`` must be non-NULL.
+
+   ``dst_stream`` must be non-NULL.  It is borrowed and must outlive 
``DIAG_MGR``.
+
+   The output for each diagnostic is written and flushed as each
+   :type:`diagnostic` is finished.
+
+   .. enum:: diagnostic_colorize
+
+      An enum for determining if we should colorize a text output sink.
+
+      .. macro:: DIAGNOSTIC_COLORIZE_IF_TTY
+
+        Diagnostics should be colorized if the destination stream is
+        directly connected to a tty.
+
+      .. macro:: DIAGNOSTIC_COLORIZE_NO
+
+        Diagnostics should not be colorized.
+
+      .. macro:: DIAGNOSTIC_COLORIZE_YES
+
+        Diagnostics should be colorized.
+
+.. function:: void diagnostic_text_sink_set_source_printing_enabled 
(diagnostic_text_sink *text_sink, \
+                                                                     int value)
+
+   Enable or disable printing of source text in the text sink.
+
+   ``text_sink`` must be non-NULL.
+
+   Default: enabled.
+
+.. function:: void diagnostic_text_sink_set_colorize (diagnostic_text_sink 
*text_sink, \
+                                                      enum diagnostic_colorize 
colorize)
+
+   Update colorization of text sink.
+
+   ``text_sink`` must be non-NULL.
+
+.. function:: void 
diagnostic_text_sink_set_labelled_source_colorization_enabled 
(diagnostic_text_sink *text_sink, \
+                                                                               
   int value)
+
+   ``text_sink`` must be non-NULL.
+
+   Enable or disable colorization of the characters of source text
+   that are underlined.
+
+   This should be true for clients that generate range information
+   (so that the ranges of code are colorized), and false for clients that
+   merely specify points within the source code (to avoid e.g. colorizing
+   just the first character in a token, which would look strange).
+
+   Default: enabled.
diff --git a/gcc/doc/libdiagnostics/topics/ux.rst 
b/gcc/doc/libdiagnostics/topics/ux.rst
new file mode 100644
index 000000000000..fc96e176eb67
--- /dev/null
+++ b/gcc/doc/libdiagnostics/topics/ux.rst
@@ -0,0 +1,26 @@
+.. Copyright (C) 2024 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
+
+User Experience
+===============
+
+Refer to
+`GCC's user experience guidelines 
<https://gcc.gnu.org/onlinedocs/gccint/User-Experience-Guidelines.html>`_
+for notes on
+`what makes a good diagnostic 
<https://gcc.gnu.org/onlinedocs/gccint/Guidelines-for-Diagnostics.html>`_.
diff --git a/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst 
b/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst
new file mode 100644
index 000000000000..4635687fabd5
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst
@@ -0,0 +1,173 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 1: "Hello world"
+==============================
+
+Before we look at the details of the API, let's look at building and
+running programs that use the library.
+
+Here's a toy program that uses libdiagnostics to emit an error message
+to stderr.
+
+  .. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-example-1.c
+     :language: c
+     :start-after: /* begin quoted source */
+     :end-before:  /* end quoted source */
+
+Copy the above to `tut01-hello-world.c`.
+
+Assuming you have libdiagnostics installed, build the test program
+using:
+
+.. code-block:: console
+
+  $ gcc \
+      tut01-hello-world.c \
+      -o tut01-hello-world \
+      -ldiagnostics
+
+You should then be able to run the built program:
+
+.. code-block:: console
+
+  $ ./tut01-hello-world
+  progname: error: I'm sorry Dave, I'm afraid I can't do that
+
+If stderr is connected to a terminal, you should get colorized output
+(using `SGR control codes <https://en.wikipedia.org/wiki/ANSI_escape_code>`_).
+
+.. image:: example-1.png
+
+Otherwise, the output will be plain text.
+
+Obviously a trivial example like the above could be done using ``fprintf``
+on stderr, and it's fairly easy to colorize text at the terminal.
+
+In :doc:`the next part of the tutorial <02-physical-locations>` we'll add
+file/location information to our error messages, and libdiagnostics will
+quote the pertinent parts of the file, underlining them, which is less trivial
+to reimplement.  libdiagnostics gives us many other such abilities, such as
+fix-it hints and execution paths, which we'll cover in the following
+tutorials.  Also, once a program's diagnostics are using libdiagnostics,
+it is trivial to add support for outputting them in
+machine-readable form as :doc:`SARIF <../topics/sarif>`.
+
+
+Structure
+*********
+
+The above example shows the typical structure of a program using
+libdiagnostics:
+
+* **initialization**: create a :type:`diagnostic_manager` instance,
+  and create an output sink for it, and other one-time initialization
+
+* **emission**: create various :type:`diagnostic` instances, populating
+  them with data, and calling "finish" once they're ready to be emitted.
+  :doc:`Text sinks <../topics/text-output>` emit their diagnostics as soon
+  as "finish" is called on them.
+
+* **cleanup**: call :func:`diagnostic_manager_release` on the
+  :type:`diagnostic_manager` to finish and free up resources.
+  :doc:`SARIF sinks <../topics/sarif>` write their output when
+  :func:`diagnostic_manager_release` is called on the manager.
+
+For non-trivial examples we'll also want to create location information,
+which could happen during initialization, or during a parsing phase of
+the program using libdiagnostics.  See :doc:`02-physical-locations` for
+more information.
+
+
+Formatted messages
+******************
+
+The above example uses :func:`diagnostic_finish`, which takes a format
+string and arguments.  libdiagnostics has its own style of format
+string arguments used for :func:`diagnostic_finish` and some other
+entrypoints.
+
+.. note:: The format syntax is *not* the same as ``printf``; see
+   :doc:`supported formatting options <../topics/message-formatting>`.
+
+You can use the ``q`` modifier on arguments
+to quote them, so, for example ``%qs`` is a quoted string, consuming a
+``const char *`` argument::
+
+   diagnostic_finish (d, "can't find %qs", "foo");
+
+This gives output like this:
+
+.. code-block:: console
+
+  progname: error: can't find ‘foo’
+
+where the quoted string will appear in bold in a suitably-capable
+terminal, and the quotes will be internationalized, so that e.g. with
+``LANG=fr_FR.UTF8`` we might get:
+
+.. code-block:: console
+
+  progname: erreur: can't find « free »
+
+Note that:
+
+* the string ``error`` has been localized by libdiagnostics to
+  ``erreur``,
+
+* locale-specific quoting has been used (``«`` and ``»`` rather than
+  ``‘`` and ``’``),
+
+* ``foo`` hasn't been localized - you would typically use quoted strings
+  for referring to identifiers in the input language (such as function names
+  in code, property names in JSON, etc),
+
+* the message itself hasn't been localized: you are responsible for
+  passing a translated format string to :func:`diagnostic_finish` if you
+  want to internationalize the output.
+
+There are many :doc:`supported formatting options 
<../topics/message-formatting>`.
+
+
+Naming the program
+******************
+
+In the above output the message was preceded with ``progname``.  This
+appears for diagnostics that don't have any location information associated
+with them.  We'll look at setting up location information in the
+:doc:`next tutorial <02-physical-locations>`, but we can override this
+default name via :func:`diagnostic_manager_set_tool_name`::
+
+   diagnostic_manager_set_tool_name (diag_mgr, "my-awesome-checker");
+
+leading to output like this::
+
+   my-awesome-checker: error: can't find ‘foo’
+
+There are various other functions for
+:doc:`supplying metadata to libdiagnostics <../../topics/metadata>`.
+
+
+Moving beyond trivial examples
+******************************
+
+Obviously it's not very useful if we can't refer to specific files and
+specific locations in those files in our diagnostics, so read
+:doc:`part 2 of the tutorial <02-physical-locations>` for information on
+how to do this.
diff --git a/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst 
b/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst
new file mode 100644
index 000000000000..2e429de17349
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst
@@ -0,0 +1,260 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 2: physical locations
+===================================
+
+libdiagnostics has two kinds of location:
+
+* *physical locations* expressed in terms of a specific file, and line(s)
+  and perhaps column(s), such as ``some-file.c:3:1``, or a range of
+  columns, such as in::
+
+    test-typo.c:19:13: error: unknown field 'colour'
+       19 |   return p->colour;
+          |             ^~~~~~
+
+  or even a range spanning multiple lines of a file.
+
+  All of these are instances of :type:`diagnostic_physical_location`.
+
+* *logical locations* which refers to semantic constructs
+  in the input, such as ``within function 'foo'``, or within
+  namespace ``foo``'s class ``bar``'s member function ``get_color``.
+
+  These are instances of :type:`diagnostic_logical_location`,
+
+A :type:`diagnostic` can have zero or more physical locations,
+and optionally have a logical location.
+
+Let's extend the previous example to add a physical location to the
+:type:`diagnostic`; we'll cover logical locations in the
+:doc:`next section <03-logical-locations>`.
+
+
+Source files
+************
+
+Given these declarations::
+
+  static diagnostic_manager *diag_mgr;
+  static const diagnostic_file *main_file;
+
+we can create a :type:`diagnostic_file` describing an input file ``foo.c``
+via :func:`diagnostic_manager_new_file`::
+
+     foo_c = diagnostic_manager_new_file (diag_mgr,
+                                          "foo.c",
+                                          "c" /* source_language */);
+
+You can use :func:`diagnostic_manager_debug_dump_file` to print a
+representation of a :type:`diagnostic_file` for debugging.
+For example::
+
+   diagnostic_manager_debug_dump_file (diag_mgr, foo_c, stderr);
+
+might lead to this output on ``stderr``::
+
+   file(name="foo.c", sarif_source_language="c")
+
+Once we have a :type:`diagnostic_file` we can use it to create instances
+of :type:`diagnostic_physical_location` within the :type:`diagnostic_manager`.
+These are owned by the :type:`diagnostic_manager` and cleaned up
+automatically when :func:`diagnostic_manager_release` is called.
+
+Instances of :type:`diagnostic_physical_location` can refer to
+
+* a source line as a whole, created via
+  :func:`diagnostic_manager_new_location_from_file_and_line`.
+
+* a particular point within a source file (line/column), created via
+  :func:`diagnostic_manager_new_location_from_file_line_column`.
+
+* a range of text within of source file, created via
+  :func:`diagnostic_manager_new_location_from_range`.
+
+
+Diagnostics affecting a whole source line
+*****************************************
+
+If we want a diagnostic to refer to an entire source line,
+we can use :func:`diagnostic_manager_new_location_from_file_and_line`.
+
+For example, given this example input where the tool can't find the header::
+
+   #include <foo.h>
+
+we could complain about it via libdiagnostics via:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-no-column.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+leading to output like this::
+
+   foo.c:17: error: can't find 'foo.h'"
+      17 | #include <foo.h>
+
+where libdiagnostics will attempt to load the source file and
+quote the pertinent line.
+
+If libdiagnostics cannot open the file, it will merely print::
+
+   foo.c:17: error: can't find 'foo.h'
+
+You can use :func:`diagnostic_manager_debug_dump_location` to dump a
+:type:`diagnostic_physical_location`.  For the above example::
+
+   diagnostic_manager_debug_dump_location (diag_mgr, loc, stderr);
+
+might print::
+
+   foo.c:17
+
+to stderr.
+
+
+Columns and ranges
+******************
+
+If we want to generate output like this::
+
+   foo.c:17:11: error: can't find 'foo'"
+      17 | #include <foo.h>
+         |           ^~~~~
+
+where the diagnostic is marked as relating to the above range of
+characters in line 17, we need to express the range of characters
+within the line of interest.
+
+We can do this by creating a :type:`diagnostic_physical_location` for the
+start of the range, another one for the end of the range, and then using
+these two to create a :type:`diagnostic_physical_location` for the
+range as a whole:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-error.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+On compiling and running the program, we should get this output::
+
+   foo.c:17:11: error: can't find 'foo.h'
+      17 | #include <foo.h>
+         |           ^~~~~
+
+where libdiagnostics will attempt to load the source file and
+underling the pertinent part of the given line.
+
+If libdiagnostics cannot open the file, it will merely print::
+
+   foo.c:17:8: error: can't find 'foo'
+
+A range can span multiple lines within the same file.
+
+As before, you can use :func:`diagnostic_manager_debug_dump_location` to
+dump the locations.  For the above example::
+
+   diagnostic_manager_debug_dump_location (diag_mgr, loc_start, stderr);
+
+and::
+
+   diagnostic_manager_debug_dump_location (diag_mgr, loc_range, stderr);
+
+might print::
+
+   foo.c:17:11
+
+to stderr, whereas::
+
+   diagnostic_manager_debug_dump_location (diag_mgr, loc_end, stderr);
+
+might print::
+
+   foo.c:17:15
+
+
+Multiple locations
+******************
+
+As well as the primary physical location seen above, a :type:`diagnostic`
+can have additional physical locations.  You can add these secondary
+locations via :func:`diagnostic_add_location`.
+
+For example, for this valid but suspicious-looking C code::
+
+   const char *strs[3] = {"foo",
+                          "bar"
+                          "baz"};
+
+the following :type:`diagnostic` has its primary location where the missing
+comma should be, and secondary locations for each of the string literals
+``"foo"``, ``"bar"``, and ``"baz"``, added via :func:`diagnostic_add_location`:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-multiple-lines.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+where the text output might be::
+
+   test-multiple-lines.c:23:29: warning: missing comma
+      22 | const char *strs[3] = {"foo",
+         |                        ~~~~~
+      23 |                        "bar"
+         |                        ~~~~~^
+      24 |                        "baz"};
+         |                        ~~~~~
+
+
+Labelling locations
+*******************
+
+You can give the locations labels using
+:func:`diagnostic_set_location_with_label` and
+:func:`diagnostic_add_location_with_label`.
+
+Consider emitting a "type mismatch" diagnostic for::
+
+  42 + "foo"
+
+where the primary location is on the ``+``, with secondary locations on 
the``42``
+and the ``"foo"``:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-labelled-ranges.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+giving this text output::
+
+   test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char 
*'
+      19 |   42 + "foo"
+         |   ~~ ^ ~~~~~
+         |   |    |
+         |   int  const char *
+
+
+More on locations
+*****************
+
+For more details on the above, see :doc:`../topics/physical-locations`.
+Otherwise the :doc:`next part of the tutorial <03-logical-locations>`
+covers logical locations.
diff --git a/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst 
b/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst
new file mode 100644
index 000000000000..d36ac098a809
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst
@@ -0,0 +1,60 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 3: logical locations
+==================================
+
+Let's extend the previous example to add a
+:doc:`logical location <../topics/logical-locations>` to the
+:type:`diagnostic`.
+
+First we create a :type:`diagnostic_logical_location` representing a
+particular function::
+
+  const diagnostic_logical_location *logical_loc
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
+                                              NULL, /* parent */
+                                              "foo",
+                                              NULL,
+                                              NULL);
+
+In this simple example we specify that it is a function, and just give
+it a name (``foo``).  For more complicated cases we can set up tree-like
+hierarchies of logical locations, set qualified names, "mangled" names,
+and so on; see :func:`diagnostic_manager_new_logical_location` for details.
+    
+Once we have :type:`diagnostic_logical_location` we can associate it with
+a :type:`diagnostic` with :func:`diagnostic_set_logical_location`::
+      
+  diagnostic_set_logical_location (d, logical_loc);
+
+The logical location will be printed by text output sinks like this::
+
+  In function 'foo':
+
+and will be captured in :doc:`SARIF <../topics/sarif>` output.
+
+
+Find out more
+*************
+
+For more details on the above, see :doc:`../topics/logical-locations`.
+Otherwise the :doc:`next part of the tutorial <04-notes>` covers adding
+supplementary "notes" to a :type:`diagnostic`.
diff --git a/gcc/doc/libdiagnostics/tutorial/04-notes.rst 
b/gcc/doc/libdiagnostics/tutorial/04-notes.rst
new file mode 100644
index 000000000000..117eb6fa7ad8
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/04-notes.rst
@@ -0,0 +1,66 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 4: adding notes
+=============================
+
+Let's further extend the previous example to add a "note" to it.
+
+We want to generate output like this::
+
+   test-with-note.c:17:11: error: can't find 'foo'
+   17 | #include <foo.h>
+      |           ^~~~~
+   test-with-note.c:17:11: note: have you looked behind the couch?
+
+The "error" and "note" are both instances of :type:`diagnostic`.
+We want to let libdiagnostics know that they are grouped together.
+The way to do this is to use :func:`diagnostic_manager_begin_group`
+and :func:`diagnostic_manager_end_group` around the "finish" calls
+to the diagnostics.
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-error-with-note.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+On compiling and running the program, we should get the desired output::
+
+   test-with-note.c:17:11: error: can't find 'foo'
+   17 | #include <foo.h>
+      |           ^~~~~
+   test-with-note.c:17:11: note: have you looked behind the couch?
+
+The grouping doesn't affect text output sinks, but a
+:doc:`SARIF sink <../topics/sarif>` will group the note within the error
+(via the ``relatedLocations`` property of ``result`` objects; see SARIF v2.1.0
+`§3.27.22 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790910>`_).
+
+In the above, the note had the same physical location as the error
+(``loc_range``).  This can be useful for splitting up a message into two
+parts to make localization easier, but they could have different locations, 
such
+as in::
+
+  test.xml:10:2: error: 'foo' is not valid here
+  test.xml:5:1: note: within element 'bar'
+
+where each :type:`diagnostic` had its own :type:`diagnostic_physical_location`.
+
+In :doc:`the next tutorial <05-warnings>` we'll look at issuing warnings,
+rather than errors.
diff --git a/gcc/doc/libdiagnostics/tutorial/05-warnings.rst 
b/gcc/doc/libdiagnostics/tutorial/05-warnings.rst
new file mode 100644
index 000000000000..1512ae78f987
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/05-warnings.rst
@@ -0,0 +1,44 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 5: warnings
+=========================
+
+So far we've only emitted errors, but other kinds of diagnostic are possible,
+such as warnings.
+
+We can select different kinds of diagnostic via :enum:`diagnostic_level`
+when calling :func:`diagnostic_begin`:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+On compiling and running the program, we should get output similar to::
+
+   test-warning.c:17:11: warning: this is a warning
+   17 | #include <foo.h>
+      |           ^~~~~
+
+Various severities are possible, see  :enum:`diagnostic_level` for more
+information.
+
+In :doc:`the next section of the tutorial <06-fix-it-hints>` we'll look
+at adding fix-it hints to diagnostics.
diff --git a/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst 
b/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst
new file mode 100644
index 000000000000..9486ab7f91c1
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst
@@ -0,0 +1,61 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 6: fix-it hints
+=============================
+
+libdiagnostics supports adding "fix-it hints" to a :type:`diagnostic`:
+suggestions for the user on how to edit their code to fix a problem.  These
+can be expressed as insertions, replacements, and removals of text.
+
+For example, here we add a replacement fix-it hint to a diagnostic:
+
+.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-fix-it-hint.c
+   :language: c
+   :start-after: /* begin quoted source */
+   :end-before:  /* end quoted source */
+
+On compiling and running the program, we should get output similar to::
+
+   test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 
'color'
+      19 |   return p->colour;
+         |             ^~~~~~
+         |             color
+
+We can also add a call to :func:`diagnostic_manager_write_patch` to the
+program cleanup code::
+
+  diagnostic_manager_write_patch (diag_mgr, stderr);
+
+This will write a patch to the stream (here ``stderr``) giving the effect
+of all fix-it hints on all diagnostics emitted by the
+:type:`diagnostic_manager`, giving something like::
+
+   @@ -16,7 +16,7 @@
+    struct rgb
+    get_color (struct object *p)
+    {
+   -  return p->colour;
+   +  return p->color;
+    }
+    
+
+See the :doc:`guide to fix-it hints <../topics/fix-it-hints>`
+for more information, or go on to
+:doc:`the next section of the tutorial <07-execution-paths>`.
diff --git a/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst 
b/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst
new file mode 100644
index 000000000000..0fbbed25d3a5
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst
@@ -0,0 +1,141 @@
+.. Copyright (C) 2024 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
+
+Tutorial part 7: execution paths
+================================
+
+A :type:`diagnostic` can optionally have a :type:`diagnostic_execution_path`
+describing a path of execution through code.
+
+For example, let's pretend we're writing a static analyis tool for finding
+bugs in `CPython extension code <https://docs.python.org/3/c-api/index.html>`_.
+
+Let's say we're analyzing this code:
+
+.. literalinclude:: 
../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
+   :language: c
+   :start-after: begin fake source
+   :end-before:  end fake source
+
+This code attempts to take an Python integer parameter and then build a
+list of that length, containing random integers.  However, there are
+**numerous bugs** in this code: a type mismatch, mistakes in
+reference-counting, and an almost total lack of error-handling.
+
+For example, ``PyList_Append`` requires a non-NULL first parameter (``list``),
+but ``PyList_New`` can fail, returning NULL, and this isn't checked for,
+which would lead to a segfault if ``PyList_New`` fails.
+
+We can add a :type:`diagnostic_execution_path` to the :type:`diagnostic`
+via :func:`diagnostic_add_execution_path`, and then add events to it
+using :func:`diagnostic_execution_path_add_event`.
+
+For example, with::
+
+  diagnostic_event_id alloc_event_id
+    = diagnostic_execution_path_add_event (path,
+                                          loc_call_to_PyList_New,
+                                          logical_loc, 0,
+                                          "when %qs fails, returning NULL",
+                                          "PyList_New");
+
+we create an event that will be worded as::
+
+  (1) when `PyList_New' fails, returning NULL
+
+Note that :func:`diagnostic_execution_path_add_event` returns a
+:type:`diagnostic_event_id`.  We can use this to refer to this event
+in another event using the ``%@`` format code in its message, which
+takes the address of a :type:`diagnostic_event_id`::
+
+  diagnostic_execution_path_add_event (path,
+                                      loc_call_to_PyList_Append,
+                                      logical_loc, 0,
+                                      "when calling %qs, passing NULL from %@ 
as argument %i",
+                                      "PyList_Append", &alloc_event_id, 1);
+
+where the latter event will be worded as::
+
+  (2) when calling `PyList_Append', passing NULL from (1) as argument 1
+
+where the ``%@`` reference to the other event has been printed as ``(1)``.
+In SARIF output the text "(1)" will have a embedded link referring within the 
sarif
+log to the ``threadFlowLocation`` object for the other event, via JSON
+pointer (see `§3.10.3 "URIs that use the sarif scheme" 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790707>`_).
+
+Let's add an event between these describing control flow, creating three
+events in all:
+
+.. literalinclude:: 
../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
+   :language: c
+   :start-after: begin path creation
+   :end-before:  end path creation
+
+Assuming we also gave it :type:`diagnostic_logical_location` with:
+
+.. literalinclude:: 
../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
+   :language: c
+   :start-after: begin create logical locs
+   :end-before:  end create logical locs
+
+and finish the :type:`diagnostic` with :func:`diagnostic_finish` like this::
+
+  diagnostic_finish (d,
+                    "passing NULL as argument %i to %qs"
+                    " which requires a non-NULL parameter",
+                    1, "PyList_Append");
+
+then we should get output to text sinks similar to the following::
+
+   In function 'make_a_list_of_random_ints_badly':
+   test-warning-with-path.c:30:5: warning: passing NULL as argument 1 to 
'PyList_Append' which requires a non-NULL parameter"
+      30 |     PyList_Append(list, item);
+         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
+   make_a_list_of_random_ints_badly': events 1-3
+      26 |   list = PyList_New(0);
+         |          ^~~~~~~~~~~~~
+         |          |
+         |          (1) when 'PyList_New' fails, returning NULL
+      27 | 
+      28 |   for (i = 0; i < count; i++) {
+         |               ~~~~~~~~~
+         |               |
+         |               (2) when 'i < count'
+      29 |     item = PyLong_FromLong(random());
+      30 |     PyList_Append(list, item);
+         |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+         |     |
+         |     (3) when calling 'PyList_Append', passing NULL from (1) as 
argument 1
+
+and for SARIF sinks the path will be added as a ``codeFlow`` object
+(see SARIF 2.1.0 `3.36 codeFlow object 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790990>`_).
+
+Here's the above example in full:
+
+.. literalinclude:: 
../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
+   :language: c
+   :start-after: begin full example
+   :end-before:  end full example
+
+
+Moving on
+*********
+
+That's the end of the tutorial.  For more information on libdiagnostics, see
+the :doc:`topic guide <../topics/index>`.
diff --git a/gcc/doc/libdiagnostics/tutorial/example-1.png 
b/gcc/doc/libdiagnostics/tutorial/example-1.png
new file mode 100644
index 
0000000000000000000000000000000000000000..d637103036bd3fd4562bef0d7e30b40662cda1c4
GIT binary patch
literal 5646
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
U%9h0_Fsp@Vsp+ehK6oDXe;Xbzy8r+H

literal 0
HcmV?d00001

diff --git a/gcc/doc/libdiagnostics/tutorial/index.rst 
b/gcc/doc/libdiagnostics/tutorial/index.rst
new file mode 100644
index 000000000000..6ea6866e2914
--- /dev/null
+++ b/gcc/doc/libdiagnostics/tutorial/index.rst
@@ -0,0 +1,32 @@
+.. Copyright (C) 2024 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/>.
+
+Tutorial
+========
+
+The following tutorial gives an overview of how to use libdiagnostics.
+
+.. toctree::
+   :maxdepth: 2
+
+   01-hello-world.rst
+   02-physical-locations.rst
+   03-logical-locations.rst
+   04-notes.rst
+   05-warnings.rst
+   06-fix-it-hints.rst
+   07-execution-paths.rst
-- 
2.26.3

Reply via email to