--- /dev/null
+/* RAII wrapper around obstack.
+ Copyright (C) 2024-2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC 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, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_AUTO_OBSTACK_H
+#define GCC_AUTO_OBSTACK_H
+
+/* RAII wrapper around obstack. */
+
+struct auto_obstack
+{
+ auto_obstack ()
+ {
+ obstack_init (&m_obstack);
+ }
+
+ ~auto_obstack ()
+ {
+ obstack_free (&m_obstack, NULL);
+ }
+
+ operator obstack & () { return m_obstack; }
+
+ void grow (const void *src, size_t length)
+ {
+ obstack_grow (&m_obstack, src, length);
+ }
+
+ void *object_base () const
+ {
+ return m_obstack.object_base;
+ }
+
+ size_t object_size () const
+ {
+ return obstack_object_size (&m_obstack);
+ }
+
+ obstack m_obstack;
+};
+
+#endif /* GCC_AUTO_OBSTACK_H */
else
m_description = nullptr;
}
+ void
+ set_description (std::string desc)
+ {
+ m_description = std::make_unique<std::string> (std::move (desc));
+ }
node *
get_node_by_id (const char *id) const
else
m_label = nullptr;
}
+ void
+ set_label (std::string label)
+ {
+ m_label = std::make_unique<std::string> (std::move (label));
+ }
size_t
get_num_children () const { return m_children.size (); }
``LIBGDIAGNOSTICS_ABI_3``
-------------------------
+
``LIBGDIAGNOSTICS_ABI_3`` covers the addition of these functions for
working with directed graphs:
* :func:`diagnostic_node_set_location`
* :func:`diagnostic_node_set_logical_location`
+
+.. _LIBGDIAGNOSTICS_ABI_4:
+
+``LIBGDIAGNOSTICS_ABI_4``
+-------------------------
+
+``LIBGDIAGNOSTICS_ABI_4`` covers the addition of these functions for
+working with :type:`diagnostic_message_buffer`.
+
+ * :func:`diagnostic_message_buffer_new`
+
+ * :func:`diagnostic_message_buffer_release`
+
+ * :func:`diagnostic_message_buffer_append_str`
+
+ * :func:`diagnostic_message_buffer_append_text`
+
+ * :func:`diagnostic_message_buffer_append_byte`
+
+ * :func:`diagnostic_message_buffer_append_printf`
+
+ * :func:`diagnostic_message_buffer_append_event_id`
+
+ * :func:`diagnostic_message_buffer_begin_url`
+
+ * :func:`diagnostic_message_buffer_end_url`
+
+ * :func:`diagnostic_message_buffer_begin_quote`
+
+ * :func:`diagnostic_message_buffer_end_quote`
+
+ * :func:`diagnostic_message_buffer_begin_color`
+
+ * :func:`diagnostic_message_buffer_end_color`
+
+ * :func:`diagnostic_message_buffer_dump`
+
+ * :func:`diagnostic_finish_via_msg_buf`
+
+ * :func:`diagnostic_add_location_with_label_via_msg_buf`
+
+ * :func:`diagnostic_execution_path_add_event_via_msg_buf`
All three parameters must be non-NULL.
+.. function:: void diagnostic_finish_via_msg_buf (diagnostic *diag, \
+ diagnostic_message_buffer *msg_buf)
+
+ This is equivalent to :func:`diagnostic_finish`, but using a message
+ buffer rather than a format string and variadic arguments.
+
+ ``diag`` and ``msg_buf`` must both be non-NULL.
+
+ Calling this function transfers ownership of ``msg_buf`` to the
+ diagnostic - do not call :func:`diagnostic_message_buffer_release` on
+ it.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
Diagnostic groups
*****************
Equivalent to :func:`diagnostic_execution_path_add_event`, but using a
:type:`va_list` rather than directly taking variadic arguments.
+.. function:: diagnostic_event_id diagnostic_execution_path_add_event_via_msg_buf (diagnostic_execution_path *path, \
+ const diagnostic_physical_location *physical_loc, \
+ const diagnostic_logical_location *logical_loc, \
+ unsigned stack_depth,
+ diagnostic_message_buffer *msg_buf)
+
+ This is equivalent to :func:`diagnostic_execution_path_add_event` but
+ using a message buffer rather than a format string and variadic
+ arguments.
+
+ ``path`` and ``msg_buf`` must both be non-NULL.
+
+ Calling this function transfers ownership of ``msg_buf`` to the
+ path - do not call :func:`diagnostic_message_buffer_release` on it.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
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>`_).
diagnostic-manager.rst
diagnostics.rst
message-formatting.rst
+ message-buffers.rst
physical-locations.rst
logical-locations.rst
metadata.rst
--- /dev/null
+.. Copyright (C) 2025 Free Software Foundation, Inc.
+ Originally contributed by David Malcolm <dmalcolm@redhat.com>
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <https://www.gnu.org/licenses/>.
+
+.. default-domain:: c
+
+Message buffers
+===============
+
+.. type:: diagnostic_message_buffer
+
+A :type:`diagnostic_message_buffer` is a buffer into which text can be
+accumulated, before being used:
+
+* as the message of a diagnostic, using :func:`diagnostic_finish_via_msg_buf`
+
+* as the text of a label for a :type:`diagnostic_physical_location` using
+ :func:`diagnostic_add_location_with_label_via_msg_buf`
+
+* as the text of an event within a :type:`diagnostic_execution_path` using
+ :func:`diagnostic_execution_path_add_event_via_msg_buf`
+
+This is to allow more flexible creation of messages than a "format string
+plus variadic arguments" API.
+
+.. function:: diagnostic_message_buffer * diagnostic_message_buffer_new (void)
+
+ This function creates a new :type:`diagnostic_message_buffer`.
+
+ The caller is responsible for cleaning it up, either by handing it off
+ to one of the API entrypoints that takes ownership of it (such as
+ :func:`diagnostic_finish_via_msg_buf`), or by calling
+ :func:`diagnostic_message_buffer_release` on it.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_release (diagnostic_message_buffer *msg_buf)
+
+ This function releases ``msg_buf``.
+
+ Typically you don't need to call this, but instead will pass the
+ buffer to one of the API entrypoints that takes over ownership of
+ it (such as :func:`diagnostic_finish_via_msg_buf`); calling it
+ after this would lead to a double-free bug, as you no longer "own"
+ the buffer.
+
+ ``msg_buf`` must be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_append_str (diagnostic_message_buffer *msg_buf, \
+ const char *p)
+
+ This function appends the null-terminated string ``p`` to the buffer.
+ The string is assumed to be UTF-8 encoded.
+
+ ``msg_buf`` and ``p`` must both be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_append_text (diagnostic_message_buffer *msg_buf, \
+ const char *p, \
+ size_t len)
+
+ This function appends ``len`` bytes from ``p`` to the buffer.
+ The bytes are assumed to be UTF-8 encoded.
+
+ ``msg_buf`` and ``p`` must both be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_append_byte (diagnostic_message_buffer *msg_buf,\
+ char ch)
+
+ This function appends ``ch`` to the buffer. This should be either
+ ASCII, or part of UTF-8 encoded text.
+
+ ``msg_buf`` must be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_append_printf (diagnostic_message_buffer *msg_buf, \
+ const char *fmt, ...)
+
+ This function appends a formatted string to the buffer, using the
+ formatting rules for ``printf``.
+
+ The string is assumed to be UTF-8 encoded.
+
+ ``msg_buf`` and ``fmt`` must both be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_append_event_id (diagnostic_message_buffer *msg_buf, \
+ diagnostic_event_id event_id)
+
+ This function appends a :type:`diagnostic_event_id` to the buffer.
+
+ ``msg_buf`` must be non-NULL.
+
+ For text output, the event will be printed in the form ``(1)``.
+
+ This is analogous to the
+ :doc:`%@ message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+Hyperlink support
+*****************
+
+.. function:: void diagnostic_message_buffer_begin_url (diagnostic_message_buffer *msg_buf, \
+ const char *url)
+
+ This function indicates the beginning of a run of text that should be
+ associated with the given URL. The run of text should be closed with
+ a matching call to :func:`diagnostic_message_buffer_end_url`.
+
+ ``msg_buf`` and ``url`` must both be non-NULL.
+
+ For text output in a suitably modern terminal, the run of text will
+ be emitted as a clickable hyperlink to the URL.
+
+ For SARIF sinks, the run of text will be emitted using SARIF's
+ embedded link syntax.
+
+ This is analogous to the
+ :doc:`%{ message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_end_url (diagnostic_message_buffer *msg_buf)
+
+ This function ends a run of text within the buffer started with
+ :func:`diagnostic_message_buffer_begin_url`.
+
+ ``msg_buf`` must be non-NULL.
+
+ This is analogous to the
+ :doc:`%} message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+Quoted text
+***********
+
+.. function:: void diagnostic_message_buffer_begin_quote (diagnostic_message_buffer *msg_buf)
+
+ This function indicates the beginning of a run of text that should be
+ printed in quotes. The run of text should be closed with
+ a matching call to :func:`diagnostic_message_buffer_end_quote`.
+
+ ``msg_buf`` must be non-NULL.
+
+ For text output in a suitably modern terminal, the run of text will
+ appear in bold.
+ be emitted as a clickable hyperlink to the URL.
+
+ For SARIF sinks, the run of text will be emitted using SARIF's
+ embedded link syntax.
+
+ This is analogous to the
+ ``%<``:doc:`message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_end_url (diagnostic_message_buffer *msg_buf)
+
+ This function ends a run of text within the buffer started with
+ :func:`diagnostic_message_buffer_begin_url`.
+
+ ``msg_buf`` must be non-NULL.
+
+ This is analogous to the
+ :doc:`%> message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+Color
+*****
+
+.. function:: void diagnostic_message_buffer_begin_color (diagnostic_message_buffer *msg_buf, \
+ const char *color)
+
+ This function indicates the beginning of a run of text that should be
+ colorized as the given color. The run of text should be closed with
+ a matching call to :func:`diagnostic_message_buffer_end_color`.
+
+ The precise set of available color names is currently undocumented.
+
+ ``msg_buf`` and ``color`` must both be non-NULL.
+
+ For text output in a suitable terminal, the run of text will
+ be colorized.
+
+ For SARIF sinks, the run of text will be emitted using SARIF's
+ embedded link syntax.
+
+ This is analogous to the
+ :doc:`%r message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+.. function:: void diagnostic_message_buffer_end_color (diagnostic_message_buffer *msg_buf)
+
+ This function ends a run of text within the buffer started with
+ :func:`diagnostic_message_buffer_begin_color`.
+
+ ``msg_buf`` must be non-NULL.
+
+ This is analogous to the
+ :doc:`%R message formatting code <message-formatting>`.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+Debugging a message buffer
+**************************
+
+.. function:: void diagnostic_message_buffer_dump (const diagnostic_message_buffer *msg_buf, \
+ FILE *outf)
+
+ This function writes a representation of the contents of ``msg_buf``
+ to ``outf``, for debugging.
+
+ ``msg_buf`` can be NULL or non-NULL.
+ ``outf`` must be non-NULL.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_4`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
Various libgdiagnostics entrypoints take a format string and
variadic arguments.
+.. note::
+
+ See also :type:`diagnostic_message_buffer`, which offers an
+ alternative way to build up messages.
+
The format strings take codes prefixed by ``%``, or ``%q`` to put
the result in quotes. For example::
| ~~ ^ ~~~~~
| | |
| int const char *
+
+.. function:: void diagnostic_add_location_with_label_via_msg_buf (diagnostic *diag, \
+ const diagnostic_physical_location *loc, \
+ diagnostic_message_buffer *msg_buf)
+
+ This is equivalent to :func:`diagnostic_add_location_with_label` but
+ using a message buffer rather than a text string.
+
+ ``diag`` and ``msg_buf`` must both be non-NULL.
+
+ Calling this function transfers ownership of ``msg_buf`` to the
+ diagnostic - do not call :func:`diagnostic_message_buffer_release` on
+ it.
+
+ This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`; you can
+ test for its presence using
+
+ .. code-block:: c
+
+ #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
:end-before: end full example
-Moving on
-*********
-
-That's the end of the tutorial. For more information on libgdiagnostics, see
-the :doc:`topic guide <../topics/index>`.
+See the :doc:`guide to execution paths <../topics/execution-paths>`
+for more information, or go on to
+:doc:`the next section of the tutorial <08-message-buffers>`.
--- /dev/null
+.. Copyright (C) 2025 Free Software Foundation, Inc.
+ Originally contributed by David Malcolm <dmalcolm@redhat.com>
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <https://www.gnu.org/licenses/>.
+
+.. default-domain:: c
+
+Tutorial part 8: message buffers
+================================
+
+In previous examples, we finished a diagnostic with a call to
+:func:`diagnostic_finish`, which takes a format string and arguments
+to determine the text message of the diagnostic.
+
+Sometimes this approach is inconvenient, such as where you might want to
+build up a message programatically from a series of components.
+Additionally, you might have existing code that uses ``fprintf``, whereas
+:func:`diagnostic_finish` has its
+:doc:`own formatting conventions <../topics/message-formatting>` which are
+:strong:`not` the same as printf.
+
+For this reason libgdiagnostics (from ``LIBGDIAGNOSTICS_ABI_3`` onwards)
+supports :type:`diagnostic_message_buffer`, which can be used to accumulate a
+message before using it.
+
+You create a :type:`diagnostic_message_buffer` using
+:func:`diagnostic_message_buffer_new`.
+
+There are various API entrypoints for accumulating text into the buffer.
+
+For example:
+
+.. literalinclude:: ../../../testsuite/libgdiagnostics.dg/test-message-buffer.c
+ :language: c
+ :start-after: /* begin quoted source */
+ :end-before: /* end quoted source */
+
+Running this will produce this text output::
+
+.. code-block:: console
+
+ $ ./test-message-buffer.c.exe
+ ./test-message-buffer.c.exe: error: this is a string; foo; int: 42 str: mostly harmless; this is a link 'this is quoted' highlight A highlight B (1).
+
+where in a suitably-capable terminal if a text sink is directly
+connected to a tty:
+
+* the ``this is a link`` will be a clickable hyperlink
+ (and the URL will be captured in SARIF output).
+
+* the quoted text will be in bold
+
+* the ``highlight A`` and ``highlight B`` text will be colorized
+
+* the event ID will be colorized (and will be a URL in SARIF output
+ if used within a :type:`diagnostic_execution_path`).
+
+
+Moving on
+*********
+
+That's the end of the tutorial. For more information on libgdiagnostics, see
+the :doc:`topic guide <../topics/index>`.
05-warnings.rst
06-fix-it-hints.rst
07-execution-paths.rst
+ 08-message-buffers.rst
class graph;
class node;
class edge;
+class message_buffer;
/* Wrapper around a borrowed diagnostic_text_sink *. */
const diagnostic_logical_location *m_inner;
};
+/* Wrapper around a diagnostic_message_buffer *, with ownership. */
+
+class message_buffer
+{
+public:
+ message_buffer () : m_inner (nullptr) {}
+ message_buffer (diagnostic_message_buffer *inner) : m_inner (inner) {}
+ ~message_buffer ()
+ {
+ if (m_inner)
+ diagnostic_message_buffer_release (m_inner);
+ }
+ message_buffer (const message_buffer &) = delete;
+ message_buffer (message_buffer &&other)
+ {
+ m_inner = other.m_inner;
+ other.m_inner = nullptr;
+ }
+ message_buffer& operator= (const message_buffer &) = delete;
+ message_buffer& operator= (message_buffer &&other)
+ {
+ if (m_inner)
+ diagnostic_message_buffer_release (m_inner);
+ m_inner = other.m_inner;
+ other.m_inner = nullptr;
+ return *this;
+ }
+
+ message_buffer&
+ operator+= (const char *str)
+ {
+ diagnostic_message_buffer_append_str (m_inner, str);
+ return *this;
+ }
+
+ message_buffer&
+ operator+= (char ch)
+ {
+ diagnostic_message_buffer_append_byte (m_inner, ch);
+ return *this;
+ }
+
+ message_buffer &
+ begin_url (const char *url)
+ {
+ diagnostic_message_buffer_begin_url (m_inner, url);
+ return *this;
+ }
+
+ message_buffer &
+ end_url ()
+ {
+ diagnostic_message_buffer_end_url (m_inner);
+ return *this;
+ }
+
+ diagnostic_message_buffer *m_inner;
+};
+
/* RAII class around a diagnostic_execution_path *. */
class execution_path
va_list *args)
LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 0);
+ diagnostic_event_id
+ add_event_via_msg_buf (physical_location physical_loc,
+ logical_location logical_loc,
+ unsigned stack_depth,
+ message_buffer &&msg_buf);
+
diagnostic_execution_path *m_inner;
bool m_owned;
};
add_location_with_label (physical_location loc,
const char *text);
+ void
+ add_location_with_label (physical_location loc,
+ message_buffer &&text);
+
void
set_logical_location (logical_location loc);
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0);
+ void
+ finish_via_msg_buf (message_buffer &&msg_buf);
+
::diagnostic * const m_inner;
};
void
set_description (const char *);
+ void
+ set_description (message_buffer &&);
node
get_node_by_id (const char *id) const;
edge
add_edge (const char *id, node src_node, node dst_node, const char *label);
+ edge
+ add_edge (const char *id, node src_node, node dst_node, message_buffer &&label);
diagnostic_graph *m_inner;
bool m_owned;
void
set_label (const char *);
+ void
+ set_label (message_buffer &&);
void
set_location (physical_location loc);
args);
}
+inline diagnostic_event_id
+execution_path::add_event_via_msg_buf (physical_location physical_loc,
+ logical_location logical_loc,
+ unsigned stack_depth,
+ message_buffer &&msg_buf)
+{
+ diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner;
+ msg_buf.m_inner = nullptr;
+ return diagnostic_execution_path_add_event_via_msg_buf (m_inner,
+ physical_loc.m_inner,
+ logical_loc.m_inner,
+ stack_depth,
+ inner_msg_buf);
+}
+
// class group
inline
diagnostic_add_location_with_label (m_inner, loc.m_inner, text);
}
+inline void
+diagnostic::add_location_with_label (physical_location loc,
+ message_buffer &&msg_buf)
+{
+ diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner;
+ msg_buf.m_inner = nullptr;
+ diagnostic_add_location_with_label_via_msg_buf (m_inner,
+ loc.m_inner,
+ inner_msg_buf);
+}
+
inline void
diagnostic::add_location (physical_location loc)
{
diagnostic_finish_va (m_inner, fmt, args);
}
+inline void
+diagnostic::finish_via_msg_buf (message_buffer &&msg_buf)
+{
+ diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner;
+ msg_buf.m_inner = nullptr;
+ diagnostic_finish_via_msg_buf (m_inner, inner_msg_buf);
+}
+
// class manager
inline file
diagnostic_graph_set_description (m_inner, desc);
}
+inline void
+graph::set_description (message_buffer &&msg_buf)
+{
+ diagnostic_message_buffer *inner_msg_buf = msg_buf.m_inner;
+ msg_buf.m_inner = nullptr;
+ diagnostic_graph_set_description_via_msg_buf (m_inner, inner_msg_buf);
+}
+
inline node
graph::get_node_by_id (const char *id) const
{
label));
}
+inline edge
+graph::add_edge (const char *id,
+ node src_node, node dst_node,
+ message_buffer &&label)
+{
+ diagnostic_message_buffer *inner_label = label.m_inner;
+ label.m_inner = nullptr;
+ return edge (diagnostic_graph_add_edge_via_msg_buf (m_inner,
+ id,
+ src_node.m_inner,
+ dst_node.m_inner,
+ inner_label));
+}
+
// class node
inline void
diagnostic_node_set_label (m_inner, label);
}
+inline void
+node::set_label (message_buffer &&label)
+{
+ diagnostic_message_buffer *inner_label = label.m_inner;
+ label.m_inner = nullptr;
+ diagnostic_node_set_label_via_msg_buf (m_inner, inner_label);
+}
+
inline void
node::set_location (physical_location loc)
{
/* Entrypoints added in LIBGDIAGNOSTICS_ABI_3. */
-extern diagnostic_event_id
-private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path,
- const diagnostic_physical_location *physical_loc,
- const diagnostic_logical_location *logical_loc,
- unsigned stack_depth,
- diagnostic_graph *state_graph,
- const char *fmt, ...)
- LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
- LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
- LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
- LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5)
- LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (6)
- LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (6, 7);
-
extern void
private_diagnostic_graph_set_property_bag (diagnostic_graph &graph,
std::unique_ptr<json::object> properties);
private_diagnostic_edge_set_property_bag (diagnostic_edge &edge,
std::unique_ptr<json::object> properties);
+/* Entrypoint added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern diagnostic_event_id
+private_diagnostic_execution_path_add_event_3 (diagnostic_execution_path *path,
+ const diagnostic_physical_location *physical_loc,
+ const diagnostic_logical_location *logical_loc,
+ unsigned stack_depth,
+ diagnostic_graph *state_graph,
+ diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (6);
+
} // extern "C"
#endif /* LIBGDIAGNOSTICS_PRIVATE_H */
#include "edit-context.h"
#include "libgdiagnostics.h"
#include "libgdiagnostics-private.h"
+#include "pretty-print-format-impl.h"
+#include "pretty-print-markup.h"
+#include "auto-obstack.h"
class owned_nullable_string
{
diagnostic_source_printing_options m_source_printing;
};
+/* A token_printer that makes a deep copy of the pp_token_list
+ into another obstack. */
+
+class copying_token_printer : public token_printer
+{
+public:
+ copying_token_printer (obstack &dst_obstack,
+ pp_token_list &dst_token_list)
+ : m_dst_obstack (dst_obstack),
+ m_dst_token_list (dst_token_list)
+ {
+ }
+
+ void
+ print_tokens (pretty_printer *,
+ const pp_token_list &tokens) final override
+ {
+ for (auto iter = tokens.m_first; iter; iter = iter->m_next)
+ switch (iter->m_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case pp_token::kind::text:
+ {
+ const pp_token_text *sub = as_a <const pp_token_text *> (iter);
+ /* Copy the text, with null terminator. */
+ obstack_grow (&m_dst_obstack, sub->m_value.get (),
+ strlen (sub->m_value.get ()) + 1);
+ m_dst_token_list.push_back_text
+ (label_text::borrow (XOBFINISH (&m_dst_obstack,
+ const char *)));
+ }
+ break;
+
+ case pp_token::kind::begin_color:
+ {
+ pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
+ /* Copy the color, with null terminator. */
+ obstack_grow (&m_dst_obstack, sub->m_value.get (),
+ strlen (sub->m_value.get ()) + 1);
+ m_dst_token_list.push_back<pp_token_begin_color>
+ (label_text::borrow (XOBFINISH (&m_dst_obstack,
+ const char *)));
+ }
+ break;
+ case pp_token::kind::end_color:
+ m_dst_token_list.push_back<pp_token_end_color> ();
+ break;
+
+ case pp_token::kind::begin_quote:
+ m_dst_token_list.push_back<pp_token_begin_quote> ();
+ break;
+ case pp_token::kind::end_quote:
+ m_dst_token_list.push_back<pp_token_end_quote> ();
+ break;
+
+ case pp_token::kind::begin_url:
+ {
+ pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
+ /* Copy the URL, with null terminator. */
+ obstack_grow (&m_dst_obstack, sub->m_value.get (),
+ strlen (sub->m_value.get ()) + 1);
+ m_dst_token_list.push_back<pp_token_begin_url>
+ (label_text::borrow (XOBFINISH (&m_dst_obstack,
+ const char *)));
+ }
+ break;
+ case pp_token::kind::end_url:
+ m_dst_token_list.push_back<pp_token_end_url> ();
+ break;
+
+ case pp_token::kind::event_id:
+ {
+ pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
+ m_dst_token_list.push_back<pp_token_event_id> (sub->m_event_id);
+ }
+ break;
+
+ case pp_token::kind::custom_data:
+ /* These should have been eliminated by replace_custom_tokens. */
+ gcc_unreachable ();
+ break;
+ }
+ }
+
+private:
+ obstack &m_dst_obstack;
+ pp_token_list &m_dst_token_list;
+};
+
class sarif_sink : public sink
{
public:
const sarif_generation_options &sarif_gen_opts);
};
+struct diagnostic_message_buffer
+{
+ diagnostic_message_buffer ()
+ : m_tokens (m_obstack)
+ {
+ }
+
+ diagnostic_message_buffer (const char *gmsgid,
+ va_list *args)
+ : m_tokens (m_obstack)
+ {
+ text_info text (gmsgid, args, errno);
+ pretty_printer pp;
+ pp.set_output_stream (nullptr);
+ copying_token_printer tok_printer (m_obstack, m_tokens);
+ pp.set_token_printer (&tok_printer);
+ pp_format (&pp, &text);
+ pp_output_formatted_text (&pp, nullptr);
+ }
+
+
+ std::string to_string () const;
+
+ auto_obstack m_obstack;
+ pp_token_list m_tokens;
+};
+
+/* A pp_element subclass that replays the saved tokens in a
+ diagnostic_message_buffer. */
+
+class pp_element_message_buffer : public pp_element
+{
+public:
+ pp_element_message_buffer (diagnostic_message_buffer &msg_buf)
+ : m_msg_buf (msg_buf)
+ {
+ }
+
+ void add_to_phase_2 (pp_markup::context &ctxt) final override
+ {
+ /* Convert to text, possibly with colorization, URLs, etc. */
+ for (auto iter = m_msg_buf.m_tokens.m_first; iter; iter = iter->m_next)
+ switch (iter->m_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case pp_token::kind::text:
+ {
+ pp_token_text *sub = as_a <pp_token_text *> (iter);
+ pp_string (&ctxt.m_pp, sub->m_value.get ());
+ ctxt.push_back_any_text ();
+ }
+ break;
+
+ case pp_token::kind::begin_color:
+ {
+ pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
+ ctxt.begin_highlight_color (sub->m_value.get ());
+ }
+ break;
+ case pp_token::kind::end_color:
+ ctxt.end_highlight_color ();
+ break;
+
+ case pp_token::kind::begin_quote:
+ ctxt.begin_quote ();
+ break;
+ case pp_token::kind::end_quote:
+ ctxt.end_quote ();
+ break;
+
+ case pp_token::kind::begin_url:
+ {
+ pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
+ ctxt.begin_url (sub->m_value.get ());
+ }
+ break;
+ case pp_token::kind::end_url:
+ ctxt.end_url ();
+ break;
+
+ case pp_token::kind::event_id:
+ {
+ pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
+ gcc_assert (sub->m_event_id.known_p ());
+ ctxt.add_event_id (sub->m_event_id);
+ }
+ break;
+
+ case pp_token::kind::custom_data:
+ /* We don't have a way of handling custom_data tokens here. */
+ gcc_unreachable ();
+ break;
+ }
+ }
+
+private:
+ diagnostic_message_buffer &m_msg_buf;
+};
+
/* Helper for the linemap code. */
static size_t
m_sinks.push_back (std::move (sink));
}
- void emit (diagnostic &diag, const char *msgid, va_list *args)
+ void emit_va (diagnostic &diag, const char *msgid, va_list *args)
LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0);
+ void emit (diagnostic &diag, const char *msgid, ...)
+ LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 4);
+
+ void emit_msg_buf (diagnostic &diag,
+ diagnostic_message_buffer &msg_buf);
+
diagnostic_file *
new_file (const char *name,
const char *sarif_source_language)
diagnostic_graph (diagnostic_manager &) {}
diagnostic_node *
- add_node_with_id (std::string id,
+ add_node_with_id (std::string node_id,
diagnostic_node *parent_node);
diagnostic_edge *
- add_edge_with_label (const char *id,
+ add_edge_with_label (const char *edge_id,
diagnostic_node &src_node,
diagnostic_node &dst_node,
const char *label);
const diagnostic_logical_location *logical_loc,
unsigned stack_depth,
std::unique_ptr<diagnostic_graph> state_graph,
- const char *gmsgid,
- va_list *args)
+ std::unique_ptr<diagnostic_message_buffer> msg_buf)
: m_physical_loc (physical_loc),
m_logical_loc (logical_loc),
m_stack_depth (stack_depth),
- m_state_graph (std::move (state_graph))
+ m_state_graph (std::move (state_graph)),
+ m_msg_buf (std::move (msg_buf))
{
- m_desc_uncolored = make_desc (gmsgid, args, false);
- m_desc_colored = make_desc (gmsgid, args, true);
+ gcc_assert (m_msg_buf);
}
/* diagnostic_event vfunc implementations. */
void print_desc (pretty_printer &pp) const final override
{
- if (pp_show_color (&pp))
- pp_string (&pp, m_desc_colored.get ());
- else
- pp_string (&pp, m_desc_uncolored.get ());
+ if (m_msg_buf)
+ {
+ pp_element_message_buffer e_msg_buf (*m_msg_buf);
+ pp_printf (&pp, "%e", &e_msg_buf);
+ }
}
logical_location get_logical_location () const final override
const diagnostic_logical_location *m_logical_loc;
unsigned m_stack_depth;
std::unique_ptr<diagnostic_graph> m_state_graph;
- label_text m_desc_uncolored;
- label_text m_desc_colored;
+ std::unique_ptr<diagnostic_message_buffer> m_msg_buf;
};
class libgdiagnostics_path_thread : public diagnostic_thread
std::unique_ptr<diagnostic_graph> state_graph,
const char *gmsgid,
va_list *args)
+ {
+ auto msg_buf = std::make_unique<diagnostic_message_buffer> (gmsgid, args);
+
+ m_events.push_back
+ (std::make_unique<libgdiagnostics_path_event> (physical_loc,
+ logical_loc,
+ stack_depth,
+ std::move (state_graph),
+ std::move (msg_buf)));
+ return m_events.size () - 1;
+ }
+
+ diagnostic_event_id_t
+ add_event_via_msg_buf (const diagnostic_physical_location *physical_loc,
+ const diagnostic_logical_location *logical_loc,
+ unsigned stack_depth,
+ std::unique_ptr<diagnostic_graph> state_graph,
+ std::unique_ptr<diagnostic_message_buffer> msg_buf)
{
m_events.push_back
(std::make_unique<libgdiagnostics_path_event> (physical_loc,
logical_loc,
stack_depth,
std::move (state_graph),
- gmsgid,
- args));
+ std::move (msg_buf)));
return m_events.size () - 1;
}
m_labels.push_back (std::move (label));
}
+ void
+ add_location_with_label (const diagnostic_physical_location *loc,
+ std::unique_ptr<diagnostic_message_buffer> msg_buf)
+ {
+ std::string str = msg_buf->to_string ();
+ std::unique_ptr<range_label> label
+ = std::make_unique <impl_range_label> (str.c_str ());
+ m_rich_loc.add_range (as_location_t (loc),
+ SHOW_RANGE_WITHOUT_CARET,
+ label.get ());
+ m_labels.push_back (std::move (label));
+ }
+
void
set_logical_location (const diagnostic_logical_location *logical_loc)
{
mgr.get_dc ().add_sink (std::move (inner_sink));
}
+// struct diagnostic_message_buffer
+
+std::string
+diagnostic_message_buffer::to_string () const
+{
+ std::string result;
+
+ /* Convert to text, dropping colorization, URLs, etc. */
+ for (auto iter = m_tokens.m_first; iter; iter = iter->m_next)
+ switch (iter->m_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case pp_token::kind::text:
+ {
+ pp_token_text *sub = as_a <pp_token_text *> (iter);
+ result += sub->m_value.get ();
+ }
+ break;
+
+ case pp_token::kind::begin_color:
+ case pp_token::kind::end_color:
+ // Skip
+ break;
+
+ case pp_token::kind::begin_quote:
+ result += open_quote;
+ break;
+
+ case pp_token::kind::end_quote:
+ result += close_quote;
+ break;
+
+ case pp_token::kind::begin_url:
+ case pp_token::kind::end_url:
+ // Skip
+ break;
+
+ case pp_token::kind::event_id:
+ {
+ pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
+ gcc_assert (sub->m_event_id.known_p ());
+ result += '(';
+ result += std::to_string (sub->m_event_id.one_based ());
+ result += ')';
+ }
+ break;
+
+ case pp_token::kind::custom_data:
+ /* We don't have a way of handling custom_data tokens here. */
+ gcc_unreachable ();
+ break;
+ }
+
+ return result;
+}
+
/* struct diagnostic_manager. */
void
}
void
-diagnostic_manager::emit (diagnostic &diag, const char *msgid, va_list *args)
+diagnostic_manager::emit_va (diagnostic &diag, const char *msgid, va_list *args)
{
set_line_table_global ();
m_current_diag = nullptr;
}
+void
+diagnostic_manager::emit (diagnostic &diag, const char *msgid, ...)
+{
+ va_list args;
+ va_start (args, msgid);
+ emit_va (diag, msgid, &args);
+ va_end (args);
+}
+
+void
+diagnostic_manager::emit_msg_buf (diagnostic &diag,
+ diagnostic_message_buffer &msg_buf)
+{
+
+ pp_element_message_buffer e_msg_buf (msg_buf);
+ emit (diag, "%e", &e_msg_buf);
+}
+
diagnostic_execution_path *
diagnostic_manager::new_execution_path ()
{
/* Error-checking at the API boundary. */
#define FAIL_IF_NULL(PTR_ARG) \
- do { \
- volatile const void *p = (PTR_ARG); \
- if (!p) { \
- fprintf (stderr, "%s: %s must be non-NULL\n", \
+ do { \
+ volatile const void *ptr_arg = (PTR_ARG); \
+ if (!ptr_arg) { \
+ fprintf (stderr, "%s: %s must be non-NULL\n", \
__func__, #PTR_ARG); \
abort (); \
} \
else
progname = "progname";
auto_diagnostic_group d;
- diag->get_manager ().emit (*diag, gmsgid, args);
+ diag->get_manager ().emit_va (*diag, gmsgid, args);
delete diag;
}
mgr->get_dc ().set_main_input_filename (file->get_name ());
}
-// struct diagnostic_graph : public diagnostics::digraphs::graph<foo_traits>
+/* Public entrypoint. */
diagnostic_node *
-diagnostic_graph::add_node_with_id (std::string id,
+diagnostic_graph::add_node_with_id (std::string node_id,
diagnostic_node *parent_node)
{
- auto node_up = std::make_unique<diagnostic_node> (*this, std::move (id));
+ auto node_up
+ = std::make_unique<diagnostic_node> (*this, std::move (node_id));
diagnostic_node *new_node = node_up.get ();
if (parent_node)
parent_node->add_child (std::move (node_up));
return new_node;
}
+/* Public entrypoint. */
+
diagnostic_edge *
-diagnostic_graph::add_edge_with_label (const char *id,
+diagnostic_graph::add_edge_with_label (const char *edge_id,
diagnostic_node &src_node,
diagnostic_node &dst_node,
const char *label)
{
auto edge_up
- = std::make_unique<diagnostic_edge> (*this, id,
+ = std::make_unique<diagnostic_edge> (*this, edge_id,
src_node, dst_node);
diagnostic_edge *new_edge = edge_up.get ();
if (label)
graph->set_description (desc);
}
+/* Public entrypoint. */
+
diagnostic_node *
diagnostic_graph_add_node (diagnostic_graph *graph,
- const char *id,
+ const char *node_id,
diagnostic_node *parent_node)
{
FAIL_IF_NULL (graph);
- FAIL_IF_NULL (id);
+ FAIL_IF_NULL (node_id);
- return graph->add_node_with_id (id, parent_node);
+ return graph->add_node_with_id (node_id, parent_node);
}
/* Public entrypoint. */
diagnostic_edge *
diagnostic_graph_add_edge (diagnostic_graph *graph,
- const char *id,
+ const char *edge_id,
diagnostic_node *src_node,
diagnostic_node *dst_node,
const char *label)
FAIL_IF_NULL (src_node);
FAIL_IF_NULL (dst_node);
- return graph->add_edge_with_label (id, *src_node, *dst_node, label);
+ return graph->add_edge_with_label (edge_id, *src_node, *dst_node, label);
}
/* Public entrypoint. */
diagnostic_node *
diagnostic_graph_get_node_by_id (diagnostic_graph *graph,
- const char *id)
+ const char *node_id)
{
FAIL_IF_NULL (graph);
- FAIL_IF_NULL (id);
+ FAIL_IF_NULL (node_id);
- return static_cast<diagnostic_node *> (graph->get_node_by_id (id));
+ return static_cast<diagnostic_node *> (graph->get_node_by_id (node_id));
}
/* Public entrypoint. */
diagnostic_edge *
diagnostic_graph_get_edge_by_id (diagnostic_graph *graph,
- const char *id)
+ const char *edge_id)
{
FAIL_IF_NULL (graph);
- FAIL_IF_NULL (id);
+ FAIL_IF_NULL (edge_id);
- return static_cast<diagnostic_edge *> (graph->get_edge_by_id (id));
+ return static_cast<diagnostic_edge *> (graph->get_edge_by_id (edge_id));
}
/* Public entrypoint. */
node->set_label (label);
}
+/* Public entrypoint. */
+
void
diagnostic_node_set_logical_location (diagnostic_node *node,
const diagnostic_logical_location *logical_loc)
/* Private entrypoint. */
-diagnostic_event_id
-private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path,
- const diagnostic_physical_location *physical_loc,
- const diagnostic_logical_location *logical_loc,
- unsigned stack_depth,
- diagnostic_graph *state_graph,
- const char *gmsgid, ...)
-
-{
- FAIL_IF_NULL (path);
- FAIL_IF_NULL (gmsgid);
-
- va_list args;
- va_start (args, gmsgid);
- diagnostic_event_id_t result
- = path->add_event_va (physical_loc,
- logical_loc,
- stack_depth,
- std::unique_ptr <diagnostic_graph> (state_graph),
- gmsgid, &args);
- va_end (args);
-
- return as_diagnostic_event_id (result);
-
-}
-
-/* Private entrypoint. */
-
void
private_diagnostic_graph_set_property_bag (diagnostic_graph &graph,
std::unique_ptr<json::object> properties)
{
edge.set_property_bag (std::move (properties));
}
+
+/* Public entrypoint. */
+
+diagnostic_message_buffer *
+diagnostic_message_buffer_new ()
+{
+ return new diagnostic_message_buffer ();
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_release (diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (msg_buf);
+ delete msg_buf;
+}
+
+void
+diagnostic_message_buffer_append_str (diagnostic_message_buffer *msg_buf,
+ const char *p)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (p);
+ msg_buf->m_tokens.push_back_text (label_text::take (xstrdup (p)));
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_append_text (diagnostic_message_buffer *msg_buf,
+ const char *p,
+ size_t len)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (p);
+ msg_buf->m_tokens.push_back_text (label_text::take (xstrndup (p, len)));
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_append_byte (diagnostic_message_buffer *msg_buf,
+ char ch)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back_byte (ch);
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_append_printf (diagnostic_message_buffer *msg_buf,
+ const char *fmt, ...)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (fmt);
+
+ va_list args;
+ va_start (args, fmt);
+
+ char *formatted_buf = xvasprintf (fmt, args);
+
+ va_end (args);
+
+ msg_buf->m_tokens.push_back_text (label_text::take (formatted_buf));
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_append_event_id (diagnostic_message_buffer *msg_buf,
+ diagnostic_event_id event_id)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back<pp_token_event_id> (event_id);
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_begin_url (diagnostic_message_buffer *msg_buf,
+ const char *url)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (url);
+ msg_buf->m_tokens.push_back<pp_token_begin_url>
+ (label_text::take (xstrdup (url)));
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_end_url (diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back<pp_token_end_url> ();
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_begin_quote (diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back<pp_token_begin_quote> ();
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_end_quote (diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back<pp_token_end_quote> ();
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_begin_color (diagnostic_message_buffer *msg_buf,
+ const char *color)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (color);
+ msg_buf->m_tokens.push_back<pp_token_begin_color>
+ (label_text::take (xstrdup (color)));
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_end_color (diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (msg_buf);
+ msg_buf->m_tokens.push_back<pp_token_end_color> ();
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_message_buffer_dump (const diagnostic_message_buffer *msg_buf,
+ FILE *outf)
+{
+ FAIL_IF_NULL (msg_buf);
+ FAIL_IF_NULL (outf);
+
+ msg_buf->m_tokens.dump (outf);
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_finish_via_msg_buf (diagnostic *diag,
+ diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (diag);
+ FAIL_IF_NULL (msg_buf);
+
+ if (const char *tool_name
+ = diag->get_manager ().get_client_version_info ()->m_name.get_str ())
+ progname = tool_name;
+ else
+ progname = "progname";
+ auto_diagnostic_group d;
+ diag->get_manager ().emit_msg_buf (*diag, *msg_buf);
+ delete diag;
+ delete msg_buf;
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_add_location_with_label_via_msg_buf (diagnostic *diag,
+ const diagnostic_physical_location *loc,
+ diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (diag);
+ diag->get_manager ().assert_valid_diagnostic_physical_location (loc);
+ FAIL_IF_NULL (msg_buf);
+
+ std::unique_ptr<diagnostic_message_buffer> msg_buf_up (msg_buf);
+ diag->add_location_with_label (loc, std::move (msg_buf_up));
+}
+
+/* Public entrypoint. */
+
+diagnostic_event_id
+diagnostic_execution_path_add_event_via_msg_buf (diagnostic_execution_path *path,
+ const diagnostic_physical_location *physical_loc,
+ const diagnostic_logical_location *logical_loc,
+ unsigned stack_depth,
+ diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (path);
+ FAIL_IF_NULL (msg_buf);
+
+ std::unique_ptr<diagnostic_message_buffer> msg_buf_up (msg_buf);
+ diagnostic_event_id_t result
+ = path->add_event_via_msg_buf (physical_loc,
+ logical_loc,
+ stack_depth,
+ nullptr,
+ std::move (msg_buf_up));
+ return as_diagnostic_event_id (result);
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_graph_set_description_via_msg_buf (diagnostic_graph *graph,
+ diagnostic_message_buffer *desc)
+{
+ FAIL_IF_NULL (graph);
+
+ if (desc)
+ graph->set_description (desc->to_string ());
+ else
+ graph->set_description (nullptr);
+}
+
+/* Public entrypoint. */
+
+diagnostic_edge *
+diagnostic_graph_add_edge_via_msg_buf (diagnostic_graph *graph,
+ const char *edge_id,
+ diagnostic_node *src_node,
+ diagnostic_node *dst_node,
+ diagnostic_message_buffer *label)
+{
+ FAIL_IF_NULL (graph);
+ FAIL_IF_NULL (src_node);
+ FAIL_IF_NULL (dst_node);
+
+ if (label)
+ {
+ std::string label_str (label->to_string ());
+ return graph->add_edge_with_label (edge_id, *src_node, *dst_node,
+ label_str.c_str ());
+ }
+ else
+ return graph->add_edge_with_label (edge_id, *src_node, *dst_node,
+ nullptr);
+}
+
+/* Public entrypoint. */
+
+void
+diagnostic_node_set_label_via_msg_buf (diagnostic_node *node,
+ diagnostic_message_buffer *label)
+{
+ FAIL_IF_NULL (node);
+
+ if (label)
+ node->set_label (label->to_string ());
+ else
+ node->set_label (nullptr);
+}
+
+/* Private entrypoint. */
+
+diagnostic_event_id
+private_diagnostic_execution_path_add_event_3 (diagnostic_execution_path *path,
+ const diagnostic_physical_location *physical_loc,
+ const diagnostic_logical_location *logical_loc,
+ unsigned stack_depth,
+ diagnostic_graph *state_graph,
+ diagnostic_message_buffer *msg_buf)
+{
+ FAIL_IF_NULL (path);
+ FAIL_IF_NULL (msg_buf);
+
+ diagnostic_event_id_t result
+ = path->add_event_via_msg_buf
+ (physical_loc,
+ logical_loc,
+ stack_depth,
+ std::unique_ptr <diagnostic_graph> (state_graph),
+ std::unique_ptr <diagnostic_message_buffer> (msg_buf));
+
+ return as_diagnostic_event_id (result);
+}
#define LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL(ARG_NUM)
/* empty; for the human reader */
+# if (LIBGDIAGNOSTICS_GCC_VERSION >= 4001)
+# define LIBGDIAGNOSTICS_PARAM_FORMAT_STRING(FMT_KIND, FMT_ARG_NUM, ARGS_ARG_NUM) \
+ __attribute__ ((__format__ (FMT_KIND, FMT_ARG_NUM, ARGS_ARG_NUM)))
+# else
+# define LIBGDIAGNOSTICS_PARAM_FORMAT_STRING(FMT_KIND, FMT_ARG_NUM, ARGS_ARG_NUM)
+# endif /* GNUC >= 4.1 */
+
#define LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(FMT_ARG_NUM, ARGS_ARG_NUM) \
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (FMT_ARG_NUM)
/* In theory we'd also add
of -Wall but undocumented, and much fussier than I'd want to inflict
on users of libgdiagnostics. */
+#define LIBGDIAGNOSTICS_PARAM_PRINTF_FORMAT_STRING(FMT_ARG_NUM, ARGS_ARG_NUM) \
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (FMT_ARG_NUM) \
+ LIBGDIAGNOSTICS_PARAM_FORMAT_STRING(gnu_printf, FMT_ARG_NUM, ARGS_ARG_NUM)
+
/**********************************************************************
Data structures and types.
All structs within the API are opaque.
typedef struct diagnostic_execution_path diagnostic_execution_path;
typedef int diagnostic_event_id;
+typedef struct diagnostic_message_buffer diagnostic_message_buffer;
+
/**********************************************************************
API entrypoints.
**********************************************************************/
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
+/* Message buffers. */
+
+#define LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer
+
+/* Create a new diagnostic_message_buffer.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern diagnostic_message_buffer *
+diagnostic_message_buffer_new (void);
+
+/* Release a diagnostic_message_buffer that hasn't been used.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_release (diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Append a UTF-8 encoded null-terminated string to the buffer.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_append_str (diagnostic_message_buffer *msg_buf,
+ const char *p)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* Append a UTF-8 encoded run of bytes to the buffer.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_append_text (diagnostic_message_buffer *msg_buf,
+ const char *p,
+ size_t len)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* Append a byte to to the buffer. This should be either
+ ASCII, or part of UTF-8 encoded text.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_append_byte (diagnostic_message_buffer *msg_buf,
+ char ch)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Append a formatted string to the buffer, using the formatting rules
+ for "printf".
+ The string is assumed to be UTF-8 encoded.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_append_printf (diagnostic_message_buffer *msg_buf,
+ const char *fmt, ...)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_PRINTF_FORMAT_STRING (2, 3);
+
+/* Append a diagnostic_event_id to the buffer in the form "(1)".
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_append_event_id (diagnostic_message_buffer *msg_buf,
+ diagnostic_event_id event_id)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Begin a run of text associated with the given URL.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_begin_url (diagnostic_message_buffer *msg_buf,
+ const char *url)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* End a run of text started with diagnostic_message_buffer_begin_url.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_end_url (diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Begin a run of text to be printed in quotes.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_begin_quote (diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* End a run of text started with diagnostic_message_buffer_begin_quote.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_end_quote (diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Begin a run of text to be printed with color.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_begin_color (diagnostic_message_buffer *msg_buf,
+ const char *color)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* End a run of text started with diagnostic_message_buffer_begin_color.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_end_color (diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
+
+/* Write a debugging representation of MSG_BUG to OUTF.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_message_buffer_dump (const diagnostic_message_buffer *msg_buf,
+ FILE *outf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* As diagnostic_finish, but takes ownership of MSG_BUF.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_finish_via_msg_buf (diagnostic *diag,
+ diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
+
+/* As diagnostic_add_location_with_label but takes ownership of MSG_BUF.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_add_location_with_label_via_msg_buf (diagnostic *diag,
+ const diagnostic_physical_location *loc,
+ diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
+
+/* As diagnostic_execution_path_add_event but takes ownership of MSG_BUF.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern diagnostic_event_id
+diagnostic_execution_path_add_event_via_msg_buf (diagnostic_execution_path *path,
+ const diagnostic_physical_location *physical_loc,
+ const diagnostic_logical_location *logical_loc,
+ unsigned stack_depth,
+ diagnostic_message_buffer *msg_buf)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (5);
+
+/* Set the description of GRAPH for use
+ in the value of the SARIF "description" property
+ (SARIF v2.1.0 section 3.39.2).
+
+ Takes ownership of DESC, if non-null.
+
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_graph_set_description_via_msg_buf (diagnostic_graph *graph,
+ diagnostic_message_buffer *desc)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
+
+/* Create and add a new edge within GRAPH.
+
+ If non-null, then EDGE_ID must be unique within edges in GRAPH;
+ if EDGE_ID is null then a unique id of the form "edge0", "edge1", etc
+ will be used automatically.
+
+ Takes ownership of LABEL, if non-null.
+
+ The new edge is owned by GRAPH.
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern diagnostic_edge *
+diagnostic_graph_add_edge_via_msg_buf (diagnostic_graph *graph,
+ const char *edge_id,
+ diagnostic_node *src_node,
+ diagnostic_node *dst_node,
+ diagnostic_message_buffer *label)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5);
+
+/* Set the label of NODE for use
+ in the value of the SARIF "label" property
+ (SARIF v2.1.0 section 3.40.3).
+
+ Takes ownership of LABEL, if non-null.
+
+ Added in LIBGDIAGNOSTICS_ABI_4. */
+
+extern void
+diagnostic_node_set_label_via_msg_buf (diagnostic_node *node,
+ diagnostic_message_buffer *label)
+ LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
+ LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
+
/* DEFERRED:
- thread-safety
- plural forms
- locations within binary files
- options and URLs for warnings
- enable/disable of warnings by kind
+ - command-line arguments
- plugin metadata. */
#ifdef __cplusplus
diagnostic_finish;
diagnostic_finish_va;
+
diagnostic_physical_location_get_file;
local: *;
diagnostic_node_set_logical_location;
# Private hooks used by sarif-replay
- private_diagnostic_execution_path_add_event_2;
private_diagnostic_graph_set_property_bag;
private_diagnostic_node_set_property_bag;
private_diagnostic_edge_set_property_bag;
} LIBGDIAGNOSTICS_ABI_2;
+
+# Add diagnostic_message_buffer
+LIBGDIAGNOSTICS_ABI_4 {
+ global:
+ diagnostic_message_buffer_new;
+ diagnostic_message_buffer_release;
+ diagnostic_message_buffer_append_str;
+ diagnostic_message_buffer_append_text;
+ diagnostic_message_buffer_append_byte;
+ diagnostic_message_buffer_append_printf;
+ diagnostic_message_buffer_append_event_id;
+ diagnostic_message_buffer_begin_url;
+ diagnostic_message_buffer_end_url;
+ diagnostic_message_buffer_begin_quote;
+ diagnostic_message_buffer_end_quote;
+ diagnostic_message_buffer_begin_color;
+ diagnostic_message_buffer_end_color;
+ diagnostic_message_buffer_dump;
+ diagnostic_finish_via_msg_buf;
+ diagnostic_add_location_with_label_via_msg_buf;
+ diagnostic_execution_path_add_event_via_msg_buf;
+ diagnostic_graph_set_description_via_msg_buf;
+ diagnostic_graph_add_edge_via_msg_buf;
+ diagnostic_node_set_label_via_msg_buf;
+
+ # Private hook used by sarif-replay
+ private_diagnostic_execution_path_add_event_3;
+} LIBGDIAGNOSTICS_ABI_3;
{
public:
annotation (libgdiagnostics::physical_location phys_loc,
- label_text label)
+ libgdiagnostics::message_buffer label)
: m_phys_loc (phys_loc),
m_label (std::move (label))
{
}
libgdiagnostics::physical_location m_phys_loc;
- label_text m_label;
+ libgdiagnostics::message_buffer m_label;
};
using id_map = std::map<std::string, const json::string *>;
enum status emit_sarif_as_diagnostics (const json::value &jv);
- label_text
+ libgdiagnostics::message_buffer
make_plain_text_within_result_message (const json::object *tool_component_obj,
const json::object &message_obj,
const json::object *rule_obj);
static void
add_any_annotations (libgdiagnostics::diagnostic &diag,
- const std::vector<annotation> &annotations)
+ std::vector<annotation> &annotations)
{
for (auto &annotation : annotations)
- if (annotation.m_label.get ())
+ if (annotation.m_label.m_inner)
diag.add_location_with_label (annotation.m_phys_loc,
- annotation.m_label.get ());
+ std::move (annotation.m_label));
else
diag.add_location (annotation.m_phys_loc);
}
}
// §3.27.11 "message" property
- label_text text;
+ libgdiagnostics::message_buffer msg_buf;
if (auto message_obj
= get_optional_property<json::object> (result_obj, PROP_result_message))
- text = make_plain_text_within_result_message (nullptr, // TODO: tool_component_obj,
- *message_obj,
- rule_obj);
- if (!text.get ())
+ msg_buf = make_plain_text_within_result_message (nullptr, // TODO: tool_component_obj,
+ *message_obj,
+ rule_obj);
+ if (!msg_buf.m_inner)
return status::err_invalid_sarif;
// §3.27.12 "locations" property
}
// §3.27.22 relatedLocations property
- std::vector<std::pair<libgdiagnostics::diagnostic, label_text>> notes;
+ std::vector<std::pair<libgdiagnostics::diagnostic,
+ libgdiagnostics::message_buffer>> notes;
const property_spec_ref prop_related_locations
("result", "relatedLocations", "3.27.22");
if (auto related_locations_arr
prop_message))
{
/* Treat related locations with a message as a "note". */
- label_text text
+ libgdiagnostics::message_buffer msg_buf
(make_plain_text_within_result_message
(tool_component_obj,
*message_obj,
rule_obj));
- if (!text.get ())
+ if (!msg_buf.m_inner)
return status::err_invalid_sarif;
auto note (m_output_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE));
note.set_location (physical_loc);
note.set_logical_location (logical_loc);
add_any_annotations (note, annotations);
- notes.push_back ({std::move (note), std::move (text)});
+ notes.push_back ({std::move (note), std::move (msg_buf)});
}
else
{
handle_fix_object (err, *fix_obj);
}
- err.finish ("%s", text.get ());
+ err.finish_via_msg_buf (std::move (msg_buf));
// Flush any notes
for (auto &iter : notes)
{
auto ¬e = iter.first;
- auto &text = iter.second;
- note.finish ("%s", text.get ());
+ auto &msg_buf = iter.second;
+ note.finish_via_msg_buf (std::move (msg_buf));
}
return status::ok;
and substitute for any placeholders (§3.11.5) and handle any
embedded links (§3.11.6).
- Limitations:
- - we don't preserve destinations within embedded links
-
MESSAGE_OBJ is "theMessage"
RULE_OBJ is "theRule". */
-label_text
+libgdiagnostics::message_buffer
sarif_replayer::
make_plain_text_within_result_message (const json::object *tool_component_obj,
const json::object &message_obj,
rule_obj,
js_str);
if (!original_text)
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
gcc_assert (js_str);
= get_optional_property<json::array> (message_obj, arguments_prop);
/* Duplicate original_text, substituting any placeholders. */
- std::string accum;
+ libgdiagnostics::message_buffer result (diagnostic_message_buffer_new ());
const char *iter_src = original_text;
while (char ch = *iter_src)
" but message object has no %qs property",
(int)arg_idx,
arguments_prop.get_property_name ());
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
if (arg_idx >= arguments->length ())
{
arguments_prop.get_property_name (),
(int)arg_idx);
// TODO: might be nice to add a note showing the args
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
auto replacement_jstr
= require_string (*arguments->get (arg_idx), arguments_prop);
if (!replacement_jstr)
- return label_text::borrow (nullptr);
- accum += replacement_jstr->get_string ();
+ return libgdiagnostics::message_buffer ();
+ result += replacement_jstr->get_string ();
}
else if (ch == '{' || ch == '}')
{
/* '{' and '}' are escaped by repeating them. */
if (iter_src[1] == ch)
{
- accum += ch;
+ result += ch;
iter_src += 2;
}
else
report_invalid_sarif (*js_str, msgs_with_placeholders,
"unescaped '%c' within message string",
ch);
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
}
else if (auto link = maybe_consume_embedded_link (iter_src))
{
- accum += link->text;
- /* TODO: use the destination. */
+ result.begin_url (link->destination.c_str ());
+ result += link->text.c_str ();
+ result.end_url ();
/* TODO: potentially could try to convert
intra-sarif links into event ids. */
}
else
{
- accum += ch;
+ result += ch;
iter_src++;
}
}
- return label_text::take (xstrdup (accum.c_str ()));
+ return result;
}
/* Handle a value that should be a multiformatMessageString object (§3.12).
{
libgdiagnostics::physical_location physical_loc;
libgdiagnostics::logical_location logical_loc;
- label_text message;
+ libgdiagnostics::message_buffer msg_buf;
int stack_depth = 0;
const property_spec_ref location_prop
= get_optional_property<json::object> (*location_obj,
location_message))
{
- message = make_plain_text_within_result_message
+ msg_buf = make_plain_text_within_result_message
(nullptr,
*message_obj,
nullptr/* TODO. */);
return s;
}
- if (message.get ())
- private_diagnostic_execution_path_add_event_2 (path.m_inner,
- physical_loc.m_inner,
- logical_loc.m_inner,
- stack_depth,
- state_graph.m_inner,
- "%s", message.get ());
- else
- private_diagnostic_execution_path_add_event_2 (path.m_inner,
- physical_loc.m_inner,
- logical_loc.m_inner,
- stack_depth,
- state_graph.m_inner,
- "");
+ if (!msg_buf.m_inner)
+ msg_buf.m_inner = diagnostic_message_buffer_new ();
+
+ private_diagnostic_execution_path_add_event_3 (path.m_inner,
+ physical_loc.m_inner,
+ logical_loc.m_inner,
+ stack_depth,
+ state_graph.m_inner,
+ msg_buf.m_inner);
+
state_graph.m_owned = false;
+ msg_buf.m_inner = nullptr;
return status::ok;
}
if (s != status::ok)
return s;
- label_text label;
+ libgdiagnostics::message_buffer label;
// §3.30.14 message property
{
if (auto description_obj
= get_optional_property<json::object> (graph_json_obj, description_prop))
{
- label_text text
+ auto msg_buf
= make_plain_text_within_result_message (&run_obj,
*description_obj,
nullptr);
- if (!text.get ())
+ if (!msg_buf.m_inner)
return status::err_invalid_sarif;
- out_graph.set_description (text.get ());
+ out_graph.set_description (std::move (msg_buf));
}
// §3.39.3: MAY contain a "nodes" property
if (auto label_obj
= get_optional_property<json::object> (node_json_obj, label_prop))
{
- label_text text
+ auto msg_buf
= make_plain_text_within_result_message (&run_obj,
*label_obj,
nullptr);
- if (!text.get ())
+ if (!msg_buf.m_inner)
return nullptr;
- new_node.set_label (text.get ());
+ new_node.set_label (std::move (msg_buf));
}
// §3.40.4 "location" property
edge_id_map[id] = id_str;
// §3.41.3 "label" property
- label_text label;
+ libgdiagnostics::message_buffer label;
const property_spec_ref label_prop
("edge", "label", "3.41.3");
if (auto label_obj
label = make_plain_text_within_result_message (nullptr,
*label_obj,
nullptr);
- if (!label.get ())
+ if (!label.m_inner)
return nullptr;
}
if (!dst_node.m_inner)
return nullptr;
- auto result = graph.add_edge (id, src_node, dst_node, label.get ());
+ auto result = graph.add_edge (id, src_node, dst_node, std::move (label));
if (auto properties = maybe_get_property_bag (edge_json_obj))
private_diagnostic_edge_set_property_bag (*result.m_inner,
push_back (std::move (tok));
}
void push_back_text (label_text &&text);
+ void push_back_byte (char ch);
void push_back (std::unique_ptr<pp_token> tok);
void push_back_list (pp_token_list &&list);
void begin_highlight_color (const char *color_name);
void end_highlight_color ();
+ void begin_url (const char *url);
+ void end_url ();
+
+ void add_event_id (diagnostic_event_id_t event_id);
+
void push_back_any_text ();
pretty_printer &m_pp;
#include "diagnostic-color.h"
#include "diagnostic-event-id.h"
#include "diagnostic-highlight-colors.h"
+#include "auto-obstack.h"
#include "selftest.h"
#if HAVE_ICONV
decode_utf8_char (const unsigned char *, size_t len, unsigned int *);
static void pp_quoted_string (pretty_printer *, const char *, size_t = -1);
-static void
+extern void
default_token_printer (pretty_printer *pp,
const pp_token_list &tokens);
push_back<pp_token_text> (std::move (text));
}
+void
+pp_token_list::push_back_byte (char ch)
+{
+ char buf[2];
+ buf[0] = ch;
+ buf[1] = '\0';
+ push_back_text (label_text::take (xstrdup (buf)));
+}
+
void
pp_token_list::push_back (std::unique_ptr<pp_token> tok)
{
gcc_assert (!formatters[argno]);
}
-struct auto_obstack
-{
- auto_obstack ()
- {
- obstack_init (&m_obstack);
- }
-
- ~auto_obstack ()
- {
- obstack_free (&m_obstack, NULL);
- }
-
- operator obstack & () { return m_obstack; }
-
- void grow (const void *src, size_t length)
- {
- obstack_grow (&m_obstack, src, length);
- }
-
- void *object_base () const
- {
- return m_obstack.object_base;
- }
-
- size_t object_size () const
- {
- return obstack_object_size (&m_obstack);
- }
-
- obstack m_obstack;
-};
-
/* Phase 3 of formatting a message (phases 1 and 2 done by pp_format).
Pop a pp_formatted_chunks from chunk_obstack, collecting all the tokens from
/* Default implementation of token printing. */
-static void
+void
default_token_printer (pretty_printer *pp,
const pp_token_list &tokens)
{
m_formatted_token_list->push_back<pp_token_end_color> ();
}
+void
+pp_markup::context::begin_url (const char *url)
+{
+ push_back_any_text ();
+ m_formatted_token_list->push_back<pp_token_begin_url>
+ (label_text::take (xstrdup (url)));
+}
+
+void
+pp_markup::context::end_url ()
+{
+ push_back_any_text ();
+ m_formatted_token_list->push_back<pp_token_end_url> ();
+}
+
+void
+pp_markup::context::add_event_id (diagnostic_event_id_t event_id)
+{
+ gcc_assert (event_id.known_p ());
+ push_back_any_text ();
+ m_formatted_token_list->push_back<pp_token_event_id> (event_id);
+}
+
void
pp_markup::context::push_back_any_text ()
{
+++ /dev/null
-import json
-import os
-
-def sarif_from_env():
- # return parsed JSON content a SARIF_PATH file
- json_filename = os.environ['SARIF_PATH']
- json_filename += '.sarif'
- print('json_filename: %r' % json_filename)
- with open(json_filename) as f:
- json_data = f.read()
- return json.loads(json_data)
-
-def get_location_artifact_uri(location):
- return location['physicalLocation']['artifactLocation']['uri']
-
-def get_location_physical_region(location):
- return location['physicalLocation']['region']
-
-def get_location_snippet_text(location):
- return location['physicalLocation']['contextRegion']['snippet']['text']
-
-def get_location_relationships(location):
- return location['relationships']
--- /dev/null
+from sarif import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def sarif():
+ return sarif_from_env()
+
+def test_message_in_generated_sarif(sarif):
+ result = get_result_by_index(sarif, 0)
+ assert result['level'] == 'error'
+ assert result['message']['text'] == "this is a string; foo; int: 42 str: mostly harmless; [this is a link](https://example.com/) 'this is quoted' highlight A highlight B (1)."
--- /dev/null
+/* Example of using a message buffer to build the text of a diagnostic
+ in pieces before emitting it. */
+
+#include "libgdiagnostics.h"
+#include "test-helpers.h"
+
+int
+main ()
+{
+ begin_test ("test-message-buffer.c.exe",
+ "test-message-buffer.c.sarif",
+ __FILE__, "c");
+
+ diagnostic_event_id event_id = 0;
+
+ /* begin quoted source */
+ diagnostic *d = diagnostic_begin (diag_mgr,
+ DIAGNOSTIC_LEVEL_ERROR);
+
+ diagnostic_message_buffer *msg_buf = diagnostic_message_buffer_new ();
+
+ /* Add a null-terminated string. */
+ diagnostic_message_buffer_append_str (msg_buf, "this is a string; ");
+
+ /* Add a length-specified string. */
+ diagnostic_message_buffer_append_text (msg_buf, "foobar", 3);
+
+ /* "printf"-formatting. */
+ diagnostic_message_buffer_append_printf (msg_buf,
+ "; int: %i str: %s; ",
+ 42, "mostly harmless");
+
+ /* Adding a URL. */
+ diagnostic_message_buffer_begin_url (msg_buf, "https://example.com/");
+ diagnostic_message_buffer_append_str (msg_buf, "this is a link");
+ diagnostic_message_buffer_end_url (msg_buf);
+
+ diagnostic_message_buffer_append_str (msg_buf, " ");
+
+ /* Add quoted text. */
+ diagnostic_message_buffer_begin_quote (msg_buf);
+ diagnostic_message_buffer_append_str (msg_buf, "this is quoted");
+ diagnostic_message_buffer_end_quote (msg_buf);
+
+ diagnostic_message_buffer_append_str (msg_buf, " ");
+
+ /* Add colorized text. */
+ diagnostic_message_buffer_begin_color (msg_buf, "highlight-a");
+ diagnostic_message_buffer_append_str (msg_buf, "highlight A");
+ diagnostic_message_buffer_end_color (msg_buf);
+
+ diagnostic_message_buffer_append_str (msg_buf, " ");
+
+ diagnostic_message_buffer_begin_color (msg_buf, "highlight-b");
+ diagnostic_message_buffer_append_str (msg_buf, "highlight B");
+ diagnostic_message_buffer_end_color (msg_buf);
+
+ diagnostic_message_buffer_append_str (msg_buf, " ");
+
+ /* Add an event ID. This will be printed as "(1)". */
+ diagnostic_message_buffer_append_event_id (msg_buf, event_id);
+
+ /* Add an ASCII char. */
+ diagnostic_message_buffer_append_byte (msg_buf, '.');
+
+ diagnostic_finish_via_msg_buf (d, msg_buf);
+ /* end quoted source */
+
+ return end_test ();
+};
+
+/* Verify the output from the text sink.
+ { dg-regexp "test-message-buffer.c.exe: error: this is a string; foo; int: 42 str: mostly harmless; this is a link 'this is quoted' highlight A highlight B \\(1\\)." } */
+
+/* Verify that some JSON was written to a file with the expected name:
+ { dg-final { verify-sarif-file } } */
+
+/* Use a Python script to verify various properties about the generated
+ .sarif file:
+ { dg-final { run-sarif-pytest test-message-buffer.c "test-message-buffer-c.py" } } */
== ' PyList_Append(list, item);\n'
assert tfl_2_loc['logicalLocations'] == location['logicalLocations']
assert tfl_2_loc['message']['text'] \
- == "when calling 'PyList_Append', passing NULL from (1) as argument 1"
+ == "when calling 'PyList_Append', passing NULL from [(1)](sarif:/runs/0/results/0/codeFlows/0/threadFlows/0/locations/0) as argument 1"
assert tfl_2['nestingLevel'] == 0
assert tfl_2['executionOrder'] == 3
+/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:file=3.11.6-embedded-links.sarif.html,javascript=no" } */
+/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=3.11.6-embedded-links.sarif.roundtrip.sarif" } */
+
{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [{"tool": {"driver": {"name": "hand-written"}},
/* With the fix from https://github.com/oasis-tcs/sarif-spec/issues/656 */
{"message": {"text": "003: Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1)."},
- "locations": []}
+ "locations": []},
/* { dg-begin-multiline-output "" }
hand-written: warning: 003: Prohibited term used in para[0]\spans[2].
+ { dg-end-multiline-output "" } */
+
+ {"message": {"text": "004: This is a [link](http://www.example.com)."},
+ "locations": []}
+/* { dg-begin-multiline-output "" }
+hand-written: warning: 004: This is a link.
{ dg-end-multiline-output "" } */
]}]}
+/* Use a Python script to verify various properties about the generated
+ .html file:
+ { dg-final { run-html-pytest 3.11.6-embedded-links.sarif "2.1.0-valid/embedded-links-check-html.py" } } */
+
+/* Use a Python script to verify various properties about the *generated*
+ .sarif file:
+ { dg-final { run-sarif-pytest 3.11.6-embedded-links.sarif.roundtrip "2.1.0-valid/embedded-links-check-sarif-roundtrip.py" } } */
+
--- /dev/null
+from htmltest import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def html_tree():
+ return html_tree_from_env()
+
+def test_generated_html(html_tree):
+ root = html_tree.getroot ()
+ assert root.tag == make_tag('html')
+
+ head = root.find('xhtml:head', ns)
+ assert head is not None
+
+ # Get "warning: 004: This is a link."
+ diag = get_diag_by_index(html_tree, 3)
+
+ msg = get_message_within_diag(diag)
+ assert msg is not None
+
+ assert_tag(msg[0], 'strong')
+ assert msg[0].text == 'warning: '
+ assert msg[0].tail == ' 004: This is a '
+ assert_tag(msg[1], 'a')
+ assert msg[1].text == 'link'
+ assert msg[1].get('href') == 'http://www.example.com'
+ assert msg[1].tail == '. '
--- /dev/null
+from sarif import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def sarif():
+ return sarif_from_env()
+
+def test_roundtrip_of_url_in_generated_sarif(sarif):
+ # Get "warning: 004: This is a link."
+ result = get_result_by_index(sarif, 3)
+ assert result['level'] == 'warning'
+ assert result['message']['text'] == "004: This is a [link](http://www.example.com)."