Revert: r16-4193 ("diagnostics: generalize state graph code to use json::property instances")

This reverts commit e4ab1f8780.

Patch r16-4193-ge4ab1f87805a65 seems to have broken the build with
GCC 5.5 (PR bootstrap/122151).  Sorry about this.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm
2025-10-03 11:04:53 -04:00
parent f864e4b54a
commit 8cd2db265e
26 changed files with 521 additions and 900 deletions

View File

@@ -478,7 +478,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = gcc gcc/analyzer gcc/custom-sarif-properties gcc/diagnostics gcc/text-art
INPUT = gcc gcc/analyzer gcc/diagnostics gcc/text-art
# This tag can be used to specify the character encoding of the source files that
# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default

View File

@@ -1852,8 +1852,6 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies.
OBJS-libcommon = \
custom-sarif-properties/digraphs.o \
custom-sarif-properties/state-graphs.o \
diagnostic-global-context.o \
diagnostics/buffering.o \
diagnostics/changes.o \
@@ -1873,6 +1871,7 @@ OBJS-libcommon = \
diagnostics/paths.o \
diagnostics/paths-output.o \
diagnostics/source-printing.o \
diagnostics/state-graphs.o \
diagnostics/state-graphs-to-dot.o \
diagnostics/selftest-context.o \
diagnostics/selftest-logical-locations.o \

View File

@@ -39,55 +39,38 @@ along with GCC; see the file COPYING3. If not see
namespace ana {
namespace node_properties = custom_sarif_properties::state_graphs::node;
using namespace ::diagnostics::state_graphs;
static void
set_wi_attr (diagnostics::digraphs::node &state_node,
const json::string_property &property,
set_wi_attr (state_node_ref state_node,
const char *attr_name,
const wide_int_ref &w,
signop sgn)
{
pretty_printer pp;
pp_wide_int (&pp, w, sgn);
state_node.set_property (property, pp_formatted_text (&pp));
state_node.set_attr (attr_name, pp_formatted_text (&pp));
}
static void
set_type_attr (diagnostics::digraphs::node &state_node,
const_tree type)
set_type_attr (state_node_ref state_node, const_tree type)
{
gcc_assert (type);
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%T", type);
state_node.set_property (node_properties::type,
pp_formatted_text (&pp));
state_node.set_type (pp_formatted_text (&pp));
}
static void
set_bits_attr (diagnostics::digraphs::node & state_node,
set_bits_attr (state_node_ref state_node,
bit_range bits)
{
pretty_printer pp;
bits.dump_to_pp (&pp);
state_node.set_property (node_properties::bits,
pp_formatted_text (&pp));
state_node.set_attr ("bits", pp_formatted_text (&pp));
}
static void
set_value_attrs (diagnostics::digraphs::node &state_node,
const svalue &sval)
{
state_node.set_property (node_properties::value,
sval.to_json ());
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
sval.dump_to_pp (&pp, true);
state_node.set_property (node_properties::value_str,
pp_formatted_text (&pp));
}
// class analyzer_state_graph : public diagnostics::digraphs::digraph
analyzer_state_graph::analyzer_state_graph (const program_state &state,
@@ -158,34 +141,34 @@ analyzer_state_graph::analyzer_state_graph (const program_state &state,
/* Ensure we have a node for the dst region. This
could lead to additional pending edges. */
auto &dst_node = get_or_create_state_node (item.m_dst_reg);
add_edge (nullptr, item.m_src_node, dst_node);
auto dst_node = get_or_create_state_node (item.m_dst_reg);
add_edge (nullptr, item.m_src_node.m_node, dst_node.m_node);
}
}
diagnostics::digraphs::node &
state_node_ref
analyzer_state_graph::get_or_create_state_node (const region &reg)
{
auto existing = m_region_to_state_node_map.find (&reg);
if (existing != m_region_to_state_node_map.end ())
return *existing->second;
auto &state_node = create_and_add_state_node (reg);
m_region_to_state_node_map[&reg] = &state_node;
return state_node;
auto ref = create_and_add_state_node (reg);
m_region_to_state_node_map[&reg] = &ref.m_node;
return ref;
}
diagnostics::digraphs::node &
state_node_ref
analyzer_state_graph::create_and_add_state_node (const region &reg)
{
auto node = create_state_node (reg);
diagnostics::digraphs::node &result = *node;
state_node_ref result = *node;
if (auto parent_reg = reg.get_parent_region ())
if (parent_reg->get_kind () != RK_ROOT)
{
auto &parent_state_node = get_or_create_state_node (*parent_reg);
parent_state_node.add_child (std::move (node));
auto parent_state_node = get_or_create_state_node (*parent_reg);
parent_state_node.m_node.add_child (std::move (node));
return result;
}
add_node (std::move (node));
@@ -281,18 +264,19 @@ analyzer_state_graph::make_node_id (const region &reg)
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::
make_state_node (enum node_properties::kind kind,
make_state_node (diagnostics::state_graphs::node_kind kind,
std::string id)
{
auto node = std::make_unique<diagnostics::digraphs::node> (*this, std::move (id));
node->set_property (node_properties::kind, kind);
state_node_ref node_ref (*node);
node_ref.set_node_kind (kind);
return node;
}
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::
make_memspace_state_node (const region &reg,
enum node_properties::kind kind)
diagnostics::state_graphs::node_kind kind)
{
return make_state_node (kind, make_node_id (reg));
}
@@ -312,7 +296,7 @@ analyzer_state_graph::create_state_node (const region &reg)
const frame_region &frame_reg
= static_cast<const frame_region &> (reg);
node = make_state_node (node_properties::kind::stack_frame,
node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame,
make_node_id (reg));
node->set_logical_loc
(m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ()));
@@ -320,59 +304,58 @@ analyzer_state_graph::create_state_node (const region &reg)
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%E", frame_reg.get_fndecl ());
node->set_property (node_properties::function,
pp_formatted_text (&pp));
node->set_attr (STATE_NODE_PREFIX, "function",
pp_formatted_text (&pp));
}
}
break;
case RK_GLOBALS:
node = make_memspace_state_node (reg,
node_properties::kind::globals);
diagnostics::state_graphs::node_kind::globals);
break;
case RK_CODE:
node = make_memspace_state_node (reg,
node_properties::kind::code);
diagnostics::state_graphs::node_kind::code);
break;
case RK_FUNCTION:
node = make_memspace_state_node (reg,
node_properties::kind::function);
diagnostics::state_graphs::node_kind::function);
// TODO
break;
case RK_STACK:
node = make_memspace_state_node (reg,
node_properties::kind::stack);
diagnostics::state_graphs::node_kind::stack);
break;
case RK_HEAP:
node = make_memspace_state_node (reg,
node_properties::kind::heap_);
diagnostics::state_graphs::node_kind::heap_);
break;
case RK_THREAD_LOCAL:
node = make_memspace_state_node (reg,
node_properties::kind::thread_local_);
diagnostics::state_graphs::node_kind::thread_local_);
break;
case RK_ROOT:
gcc_unreachable ();
break;
case RK_SYMBOLIC:
node = make_memspace_state_node (reg,
node_properties::kind::other);
diagnostics::state_graphs::node_kind::other);
break;
case RK_DECL:
{
node = make_state_node (node_properties::kind::variable,
node = make_state_node (diagnostics::state_graphs::node_kind::variable,
make_node_id (reg));
const decl_region &decl_reg
= static_cast<const decl_region &> (reg);
state_node_ref node_ref (*node);
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%E", decl_reg.get_decl ());
node->set_property (node_properties::name,
pp_formatted_text (&pp));
node_ref.set_name (pp_formatted_text (&pp));
}
set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ()));
}
@@ -394,14 +377,14 @@ analyzer_state_graph::create_state_node (const region &reg)
case RK_ERRNO:
case RK_PRIVATE:
case RK_UNKNOWN:
node = make_state_node (node_properties::kind::other,
node = make_state_node (diagnostics::state_graphs::node_kind::other,
make_node_id (reg));
break;
case RK_HEAP_ALLOCATED:
case RK_ALLOCA:
node = make_memspace_state_node (reg,
node_properties::kind::dynalloc_buffer);
diagnostics::state_graphs::node_kind::dynalloc_buffer);
set_attr_for_dynamic_extents (reg, *node);
break;
}
@@ -442,9 +425,9 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster,
get_or_create_state_node (*reg);
}
auto &ref = get_or_create_state_node (*cluster.get_base_region ());
auto ref = get_or_create_state_node (*cluster.get_base_region ());
ref.add_child (create_state_node_for_conc_bindings (conc_bindings));
ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings));
const region *typed_reg = cluster.get_base_region ();
if (!typed_reg->get_type ())
@@ -472,18 +455,23 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster,
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings)
{
auto node = make_state_node (node_properties::kind::other,
auto node = make_state_node (diagnostics::state_graphs::node_kind::other,
make_node_id ("concrete-bindings"));
for (auto iter : conc_bindings)
{
const bit_range bits = iter.first;
const svalue *sval = iter.second;
auto binding_state_node
= make_state_node (node_properties::kind::other,
= make_state_node (diagnostics::state_graphs::node_kind::other,
make_node_id ("binding"));
set_bits_attr (*binding_state_node, bits);
gcc_assert (sval);
set_value_attrs (*binding_state_node, *sval);
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
sval->dump_to_pp (&pp, true);
binding_state_node->set_attr (STATE_NODE_PREFIX, "value",
pp_formatted_text (&pp));
}
node->add_child (std::move (binding_state_node));
}
return node;
@@ -508,28 +496,27 @@ analyzer_state_graph::get_bit_range_within_base_region (const region &reg,
void
analyzer_state_graph::
populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
populate_state_node_for_typed_region (state_node_ref node,
const region &reg,
const concrete_bindings_t &conc_bindings,
bool create_all)
{
const_tree reg_type = reg.get_type ();
gcc_assert (reg_type);
set_type_attr (state_node, reg_type);
set_type_attr (node, reg_type);
bit_range bits (0, 0);
if (get_bit_range_within_base_region (reg, bits))
{
set_bits_attr (state_node, bits);
set_bits_attr (node, bits);
auto search = conc_bindings.find (bits);
if (search != conc_bindings.end ())
{
const svalue *bound_sval = search->second;
gcc_assert (bound_sval);
set_value_attrs (state_node, *bound_sval);
node.set_json_attr ("value", bound_sval->to_json ());
if (const region *dst_reg = bound_sval->maybe_get_region ())
m_pending_edges.push_back ({state_node, *dst_reg});
m_pending_edges.push_back ({node, *dst_reg});
}
}
@@ -568,10 +555,9 @@ populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
{
auto child_state_node
= make_state_node
(node_properties::kind::element,
(diagnostics::state_graphs::node_kind::element,
make_node_id (*child_reg));
set_wi_attr (*child_state_node,
node_properties::index, idx, UNSIGNED);
set_wi_attr (*child_state_node, "index", idx, UNSIGNED);
// Recurse:
gcc_assert (element_type);
@@ -579,7 +565,7 @@ populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
*child_reg,
conc_bindings,
create_all);
state_node.add_child (std::move (child_state_node));
node.m_node.add_child (std::move (child_state_node));
}
}
}
@@ -601,12 +587,11 @@ populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
{
auto child_state_node
= make_state_node
(node_properties::kind::padding,
(diagnostics::state_graphs::node_kind::padding,
make_node_id (*child_reg));
set_wi_attr (*child_state_node,
node_properties::num_bits,
set_wi_attr (*child_state_node, "num_bits",
item.m_bit_range.m_size_in_bits, SIGNED);
state_node.add_child (std::move (child_state_node));
node.m_node.add_child (std::move (child_state_node));
}
}
else
@@ -615,27 +600,27 @@ populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
= m_mgr.get_field_region (&reg,
const_cast<tree> (item.m_field));
if (show_child_state_node_for_child_region_p (*child_reg,
conc_bindings,
create_all))
conc_bindings,
create_all))
{
auto child_state_node
= make_state_node
(node_properties::kind::field,
(diagnostics::state_graphs::node_kind::field,
make_node_id (*child_reg));
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%D", item.m_field);
child_state_node->set_property (node_properties::name,
pp_formatted_text (&pp));
child_state_node->set_attr (STATE_NODE_PREFIX, "name",
pp_formatted_text (&pp));
}
// Recurse:
populate_state_node_for_typed_region (*child_state_node,
*child_reg,
conc_bindings,
create_all);
state_node.add_child (std::move (child_state_node));
*child_reg,
conc_bindings,
create_all);
node.m_node.add_child (std::move (child_state_node));
}
}
}
@@ -645,9 +630,8 @@ populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
}
void
analyzer_state_graph::
set_attr_for_dynamic_extents (const region &reg,
diagnostics::digraphs::node &state_node)
analyzer_state_graph::set_attr_for_dynamic_extents (const region &reg,
state_node_ref node_ref)
{
const svalue *sval = m_state.m_region_model->get_dynamic_extents (&reg);
if (sval)
@@ -658,16 +642,15 @@ set_attr_for_dynamic_extents (const region &reg,
pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED);
else
sval->dump_to_pp (&pp, true);
state_node.set_property (state_node_properties::dynamic_extents,
pp_formatted_text (&pp));
node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp));
}
}
bool
analyzer_state_graph::
show_child_state_node_for_child_region_p (const region &reg,
const concrete_bindings_t &conc_bindings,
bool create_all)
const concrete_bindings_t &conc_bindings,
bool create_all)
{
if (create_all)
return true;

View File

@@ -23,38 +23,34 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostics/state-graphs.h"
#include "tree-logical-location.h"
#include "custom-sarif-properties/state-graphs.h"
namespace ana {
namespace state_node_properties = custom_sarif_properties::state_graphs::node;
class analyzer_state_graph : public diagnostics::digraphs::digraph
{
public:
analyzer_state_graph (const program_state &state,
const extrinsic_state &ext_state);
diagnostics::digraphs::node &
diagnostics::state_graphs::state_node_ref
get_or_create_state_node (const region &reg);
private:
struct pending_edge
{
diagnostics::digraphs::node & m_src_node;
diagnostics::state_graphs::state_node_ref m_src_node;
const region &m_dst_reg;
};
diagnostics::digraphs::node &
diagnostics::state_graphs::state_node_ref
create_and_add_state_node (const region &reg);
std::unique_ptr<diagnostics::digraphs::node>
make_state_node (enum state_node_properties::kind kind,
make_state_node (diagnostics::state_graphs::node_kind kind,
std::string id);
std::unique_ptr<diagnostics::digraphs::node>
make_memspace_state_node (const region &reg,
enum state_node_properties::kind kind);
enum diagnostics::state_graphs::node_kind kind);
std::unique_ptr<diagnostics::digraphs::node>
create_state_node (const region &reg);
@@ -75,14 +71,14 @@ private:
bit_range &out);
void
populate_state_node_for_typed_region (diagnostics::digraphs::node &,
populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref,
const region &reg,
const concrete_bindings_t &conc_bindings,
bool create_all);
void
set_attr_for_dynamic_extents (const region &reg,
diagnostics::digraphs::node &);
diagnostics::state_graphs::state_node_ref);
bool
show_child_state_node_for_child_region_p (const region &reg,
@@ -99,8 +95,7 @@ private:
const program_state &m_state;
const extrinsic_state &m_ext_state;
region_model_manager &m_mgr;
std::map<const region *,
diagnostics::digraphs::node *> m_region_to_state_node_map;
std::map<const region *, diagnostics::digraphs::node *> m_region_to_state_node_map;
std::map<const region *, tree> m_types_for_untyped_regions;
unsigned m_next_id;
std::vector<pending_edge> m_pending_edges;

View File

@@ -29,7 +29,6 @@ along with GCC; see the file COPYING3. If not see
#include "tree-logical-location.h"
#include "diagnostics/sarif-sink.h"
#include "diagnostics/state-graphs.h"
#include "custom-sarif-properties/state-graphs.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
@@ -243,11 +242,9 @@ checker_event::maybe_make_diagnostic_state_graph (bool debug) const
pretty_printer pp;
text_art::theme *theme = global_dc->get_diagram_theme ();
text_art::dump_to_pp (*state, theme, &pp);
const json::string_property program_state_property
(custom_sarif_properties::state_graphs::graph::prefix,
"analyzer/program_state/");
result->set_property (program_state_property,
pp_formatted_text (&pp));
result->set_attr (STATE_GRAPH_PREFIX,
"analyzer/program_state/",
pp_formatted_text (&pp));
}
return result;

View File

@@ -2735,7 +2735,7 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state);
}
static enum custom_sarif_properties::state_graphs::node::dynalloc_state
static enum diagnostics::state_graphs::node_dynalloc_state
get_dynalloc_state_for_state (enum resource_state rs)
{
switch (rs)
@@ -2746,17 +2746,17 @@ get_dynalloc_state_for_state (enum resource_state rs)
case RS_NULL:
case RS_NON_HEAP:
case RS_STOP:
return state_node_properties::dynalloc_state::unknown;
return diagnostics::state_graphs::node_dynalloc_state::unknown;
case RS_ASSUMED_NON_NULL:
return state_node_properties::dynalloc_state::nonnull;
return diagnostics::state_graphs::node_dynalloc_state::nonnull;
case RS_UNCHECKED:
return state_node_properties::dynalloc_state::unchecked;
return diagnostics::state_graphs::node_dynalloc_state::unchecked;
case RS_NONNULL:
return state_node_properties::dynalloc_state::nonnull;
return diagnostics::state_graphs::node_dynalloc_state::nonnull;
case RS_FREED:
return state_node_properties::dynalloc_state::freed;
return diagnostics::state_graphs::node_dynalloc_state::freed;
}
}
@@ -2768,23 +2768,24 @@ add_state_to_state_graph (analyzer_state_graph &out_state_graph,
{
if (const region *reg = sval.maybe_get_region ())
{
auto &reg_node = out_state_graph.get_or_create_state_node (*reg);
auto reg_node = out_state_graph.get_or_create_state_node (*reg);
auto alloc_state = as_a_allocation_state (state);
gcc_assert (alloc_state);
reg_node.set_property (state_node_properties::dynalloc_state,
get_dynalloc_state_for_state (alloc_state->m_rs));
reg_node.set_dynalloc_state
(get_dynalloc_state_for_state (alloc_state->m_rs));
if (alloc_state->m_deallocators)
{
pretty_printer pp;
alloc_state->m_deallocators->dump_to_pp (&pp);
reg_node.set_property (state_node_properties::expected_deallocators,
pp_formatted_text (&pp));
reg_node.m_node.set_attr (STATE_NODE_PREFIX,
"expected-deallocators",
pp_formatted_text (&pp));
}
if (alloc_state->m_deallocator)
reg_node.set_property (state_node_properties::deallocator,
alloc_state->m_deallocator->m_name);
reg_node.m_node.set_attr (STATE_NODE_PREFIX,
"deallocator",
alloc_state->m_deallocator->m_name);
}
}

2
gcc/configure vendored
View File

@@ -36868,7 +36868,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
"depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;;
"gccdepdir":C)
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec
for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec
do
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
done ;;

View File

@@ -1368,7 +1368,7 @@ AC_CHECK_HEADERS(ext/hash_map)
ZW_CREATE_DEPDIR
AC_CONFIG_COMMANDS([gccdepdir],[
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec
for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec
do
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR])

View File

@@ -1,28 +0,0 @@
/* Extra properties for digraphs in SARIF property bags.
Copyright (C) 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "json.h"
#include "custom-sarif-properties/digraphs.h"
const json::string_property custom_sarif_properties::digraphs::digraph::kind
("gcc/digraphs/graph/kind");

View File

@@ -1,37 +0,0 @@
/* Extra properties for digraphs in SARIF property bags.
Copyright (C) 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_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H
#define GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H
/* SARIF property names relating to digraphs. */
namespace custom_sarif_properties {
namespace digraphs {
namespace digraph {
/* A hint about the kind of graph we have,
and thus what kinds of nodes and edges to expect. */
extern const json::string_property kind;
// string; values: "cfg"
}
}
}
#endif /* ! GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H */

View File

@@ -1,157 +0,0 @@
/* Properties for capturing state graphs in SARIF property bags.
Copyright (C) 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "json.h"
#include "custom-sarif-properties/state-graphs.h"
/* graph. */
namespace graph = custom_sarif_properties::state_graphs::graph;
#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/"
const char *const graph::prefix = STATE_GRAPH_PREFIX;
#undef STATE_GRAPH_PREFIX
/* node. */
namespace node = custom_sarif_properties::state_graphs::node;
#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/"
const json::enum_property<enum node::kind> node::kind
(STATE_NODE_PREFIX "kind");
const json::string_property node::function (STATE_NODE_PREFIX "function");
const json::string_property node::dynamic_extents
(STATE_NODE_PREFIX "dynamic-extents");
const json::string_property node::name (STATE_NODE_PREFIX "name");
const json::string_property node::type (STATE_NODE_PREFIX "type");
const json::json_property node::value (STATE_NODE_PREFIX "value");
const json::string_property node::value_str (STATE_NODE_PREFIX "value_str");
const json::string_property node::index (STATE_NODE_PREFIX "index");
const json::string_property node::bits (STATE_NODE_PREFIX "bits");
const json::string_property node::num_bits (STATE_NODE_PREFIX "num_bits");
const json::string_property node::deallocator (STATE_NODE_PREFIX "deallocator");
const json::string_property node::expected_deallocators
(STATE_NODE_PREFIX "expected-deallocators");
const json::enum_property<enum node::dynalloc_state> node::dynalloc_state
(STATE_NODE_PREFIX "dynalloc-state");
#undef STATE_NODE_PREFIX
/* edge. */
namespace edge_props = custom_sarif_properties::state_graphs::edge;
#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/"
extern const char *const edge_props::prefix = STATE_EDGE_PREFIX;
#undef STATE_EDGE_PREFIX
// Traits for enum node:kind
template<>
enum node::kind
json::enum_traits<enum node::kind>::get_unknown_value ()
{
return node::kind::other;
}
static const char * const node_kind_strs[] = {
"globals",
"code",
"function",
"stack",
"stack-frame",
"heap",
"thread-local",
"dynalloc-buffer",
"variable",
"field",
"padding",
"element",
"other",
};
template<>
bool
json::enum_traits<enum node::kind>::
maybe_get_value_from_string (const char *str,
enum_t &out)
{
for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i)
if (!strcmp (node_kind_strs[i], str))
{
out = static_cast<enum_t> (i);
return true;
}
return false;
}
template<>
const char *
json::enum_traits<enum node::kind>::get_string_for_value (enum_t value)
{
return node_kind_strs[static_cast<int> (value)];
}
// Traits for enum node:dynalloc_state
template<>
enum node::dynalloc_state
json::enum_traits<enum node::dynalloc_state>::get_unknown_value ()
{
return node::dynalloc_state::unknown;
}
static const char * const dynalloc_state_strs[] = {
"unknown",
"nonnull",
"unchecked",
"freed"
};
template<>
bool
json::enum_traits<enum node::dynalloc_state>::
maybe_get_value_from_string (const char *str,
enum_t &out)
{
for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i)
if (!strcmp (dynalloc_state_strs[i], str))
{
out = static_cast<enum_t> (i);
return true;
}
return false;
}
template<>
const char *
json::enum_traits<enum node::dynalloc_state>::
get_string_for_value (enum_t value)
{
return dynalloc_state_strs[static_cast <size_t> (value)];
}

View File

@@ -1,97 +0,0 @@
/* Properties for capturing state graphs in SARIF property bags.
Copyright (C) 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/>. */
#include "json.h"
#ifndef GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
#define GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
/* SARIF property names relating to GCC's CFGs. */
namespace custom_sarif_properties {
namespace state_graphs {
namespace graph {
extern const char *const prefix;
}
namespace node {
enum class kind
{
// Memory regions
globals,
code,
function, // code within a particular function
stack,
stack_frame,
heap_,
thread_local_,
/* Dynamically-allocated buffer,
on heap or stack (depending on parent). */
dynalloc_buffer,
variable,
field, // field within a struct or union
padding, // padding bits in a struct or union
element, // element within an array
other // anything else
};
enum class dynalloc_state
{
unknown,
nonnull,
unchecked,
freed
};
extern const json::enum_property<enum kind> kind;
extern const json::string_property function;
extern const json::string_property dynamic_extents;
extern const json::string_property name;
extern const json::string_property type;
/* The value of a memory region, expressed as a json::value. */
extern const json::json_property value;
/* The value of a memory region, expressed as a string. */
extern const json::string_property value_str;
/* For element nodes, the index within the array. */
extern const json::string_property index;
/* The range of bits or bytes within the base region. */
extern const json::string_property bits;
/* The size of a padding region. */
extern const json::string_property num_bits;
extern const json::string_property deallocator;
extern const json::string_property expected_deallocators;
extern const json::enum_property<enum dynalloc_state> dynalloc_state;
}
namespace edge {
extern const char *const prefix;
}
}
}
#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */

View File

@@ -46,6 +46,7 @@ run_diagnostics_selftests ()
sarif_sink_cc_tests ();
digraphs_cc_tests ();
output_spec_cc_tests ();
state_graphs_cc_tests ();
lazy_paths_cc_tests ();
paths_output_cc_tests ();
changes_cc_tests ();

View File

@@ -44,6 +44,7 @@ extern void paths_output_cc_tests ();
extern void sarif_sink_cc_tests ();
extern void selftest_logical_locations_cc_tests ();
extern void source_printing_cc_tests ();
extern void state_graphs_cc_tests ();
} /* end of namespace diagnostics::selftest. */

View File

@@ -30,15 +30,13 @@ along with GCC; see the file COPYING3. If not see
#include "graphviz.h"
#include "diagnostics/digraphs.h"
#include "diagnostics/sarif-sink.h"
#include "custom-sarif-properties/digraphs.h"
using digraph_object = diagnostics::digraphs::object;
#include "selftest.h"
using digraph = diagnostics::digraphs::digraph;
using digraph_node = diagnostics::digraphs::node;
using digraph_edge = diagnostics::digraphs::edge;
namespace properties = custom_sarif_properties::digraphs;
namespace {
class conversion_to_dot
@@ -173,145 +171,66 @@ conversion_to_dot::has_edges_p (const digraph_node &input_node)
// class object
/* String properties. */
const char *
digraph_object::get_property (const json::string_property &property) const
diagnostics::digraphs::object::
get_attr (const char *key_prefix, const char *key) const
{
if (!m_property_bag)
return nullptr;
if (json::value *jv = m_property_bag->get (property.m_key.get ()))
std::string prefixed_key = std::string (key_prefix) + key;
if (json::value *jv = m_property_bag->get (prefixed_key.c_str ()))
if (json::string *jstr = jv->dyn_cast_string ())
return jstr->get_string ();
return nullptr;
}
void
digraph_object::set_property (const json::string_property &property,
const char *utf8_value)
diagnostics::digraphs::object::
set_attr (const char *key_prefix, const char *key, const char *value)
{
auto &bag = ensure_property_bag ();
bag.set_string (property.m_key.get (), utf8_value);
set_json_attr (key_prefix, key, std::make_unique<json::string> (value));
}
/* Integer properties. */
bool
digraph_object::maybe_get_property (const json::integer_property &property,
long &out_value) const
void
diagnostics::digraphs::object::
set_json_attr (const char *key_prefix, const char *key, std::unique_ptr<json::value> value)
{
std::string prefixed_key = std::string (key_prefix) + key;
if (!m_property_bag)
return false;
if (json::value *jv = m_property_bag->get (property.m_key.get ()))
if (json::integer_number *jnum = jv->dyn_cast_integer_number ())
{
out_value = jnum->get ();
return true;
}
return false;
}
void
digraph_object::set_property (const json::integer_property &property, long value)
{
auto &bag = ensure_property_bag ();
bag.set_integer (property.m_key.get (), value);
}
/* Bool properties. */
void
digraph_object::set_property (const json::bool_property &property, bool value)
{
auto &bag = ensure_property_bag ();
bag.set_bool (property.m_key.get (), value);
}
tristate
digraph_object::
get_property_as_tristate (const json::bool_property &property) const
{
if (m_property_bag)
{
if (json::value *jv = m_property_bag->get (property.m_key.get ()))
switch (jv->get_kind ())
{
default:
break;
case json::JSON_TRUE:
return tristate (true);
case json::JSON_FALSE:
return tristate (false);
}
}
return tristate::unknown ();
}
/* Array-of-string properties. */
json::array *
digraph_object::get_property (const json::array_of_string_property &property) const
{
if (m_property_bag)
if (json::value *jv = m_property_bag->get (property.m_key.get ()))
if (json::array *arr = jv->dyn_cast_array ())
return arr;
return nullptr;
}
/* json::value properties. */
const json::value *
digraph_object::get_property (const json::json_property &property) const
{
if (m_property_bag)
return m_property_bag->get (property.m_key.get ());
return nullptr;
}
void
digraph_object::set_property (const json::json_property &property,
std::unique_ptr<json::value> value)
{
auto &bag = ensure_property_bag ();
bag.set (property.m_key.get (), std::move (value));
}
json::object &
digraph_object::ensure_property_bag ()
{
if (!m_property_bag)
m_property_bag = std::make_unique<sarif_property_bag> ( );
return *m_property_bag;
m_property_bag = std::make_unique<json::object> ();
m_property_bag->set (prefixed_key.c_str (), std::move (value));
}
// class digraph
DEBUG_FUNCTION void
digraph::dump () const
diagnostics::digraphs::digraph::dump () const
{
make_json_sarif_graph ()->dump ();
}
std::unique_ptr<json::object>
digraph::make_json_sarif_graph () const
diagnostics::digraphs::digraph::make_json_sarif_graph () const
{
return make_sarif_graph (*this, nullptr, nullptr);
}
std::unique_ptr<dot::graph>
digraph::make_dot_graph () const
diagnostics::digraphs::digraph::make_dot_graph () const
{
conversion_to_dot converter;
return converter.make_dot_graph_from_diagnostic_graph (*this);
conversion_to_dot to_dot;
return to_dot.make_dot_graph_from_diagnostic_graph (*this);
}
std::unique_ptr<digraph>
digraph::clone () const
std::unique_ptr<diagnostics::digraphs::digraph>
diagnostics::digraphs::digraph::clone () const
{
auto result = std::make_unique<diagnostics::digraphs::digraph> ();
if (get_property_bag ())
result->set_property_bag (get_property_bag ()->clone_as_object ());
std::map<digraph_node *, digraph_node *> node_mapping;
std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> node_mapping;
for (auto &iter : m_nodes)
result->add_node (iter->clone (*result, node_mapping));
@@ -322,10 +241,10 @@ digraph::clone () const
}
void
digraph::add_edge (const char *id,
node &src_node,
node &dst_node,
const char *label)
diagnostics::digraphs::digraph::add_edge (const char *id,
node &src_node,
node &dst_node,
const char *label)
{
auto e = std::make_unique<digraph_edge> (*this,
id,
@@ -344,7 +263,7 @@ digraph::add_edge (const char *id,
to edges by id (SARIF 2.1.0's §3.43.2 edgeId property). */
std::string
digraph::make_edge_id (const char *edge_id)
diagnostics::digraphs::digraph::make_edge_id (const char *edge_id)
{
/* If we have an id, use it. */
if (edge_id)
@@ -365,38 +284,27 @@ digraph::make_edge_id (const char *edge_id)
}
}
const char *
digraph::get_graph_kind () const
{
return get_property (properties::digraph::kind);
}
void
digraph::set_graph_kind (const char *kind)
{
set_property (properties::digraph::kind, kind);
}
// class node
DEBUG_FUNCTION void
digraph_node::dump () const
diagnostics::digraphs::node::dump () const
{
to_json_sarif_node ()->dump ();
}
std::unique_ptr<json::object>
digraph_node::to_json_sarif_node () const
diagnostics::digraphs::node::to_json_sarif_node () const
{
return make_sarif_node (*this, nullptr, nullptr);
}
std::unique_ptr<digraph_node>
digraph_node::clone (digraph &new_graph,
std::map<node *, node *> &node_mapping) const
std::unique_ptr<diagnostics::digraphs::node>
diagnostics::digraphs::node::clone (digraph &new_graph,
std::map<node *, node *> &node_mapping) const
{
auto result
= std::make_unique<digraph_node> (new_graph, get_id ());
= std::make_unique<diagnostics::digraphs::node> (new_graph,
get_id ());
node_mapping.insert ({const_cast <node *> (this), result.get ()});
result->set_logical_loc (m_logical_loc);
@@ -445,9 +353,6 @@ diagnostics::digraphs::edge::to_json_sarif_edge () const
#if CHECKING_P
#include "selftest.h"
#include "custom-sarif-properties/state-graphs.h"
namespace diagnostics {
namespace selftest {
@@ -486,17 +391,16 @@ test_simple_graph ()
#define KEY_PREFIX "/placeholder/"
auto g = std::make_unique<digraph> ();
g->set_description ("test graph");
g->set_property (json::string_property (KEY_PREFIX, "date"), "1066");
g->set_attr (KEY_PREFIX, "date", "1066");
auto a = std::make_unique<digraph_node> (*g, "a");
auto b = std::make_unique<digraph_node> (*g, "b");
b->set_property (json::string_property (KEY_PREFIX, "color"), "red");
b->set_attr (KEY_PREFIX, "color", "red");
auto c = std::make_unique<digraph_node> (*g, "c");
c->set_label ("I am a node label");
auto e = std::make_unique<digraph_edge> (*g, nullptr, *a, *c);
e->set_property (json::string_property (KEY_PREFIX, "status"),
"copacetic");
e->set_attr (KEY_PREFIX, "status", "copacetic");
e->set_label ("I am an edge label");
g->add_edge (std::move (e));
@@ -545,34 +449,6 @@ test_simple_graph ()
}
}
static void
test_property_objects ()
{
namespace state_node_properties = custom_sarif_properties::state_graphs::node;
digraph g;
digraph_node node (g, "a");
ASSERT_EQ (node.get_property (state_node_properties::kind),
state_node_properties::kind::other);
node.set_property (state_node_properties::kind,
state_node_properties::kind::stack);
ASSERT_EQ (node.get_property (state_node_properties::kind),
state_node_properties::kind::stack);
ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
state_node_properties::dynalloc_state::unknown);
node.set_property (state_node_properties::dynalloc_state,
state_node_properties::dynalloc_state::freed);
ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
state_node_properties::dynalloc_state::freed);
ASSERT_EQ (node.get_property (state_node_properties::type), nullptr);
node.set_property (state_node_properties::type, "const char *");
ASSERT_STREQ (node.get_property (state_node_properties::type),
"const char *");
}
/* Run all of the selftests within this file. */
void
@@ -580,7 +456,6 @@ digraphs_cc_tests ()
{
test_empty_graph ();
test_simple_graph ();
test_property_objects ();
}
} // namespace diagnostics::selftest

View File

@@ -22,7 +22,6 @@ along with GCC; see the file COPYING3. If not see
#define GCC_DIAGNOSTICS_DIGRAPHS_H
#include "json.h"
#include "tristate.h"
#include "diagnostics/logical-locations.h"
class graphviz_out;
@@ -56,57 +55,23 @@ class edge;
class object
{
public:
/* String properties. */
const char *get_property (const json::string_property &property) const;
void set_property (const json::string_property &property,
const char *utf8_value);
const char *
get_attr (const char *key_prefix,
const char *key) const;
/* Integer properties. */
bool maybe_get_property (const json::integer_property &property, long &out) const;
void set_property (const json::integer_property &property, long value);
/* Bool properties. */
tristate
get_property_as_tristate (const json::bool_property &property) const;
void set_property (const json::bool_property &property, bool value);
/* Array-of-string properties. */
json::array *
get_property (const json::array_of_string_property &property) const;
/* enum properties. */
template <typename EnumType>
EnumType
get_property (const json::enum_property<EnumType> &property) const
{
if (m_property_bag)
{
EnumType result;
if (m_property_bag->maybe_get_enum<EnumType> (property, result))
return result;
}
return json::enum_traits<EnumType>::get_unknown_value ();
}
template <typename EnumType>
void
set_property (const json::enum_property<EnumType> &property,
EnumType value)
{
auto &bag = ensure_property_bag ();
bag.set_enum<EnumType> (property, value);
}
set_attr (const char *key_prefix,
const char *key,
const char *value);
/* json::value properties. */
const json::value *get_property (const json::json_property &property) const;
void set_property (const json::json_property &property,
std::unique_ptr<json::value> value);
void
set_json_attr (const char *key_prefix,
const char *key,
std::unique_ptr<json::value> value);
json::object *
get_property_bag () const { return m_property_bag.get (); }
json::object &
ensure_property_bag ();
void
set_property_bag (std::unique_ptr<json::object> property_bag)
{
@@ -223,9 +188,6 @@ class digraph : public object
std::unique_ptr<digraph> clone () const;
const char *get_graph_kind () const;
void set_graph_kind (const char *);
private:
void
add_node_id (std::string node_id, node &new_node)
@@ -338,7 +300,7 @@ class node : public object
clone (digraph &new_graph,
std::map<node *, node *> &node_mapping) const;
private:
private:
std::string m_id;
std::unique_ptr<std::string> m_label;
std::vector<std::unique_ptr<node>> m_children;

View File

@@ -57,8 +57,8 @@ html_generation_options::html_generation_options ()
: m_css (true),
m_javascript (true),
m_show_state_diagrams (false),
m_show_graph_sarif (false),
m_show_graph_dot_src (false)
m_show_state_diagrams_sarif (false),
m_show_state_diagrams_dot_src (false)
{
}
@@ -68,8 +68,8 @@ html_generation_options::dump (FILE *outfile, int indent) const
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_css);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_javascript);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_sarif);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_dot_src);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_sarif);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src);
}
class html_builder;
@@ -640,7 +640,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
the debug version. */
auto state_graph
= event.maybe_make_diagnostic_state_graph
(m_html_gen_opts.m_show_graph_sarif);
(m_html_gen_opts.m_show_state_diagrams_sarif);
if (!state_graph)
return nullptr;
@@ -652,7 +652,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
auto wrapper = std::make_unique<xml::element> ("div", false);
xml::printer xp (*wrapper);
if (m_html_gen_opts.m_show_graph_sarif)
if (m_html_gen_opts.m_show_state_diagrams_sarif)
{
// For debugging, show the SARIF src inline:
pretty_printer pp;
@@ -660,7 +660,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
print_pre_source (xp, pp_formatted_text (&pp));
}
if (m_html_gen_opts.m_show_graph_dot_src)
if (m_html_gen_opts.m_show_state_diagrams_dot_src)
{
// For debugging, show the dot src inline:
pretty_printer pp;
@@ -1278,41 +1278,21 @@ void
html_builder::add_graph (const digraphs::digraph &dg,
xml::element &parent_element)
{
auto div = std::make_unique<xml::element> ("div", false);
div->set_attr ("class", "gcc-directed-graph");
xml::printer xp (*div);
if (m_html_gen_opts.m_show_graph_sarif)
{
// For debugging, show the SARIF src inline:
pretty_printer pp;
dg.make_json_sarif_graph ()->print (&pp, true);
print_pre_source (xp, pp_formatted_text (&pp));
}
if (auto dot_graph = dg.make_dot_graph ())
{
if (m_html_gen_opts.m_show_graph_dot_src)
{
// For debugging, show the dot src inline:
pretty_printer pp;
dot::writer w (pp);
dot_graph->print (w);
print_pre_source (xp, pp_formatted_text (&pp));
}
if (auto svg_element = dot::make_svg_from_graph (*dot_graph))
{
if (const char *description = dg.get_description ())
{
xp.push_tag ("h2", true);
xp.add_text (description);
xp.pop_tag ("h2");
}
xp.append (std::move (svg_element));
parent_element.add_child (std::move (div));
}
}
if (auto svg_element = dot::make_svg_from_graph (*dot_graph))
{
auto div = std::make_unique<xml::element> ("div", false);
div->set_attr ("class", "gcc-directed-graph");
xml::printer xp (*div);
if (const char *description = dg.get_description ())
{
xp.push_tag ("h2", true);
xp.add_text (description);
xp.pop_tag ("h2");
}
xp.append (std::move (svg_element));
parent_element.add_child (std::move (div));
}
}
void

View File

@@ -40,12 +40,11 @@ struct html_generation_options
// If true, attempt to show state diagrams at events
bool m_show_state_diagrams;
/* If true, show the SARIF form of the state with such diagrams,
and of other graphs. */
bool m_show_graph_sarif;
// If true, show the SARIF form of the state with such diagrams
bool m_show_state_diagrams_sarif;
// If true, show the .dot source used for such graphs
bool m_show_graph_dot_src;
// If true, show the .dot source used for the diagram
bool m_show_state_diagrams_dot_src;
};
extern diagnostics::output_file

View File

@@ -650,12 +650,12 @@ html_scheme_handler::maybe_handle_kv (const context &ctxt,
if (key == "show-state-diagrams")
return parse_bool_value (ctxt, key, value,
m_html_gen_opts.m_show_state_diagrams);
if (key == "show-graph-dot-src")
if (key == "show-state-diagrams-dot-src")
return parse_bool_value (ctxt, key, value,
m_html_gen_opts.m_show_graph_dot_src);
if (key == "show-graph-sarif")
m_html_gen_opts.m_show_state_diagrams_dot_src);
if (key == "show-state-diagrams-sarif")
return parse_bool_value (ctxt, key, value,
m_html_gen_opts.m_show_graph_sarif);
m_html_gen_opts.m_show_state_diagrams_sarif);
return result::unrecognized;
}
@@ -666,8 +666,8 @@ html_scheme_handler::get_keys (auto_vec<const char *> &out) const
out.safe_push ("file");
out.safe_push ("javascript");
out.safe_push ("show-state-diagrams");
out.safe_push ("show-graph-dot-src");
out.safe_push ("show-graph-sarif");
out.safe_push ("show-state-diagrams-dot-src");
out.safe_push ("show-state-diagrams-sarif");
}
} // namespace output_spec

View File

@@ -27,7 +27,6 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "custom-sarif-properties/state-graphs.h"
#include "diagnostics/state-graphs.h"
#include "graphviz.h"
#include "xml.h"
@@ -37,8 +36,6 @@ along with GCC; see the file COPYING3. If not see
using namespace diagnostics;
using namespace diagnostics::state_graphs;
namespace state_node_properties = custom_sarif_properties::state_graphs::node;
static int
get_depth (const digraphs::node &n)
{
@@ -50,28 +47,28 @@ get_depth (const digraphs::node &n)
}
static const char *
get_color_for_dynalloc_state (enum state_node_properties::dynalloc_state dynalloc_st)
get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st)
{
switch (dynalloc_st)
{
default:
gcc_unreachable ();
break;
case state_node_properties::dynalloc_state::unknown:
case state_node_properties::dynalloc_state::nonnull:
case node_dynalloc_state::unknown:
case node_dynalloc_state::nonnull:
return nullptr;
case state_node_properties::dynalloc_state::unchecked:
case node_dynalloc_state::unchecked:
return "#ec7a08"; // pf-orange-400
case state_node_properties::dynalloc_state::freed:
case node_dynalloc_state::freed:
return "#cc0000"; // pf-red-100
}
}
static void
set_color_for_dynalloc_state (dot::attr_list &attrs,
enum state_node_properties::dynalloc_state state)
enum node_dynalloc_state state)
{
if (const char *color = get_color_for_dynalloc_state (state))
attrs.add (dot::id ("color"), dot::id (color));
@@ -109,7 +106,7 @@ public:
= std::make_unique<dot::subgraph> (dot::id ("cluster_memory_regions"));
for (size_t i = 0; i < input_state_graph.get_num_nodes (); ++i)
on_input_state_node (*root_cluster,
input_state_graph.get_node (i));
state_node_ref (input_state_graph.get_node (i)));
add_stmt (std::move (root_cluster));
/* Now create dot edges for edges in input_stage_graph. */
@@ -129,8 +126,7 @@ public:
auto e = std::make_unique<dot::edge_stmt> (src_port_id->second,
dst_port_id->second);
set_color_for_dynalloc_state
(e->m_attrs,
dst_node.get_property (state_node_properties::dynalloc_state));
(e->m_attrs, state_node_ref (dst_node).get_dynalloc_state ());
add_stmt (std::move (e));
}
@@ -151,9 +147,9 @@ private:
}
dot::id
make_id (const diagnostics::digraphs::node &state_node, bool cluster)
make_id (state_node_ref state_node, bool cluster)
{
std::string input_node_id = state_node.get_id ();
std::string input_node_id = state_node.m_node.get_id ();
if (cluster)
return std::string ("cluster_") + input_node_id;
else
@@ -161,44 +157,44 @@ private:
}
bool
starts_node_p (const diagnostics::digraphs::node &state_node)
starts_node_p (state_node_ref state_node)
{
switch (state_node.get_property (state_node_properties::kind))
switch (state_node.get_node_kind ())
{
default:
return false;
case state_node_properties::kind::stack:
case node_kind::stack:
/* We want all frames in the stack in the same table,
so they are grouped. */
case state_node_properties::kind::dynalloc_buffer:
case state_node_properties::kind::variable:
case node_kind::dynalloc_buffer:
case node_kind::variable:
return true;
}
}
const char *
get_label_for_node (const diagnostics::digraphs::node &state_node)
get_label_for_node (state_node_ref state_node)
{
switch (state_node.get_property (state_node_properties::kind))
switch (state_node.get_node_kind ())
{
default:
return nullptr;
case state_node_properties::kind::globals:
case node_kind::globals:
return _("Globals");
case state_node_properties::kind::code:
case node_kind::code:
return _("Code");
case state_node_properties::kind::stack:
case node_kind::stack:
return _("Stack");
case state_node_properties::kind::heap_:
case node_kind::heap_:
return _("Heap");
}
}
void
on_input_state_node (dot::subgraph &parent_subgraph,
const diagnostics::digraphs::node &state_node)
state_node_ref state_node)
{
dot::id sg_id = make_id (state_node, true);
@@ -211,7 +207,7 @@ private:
xp.set_attr ("cellborder", "1");
xp.set_attr ("cellspacing", "0");
const int max_depth = get_depth (state_node);
const int max_depth = get_depth (state_node.m_node);
const int num_columns = max_depth + 2;
dot::id id_of_dot_node = make_id (state_node, false);
@@ -237,9 +233,9 @@ private:
child_subgraph->add_attr (dot::id ("label"), dot::id (label));
// recurse:
for (size_t i = 0; i < state_node.get_num_children (); ++i)
for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
on_input_state_node (*child_subgraph,
state_node.get_child (i));
state_node.m_node.get_child (i));
parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph));
}
}
@@ -250,10 +246,10 @@ private:
add_title_tr (const dot::id &id_of_dot_node,
xml::printer &xp,
int num_columns,
const diagnostics::digraphs::node &state_node,
state_node_ref state_node,
std::string heading,
enum style styl,
enum state_node_properties::dynalloc_state dynalloc_state)
enum node_dynalloc_state dynalloc_state)
{
xp.push_tag ("tr", true);
xp.push_tag ("td", false);
@@ -302,49 +298,48 @@ private:
void
on_node_in_table (const dot::id &id_of_dot_node,
xml::printer &xp,
const diagnostics::digraphs::node &state_node,
state_node_ref state_node,
int max_depth,
int depth,
int num_columns)
{
bool recurse = true;
auto input_node_kind = state_node.get_property (state_node_properties::kind);
auto input_node_kind = state_node.get_node_kind ();
switch (input_node_kind)
{
case state_node_properties::kind::padding:
case state_node_properties::kind::other:
case node_kind::padding:
case node_kind::other:
return;
case state_node_properties::kind::stack:
case node_kind::stack:
add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack",
style::h1,
state_node_properties::dynalloc_state::unknown);
node_dynalloc_state::unknown);
break;
case state_node_properties::kind::stack_frame:
case node_kind::stack_frame:
if (auto logical_loc = state_node.get_logical_loc ())
if (const char *function
= m_logical_loc_mgr.get_short_name (logical_loc))
add_title_tr (id_of_dot_node, xp, num_columns, state_node,
std::string ("Frame: ") + function,
style::h2,
state_node_properties::dynalloc_state::unknown);
node_dynalloc_state::unknown);
break;
case state_node_properties::kind::dynalloc_buffer:
case node_kind::dynalloc_buffer:
{
enum state_node_properties::dynalloc_state dynalloc_st
= state_node.get_property (state_node_properties::dynalloc_state);
const char *extents
= state_node.get_property (state_node_properties::dynamic_extents);
const char *type = state_node.get_property (state_node_properties::type);
enum node_dynalloc_state dynalloc_st
= state_node.get_dynalloc_state ();
const char *extents = state_node.get_dynamic_extents ();
const char *type = state_node.get_type ();
pretty_printer pp;
switch (dynalloc_st)
{
default:
gcc_unreachable ();
case state_node_properties::dynalloc_state::unknown:
case state_node_properties::dynalloc_state::nonnull:
case node_dynalloc_state::unknown:
case node_dynalloc_state::nonnull:
if (type)
{
if (extents)
@@ -361,7 +356,7 @@ private:
}
break;
case state_node_properties::dynalloc_state::unchecked:
case node_dynalloc_state::unchecked:
if (type)
{
if (extents)
@@ -376,7 +371,7 @@ private:
}
break;
case state_node_properties::dynalloc_state::freed:
case node_dynalloc_state::freed:
// TODO: show deallocator
// TODO: show deallocation event
pp_printf (&pp, "Freed buffer");
@@ -409,10 +404,9 @@ private:
{
default:
break;
case state_node_properties::kind::variable:
case node_kind::variable:
{
const char *name
= state_node.get_property (state_node_properties::name);
const char *name = state_node.get_name ();
gcc_assert (name);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -422,10 +416,9 @@ private:
xp.pop_tag ("td");
}
break;
case state_node_properties::kind::element:
case node_kind::element:
{
const char *index
= state_node.get_property (state_node_properties::index);
const char *index = state_node.get_index ();
gcc_assert (index);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -437,10 +430,9 @@ private:
xp.pop_tag ("td");
}
break;
case state_node_properties::kind::field:
case node_kind::field:
{
const char *name
= state_node.get_property (state_node_properties::name);
const char *name = state_node.get_name ();
gcc_assert (name);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -453,8 +445,7 @@ private:
break;
}
if (const char *type
= state_node.get_property (state_node_properties::type))
if (const char *type = state_node.get_type ())
{
xp.push_tag ("td", false);
xp.set_attr ("align", "right");
@@ -464,8 +455,7 @@ private:
xp.pop_tag ("td");
}
if (const char *value
= state_node.get_property (state_node_properties::value_str))
if (const char *value = state_node.get_value ())
{
xp.push_tag ("td", false);
xp.set_attr ("align", "left");
@@ -476,16 +466,15 @@ private:
xp.pop_tag ("td");
recurse = false;
}
xp.pop_tag ("tr");
}
break;
}
if (recurse)
for (size_t i = 0; i < state_node.get_num_children (); ++i)
for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
on_node_in_table (id_of_dot_node, xp,
state_node.get_child (i),
state_node.m_node.get_child (i),
max_depth, depth + 1, num_columns);
}
@@ -508,9 +497,9 @@ private:
void
maybe_add_src_port (const dot::id &id_of_dot_node,
xml::printer &xp,
const diagnostics::digraphs::node &state_node)
state_node_ref state_node)
{
auto iter = m_src_nodes.find (&state_node);
auto iter = m_src_nodes.find (&state_node.m_node);
if (iter == m_src_nodes.end ())
return;
@@ -518,7 +507,7 @@ private:
dot::node_id node_id (id_of_dot_node,
dot::port (src_id,
dot::compass_pt::e));
m_src_node_to_port_id.insert ({&state_node, node_id});
m_src_node_to_port_id.insert ({&state_node.m_node, node_id});
xp.set_attr ("port", src_id.m_str);
}
@@ -528,9 +517,9 @@ private:
void
maybe_add_dst_port (const dot::id &id_of_dot_node,
xml::printer &xp,
const diagnostics::digraphs::node &state_node)
state_node_ref state_node)
{
auto iter = m_dst_nodes.find (&state_node);
auto iter = m_dst_nodes.find (&state_node.m_node);
if (iter == m_dst_nodes.end ())
return;
@@ -538,7 +527,7 @@ private:
dot::node_id node_id (id_of_dot_node,
dot::port (dst_id/*,
dot::compass_pt::w*/));
m_dst_node_to_port_id.insert ({&state_node, node_id});
m_dst_node_to_port_id.insert ({&state_node.m_node, node_id});
xp.set_attr ("port", dst_id.m_str);
}
@@ -546,11 +535,11 @@ private:
const logical_locations::manager &m_logical_loc_mgr;
/* All nodes involved in edges (and thus will need a port). */
std::set<const digraphs::node *> m_src_nodes;
std::set<const digraphs::node *> m_dst_nodes;
std::set<digraphs::node *> m_src_nodes;
std::set<digraphs::node *> m_dst_nodes;
std::map<const digraphs::node *, dot::node_id> m_src_node_to_port_id;
std::map<const digraphs::node *, dot::node_id> m_dst_node_to_port_id;
std::map<digraphs::node *, dot::node_id> m_src_node_to_port_id;
std::map<digraphs::node *, dot::node_id> m_dst_node_to_port_id;
};
std::unique_ptr<dot::graph>

View File

@@ -0,0 +1,156 @@
/* Extensions to diagnostics::digraphs to support state graphs.
Copyright (C) 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/>. */
#define INCLUDE_ALGORITHM
#define INCLUDE_MAP
#define INCLUDE_SET
#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "diagnostics/state-graphs.h"
#include "selftest.h"
using namespace diagnostics::state_graphs;
const char * const node_kind_strs[] = {
"globals",
"code",
"function",
"stack",
"stack-frame",
"heap",
"thread-local",
"dynalloc-buffer",
"variable",
"field",
"padding",
"element",
"other",
};
const char *
diagnostics::state_graphs::node_kind_to_str (enum node_kind k)
{
return node_kind_strs[static_cast<int> (k)];
}
// struct state_node_ref
enum node_kind
state_node_ref::get_node_kind () const
{
const char *value = get_attr ("kind");
if (!value)
return node_kind::other;
for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i)
if (!strcmp (node_kind_strs[i], value))
return static_cast<enum node_kind> (i);
return node_kind::other;
}
void
state_node_ref::set_node_kind (enum node_kind k)
{
set_attr ("kind", node_kind_to_str (k));
}
const char * const dynalloc_state_strs[] = {
"unknown",
"nonnull",
"unchecked",
"freed"
};
enum node_dynalloc_state
state_node_ref::get_dynalloc_state () const
{
const char *value = get_attr ("dynalloc-state");
if (!value)
return node_dynalloc_state::unknown;
for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i)
if (!strcmp (dynalloc_state_strs[i], value))
return static_cast<enum node_dynalloc_state> (i);
return node_dynalloc_state::unknown;
}
void
state_node_ref::set_dynalloc_state (enum node_dynalloc_state s) const
{
set_attr ("dynalloc-state",
dynalloc_state_strs[static_cast <size_t> (s)]);
}
const char *
state_node_ref::get_dynamic_extents () const
{
return m_node.get_attr (STATE_NODE_PREFIX, "dynamic-extents");
}
void
state_node_ref::set_json_attr (const char *key,
std::unique_ptr<json::value> value) const
{
m_node.set_json_attr (STATE_NODE_PREFIX, key, std::move (value));
}
#if CHECKING_P
namespace diagnostics {
namespace selftest {
static void
test_node_attrs ()
{
digraphs::digraph g;
digraphs::node n (g, "a");
state_node_ref node_ref (n);
ASSERT_EQ (node_ref.get_node_kind (), node_kind::other);
node_ref.set_node_kind (node_kind::stack);
ASSERT_EQ (node_ref.get_node_kind (), node_kind::stack);
ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::unknown);
node_ref.set_dynalloc_state (node_dynalloc_state::freed);
ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::freed);
ASSERT_EQ (node_ref.get_type (), nullptr);
node_ref.set_type ("const char *");
ASSERT_STREQ (node_ref.get_type (), "const char *");
}
/* Run all of the selftests within this file. */
void
state_graphs_cc_tests ()
{
test_node_attrs ();
}
} // namespace diagnostics::selftest
} // namespace diagnostics
#endif /* CHECKING_P */

View File

@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#define GCC_DIAGNOSTICS_STATE_GRAPHS_H
#include "diagnostics/digraphs.h"
#include "diagnostics/logical-locations.h"
/* diagnostics::digraphs provides support for directed graphs.
@@ -33,11 +34,118 @@ along with GCC; see the file COPYING3. If not see
in these nodes to stash extra properties (e.g. what kind of memory region
a node is e.g. stack vs heap). */
class sarif_graph;
namespace dot { class graph; }
namespace diagnostics {
namespace state_graphs {
enum class node_kind
{
// Memory regions
globals,
code,
function, // code within a particular function
stack,
stack_frame,
heap_,
thread_local_,
/* Dynamically-allocated buffer,
on heap or stack (depending on parent). */
dynalloc_buffer,
variable,
field, // field within a struct or union
padding, // padding bits in a struct or union
element, // element within an array
other // anything else
};
extern const char *
node_kind_to_str (enum node_kind);
enum class node_dynalloc_state
{
unknown,
nonnull,
unchecked,
freed
};
/* Prefixes to use in SARIF property bags. */
#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/"
#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/"
#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/"
/* A wrapper around a node that gets/sets attributes, using
the node's property bag for storage, so that the data roundtrips
through SARIF. */
struct state_node_ref
{
state_node_ref (diagnostics::digraphs::node &node)
: m_node (node)
{}
enum node_kind
get_node_kind () const;
void
set_node_kind (enum node_kind);
// For node_kind::stack_frame, this will be the function
logical_locations::key
get_logical_loc () const
{
return m_node.get_logical_loc ();
}
// For node_kind::dynalloc_buffer
enum node_dynalloc_state
get_dynalloc_state () const;
void
set_dynalloc_state (enum node_dynalloc_state) const;
const char *
get_dynamic_extents () const;
const char *
get_name () const { return get_attr ("name"); }
void
set_name (const char *name) const { set_attr ("name", name); }
const char *
get_type () const { return get_attr ("type"); }
void
set_type (const char *type) const { set_attr ("type", type); }
const char *
get_value () const { return get_attr ("value"); }
const char *
get_index () const { return get_attr ("index"); }
const char *
get_attr (const char *key) const
{
return m_node.get_attr (STATE_NODE_PREFIX, key);
}
void
set_attr (const char *key, const char *value) const
{
return m_node.set_attr (STATE_NODE_PREFIX, key, value);
}
void
set_json_attr (const char *key, std::unique_ptr<json::value> value) const;
diagnostics::digraphs::node &m_node;
};
extern std::unique_ptr<dot::graph>
make_dot_graph (const diagnostics::digraphs::digraph &state_graph,
const logical_locations::manager &logical_loc_mgr);

View File

@@ -6271,16 +6271,16 @@ These are visible by pressing ``j'' and ``k'' to single-step forward and
backward through events. Enabling this option will slow down
HTML generation.
@item show-graph-dot-src=@r{[}yes@r{|}no@r{]}
@item show-state-diagrams-dot-src=@r{[}yes@r{|}no@r{]}
This is a debugging feature and defaults to @code{no}.
If @code{show-graph-dot-src=yes}
If @code{show-state-diagrams-dot-src=yes}
then if @code{show-state-diagrams=yes},
the generated state diagrams will also show the .dot source input to
GraphViz used for the diagram.
@item show-graph-sarif=@r{[}yes@r{|}no@r{]}
@item show-state-diagrams-sarif=@r{[}yes@r{|}no@r{]}
This is a debugging feature and defaults to @code{no}.
If @code{show-graph-sarif=yes}
If @code{show-state-diagrams-sarif=yes}
then if @code{show-state-diagrams=yes}, the generated state diagrams will
also show a SARIF representation of the state.

View File

@@ -394,31 +394,6 @@ object::set_bool (const char *key, bool v)
set (key, new json::literal (v));
}
void
object::set_string (const string_property &property, const char *utf8_value)
{
set_string (property.m_key.get (), utf8_value);
}
void
object::set_integer (const integer_property &property, long value)
{
set_integer (property.m_key.get (), value);
}
void
object::set_bool (const bool_property &property, bool value)
{
set_bool (property.m_key.get (), value);
}
void
object::set_array_of_string (const array_of_string_property &property,
std::unique_ptr<json::array> value)
{
set<array> (property.m_key.get (), std::move (value));
}
/* Subroutine of json::compare for comparing a pairs of objects. */
int

View File

@@ -21,8 +21,6 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_JSON_H
#define GCC_JSON_H
#include "label-text.h"
/* Implementation of JSON, a lightweight data-interchange format.
See http://www.json.org/
@@ -118,41 +116,6 @@ struct token
} // namespace json::pointer
/* Typesafe way to work with properties in JSON objects. */
template <typename Traits>
struct property
{
explicit property (const char *key)
: m_key (label_text::borrow (key))
{}
explicit property (const char *key_prefix, const char *key)
: m_key (label_text::take (concat (key_prefix, key, nullptr)))
{}
label_text m_key;
};
using string_property = property<string>;
using integer_property = property<integer_number>;
using bool_property = property<literal>;
using json_property = property<value>;
using array_of_string_property = property<array>;
template <typename EnumType>
struct enum_traits
{
typedef EnumType enum_t;
static enum_t get_unknown_value ();
static bool maybe_get_value_from_string (const char *, enum_t &out);
static const char *get_string_for_value (enum_t value);
};
template <typename EnumType>
using enum_property = property<enum_traits<EnumType>>;
/* Base class of JSON value. */
class value
@@ -167,8 +130,6 @@ class value
void DEBUG_FUNCTION dump () const;
virtual object *dyn_cast_object () { return nullptr; }
virtual array *dyn_cast_array () { return nullptr; }
virtual integer_number *dyn_cast_integer_number () { return nullptr; }
virtual string *dyn_cast_string () { return nullptr; }
static int compare (const json::value &val_a, const json::value &val_b);
@@ -222,19 +183,6 @@ class object : public value
/* Set to literal true/false. */
void set_bool (const char *key, bool v);
/* Typesafe access to properties by name (such as from a schema). */
void set_string (const string_property &property, const char *utf8_value);
void set_integer (const integer_property &property, long value);
void set_bool (const bool_property &property, bool value);
void set_array_of_string (const array_of_string_property &property,
std::unique_ptr<json::array> value);
template <typename EnumType>
bool maybe_get_enum (const enum_property<EnumType> &property,
EnumType &out) const;
template <typename EnumType>
void set_enum (const enum_property<EnumType> &property,
EnumType value);
static int compare (const json::object &obj_a, const json::object &obj_b);
size_t get_num_keys () const { return m_keys.length (); }
@@ -262,8 +210,6 @@ class array : public value
void print (pretty_printer *pp, bool formatted) const final override;
std::unique_ptr<value> clone () const final override;
array *dyn_cast_array () final override { return this; }
void append (value *v);
void append_string (const char *utf8_value);
@@ -323,8 +269,6 @@ class integer_number : public value
void print (pretty_printer *pp, bool formatted) const final override;
std::unique_ptr<value> clone () const final override;
integer_number *dyn_cast_integer_number () final override { return this; }
long get () const { return m_value; }
private:
@@ -373,32 +317,6 @@ class literal : public value
enum kind m_kind;
};
template <typename EnumType>
inline bool
object::maybe_get_enum (const enum_property<EnumType> &property,
EnumType &out) const
{
if (value *jv = get (property.m_key.get ()))
if (string *jstr = jv->dyn_cast_string ())
{
if (enum_traits<EnumType>::maybe_get_value_from_string
(jstr->get_string (), out))
return true;
}
return false;
}
template <typename EnumType>
inline void
object::set_enum (const enum_property<EnumType> &property,
EnumType value)
{
const char *str
= json::enum_traits<EnumType>::get_string_for_value (value);
set_string (property.m_key.get (), str);
}
} // namespace json
template <>

View File

@@ -210,8 +210,9 @@ report_diag_with_graphs (location_t loc)
g->set_description (desc);
auto a = std::make_unique<diagnostic_node> (*g, "a");
auto b = std::make_unique<diagnostic_node> (*g, "b");
const json::string_property color ("/placeholder-prefix/color");
b->set_property (color, "red");
#define KEY_PREFIX "/placeholder-prefix/"
b->set_attr (KEY_PREFIX, "color", "red");
#undef KEY_PREFIX
auto c = std::make_unique<diagnostic_node> (*g, "c");
c->set_label ("I am a node label");