]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
diagnostics: generalize state graph code to use json::property instances (v2)
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 16 Oct 2025 21:39:03 +0000 (17:39 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Thu, 16 Oct 2025 21:39:03 +0000 (17:39 -0400)
In r16-1631-g2334d30cd8feac I added support for capturing state
information from -fanalyzer in the form of embedded XML strings
in SARIF output.

In r16-2211-ga5d9debedd2f46 I rewrote this so the state was captured in
the form of a SARIF directed graph, using various custom types.

I want to add the ability to capture other kinds of graph in our SARIF
output (e.g. inheritance hierarchies, CFGs, etc), so  the following patch
reworks the state graph handling code to minimize the use of custom types.
Instead, the patch introduces various json::property types, and
describes the state graph serialization in terms of instances of these
properties, rather than hardcoding string attribute names in readers and
writers.  The custom SARIF properties live in a new
"gcc/custom-sarif-properties/" directory.

The "experimental-html" scheme keys "show-state-diagrams-dot-src" and
"show-state-diagrams-sarif" become "show-graph-dot-src" and
"show-graph-dot-src" in preparation for new kinds of graph in the output.

This is an updated version of the patch, tested to build with GCC 5
(which the previous version didn't leading to PR bootstrap/122151)

contrib/ChangeLog:
* gcc.doxy (INPUT): Add gcc/custom-sarif-properties

gcc/ChangeLog:
* Makefile.in (OBJS-libcommon): Add
custom-sarif-properties/digraphs.o and
custom-sarif-properties/state-graphs.o.  Remove
diagnostics/state-graphs.o.
* configure: Regenerate.
* configure.ac: Add custom-sarif-properties to subdir iteration.
* custom-sarif-properties/digraphs.cc: New file.
* custom-sarif-properties/digraphs.h: New file.
* custom-sarif-properties/state-graphs.cc: New file.
* custom-sarif-properties/state-graphs.h: New file.
* diagnostics/diagnostics-selftests.cc
(run_diagnostics_selftests): Drop call of state_graphs_cc_tests.
* diagnostics/diagnostics-selftests.h (state_graphs_cc_tests):
Delete decl.
* diagnostics/digraphs.cc: Include
"custom-sarif-properties/digraphs.h".  Move include of
"selftest.h" to within CHECKING_P section.
(using digraph_object): New.
(namespace properties): New.
(diagnostics::digraphs::object::get_attr): Delete.
(diagnostics::digraphs::object::set_attr): Delete.
(diagnostics::digraphs::object::set_json_attr): Delete.
(digraph_object::get_property): New definitions, for various
property types.
(digraph_object::set_property): Likewise.
(digraph_object::maybe_get_property): New.
(digraph_object::get_property_as_tristate): New.
(digraph_object::ensure_property_bag): New.
(digraph::get_graph_kind): New.
(digraph::set_graph_kind): New.
Add include of "custom-sarif-properties/state-graphs.h".
(selftest::test_simple_graph): Rewrite to use json::property
instances rather than string attribute names.
(selftest::test_property_objects): New test.
(selftest::digraphs_cc_tests): Call it.
* diagnostics/digraphs.h: Include "tristate.h".
(object::get_attr): Delete.
(object::set_attr): Delete.
(object::get_property): New decls.
(object::set_property): New decls.
(object::maybe_get_property): New.
(object::get_property_as_tristate): New.
(object::set_json_attr): Delete.
(object::ensure_property_bag): New.
(graph::get_graph_kind): New.
(graph::set_graph_kind): New.
* diagnostics/html-sink.cc
(html_generation_options::html_generation_options): Update for
field renamings.
(html_generation_options::dump): Likewise.
(html_builder::maybe_make_state_diagram): Likewise.
(html_builder::add_graph): Show SARIF and .dot src inline, if
requested.
* diagnostics/html-sink.h
(html_generation_options::m_show_state_diagrams_sarif): Rename
to...
(html_generation_options::m_show_graph_sarif): ...this.
(html_generation_options::m_show_state_diagrams_dot_src): Rename
to...
(html_generation_options::m_show_graph_dot_src0): ...this.
* diagnostics/output-spec.cc
(html_scheme_handler::maybe_handle_kv): Rename keys.
(html_scheme_handler::get_keys): Likewise.
* diagnostics/state-graphs-to-dot.cc: : Reimplement throughout to
use json::property instances found within custom_sarif_properties
throughout, rather than types in diagnostics::state_graphs.
* diagnostics/state-graphs.cc: Deleted file.
* diagnostics/state-graphs.h: Delete almost all, except decl of
diagnostics::state_graphs::make_dot_graph.
* doc/invoke.texi: Update for changes to "experimental-html" sink
keys.
* json.cc (json::object::set_string): New.
(json::object::set_integer): New.
(json::object::set_bool): New.
(json::object::set_array_of_string): New.
* json.h: Include "label-text.h".
(struct json::property): New template.
(json::string_property): New.
(json::integer_property): New.
(json::bool_property): New.
(json::json_property): New.
(using json::array_of_string_property): New.
(struct json::enum_traits): New.
(enum_json::property): New.
(json::value::dyn_cast_array): New vfunc.
(json::value::dyn_cast_integer_number): New vfunc.
(json::value::set_string): New.
(json::value::set_integer): New.
(json::value::set_bool): New.
(json::value::set_array_of_string): New.
(json::value::maybe_get_enum): New.
(json::value::set_enum): New.
(json::array::dyn_cast_array): New.
(json::integer_number::dyn_cast_integer_number): New.
(object::maybe_get_enum): New.
(object::set_enum): New.

gcc/analyzer/ChangeLog:
* ana-state-to-diagnostic-state.cc: Reimplement throughout to use
json::property instances found within custom_sarif_properties
throughout, rather than types in diagnostics::state_graphs.
* ana-state-to-diagnostic-state.h: Likewise.
* checker-event.cc: Likewise.
* sm-malloc.cc: Likewise.

gcc/testsuite/ChangeLog:
* gcc.dg/plugin/diagnostic_plugin_test_graphs.cc
(report_diag_with_graphs): Port from set_attr to set_property.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
26 files changed:
contrib/gcc.doxy
gcc/Makefile.in
gcc/analyzer/ana-state-to-diagnostic-state.cc
gcc/analyzer/ana-state-to-diagnostic-state.h
gcc/analyzer/checker-event.cc
gcc/analyzer/sm-malloc.cc
gcc/configure
gcc/configure.ac
gcc/custom-sarif-properties/digraphs.cc [new file with mode: 0644]
gcc/custom-sarif-properties/digraphs.h [new file with mode: 0644]
gcc/custom-sarif-properties/state-graphs.cc [new file with mode: 0644]
gcc/custom-sarif-properties/state-graphs.h [new file with mode: 0644]
gcc/diagnostics/diagnostics-selftests.cc
gcc/diagnostics/diagnostics-selftests.h
gcc/diagnostics/digraphs.cc
gcc/diagnostics/digraphs.h
gcc/diagnostics/html-sink.cc
gcc/diagnostics/html-sink.h
gcc/diagnostics/output-spec.cc
gcc/diagnostics/state-graphs-to-dot.cc
gcc/diagnostics/state-graphs.cc [deleted file]
gcc/diagnostics/state-graphs.h
gcc/doc/invoke.texi
gcc/json.cc
gcc/json.h
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc

index 15952046f25658c2363698f1ee57c4c5d31ea48f..56e3845d00d4a6ea2fad8a0e8d45a687e048836f 100644 (file)
@@ -478,7 +478,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = gcc gcc/analyzer gcc/diagnostics gcc/text-art
+INPUT                  = gcc gcc/analyzer gcc/custom-sarif-properties 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 
index cf1408d56e29a314b5d089d758dd5a11ff6ccf31..8d9528cb07d0ceb053df7d5eb44fb6de053d1472 100644 (file)
@@ -1861,6 +1861,8 @@ 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 \
@@ -1880,7 +1882,6 @@ 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 \
index 25e66a0e541064c6ccf1e91c00973a9653d526b9..35740362b180952486c69ddb679dd6f1ca8f154d 100644 (file)
@@ -39,38 +39,55 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace ana {
 
-using namespace ::diagnostics::state_graphs;
+namespace node_properties = custom_sarif_properties::state_graphs::node;
 
 static void
-set_wi_attr (state_node_ref state_node,
-            const char *attr_name,
+set_wi_attr (diagnostics::digraphs::node &state_node,
+            const json::string_property &property,
             const wide_int_ref &w,
             signop sgn)
 {
   pretty_printer pp;
   pp_wide_int (&pp, w, sgn);
-  state_node.set_attr (attr_name, pp_formatted_text (&pp));
+  state_node.set_property (property, pp_formatted_text (&pp));
 }
 
 static void
-set_type_attr (state_node_ref state_node, const_tree type)
+set_type_attr (diagnostics::digraphs::node &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_type (pp_formatted_text (&pp));
+  state_node.set_property (node_properties::type,
+                          pp_formatted_text (&pp));
 }
 
 static void
-set_bits_attr (state_node_ref state_node,
+set_bits_attr (diagnostics::digraphs::node & state_node,
               bit_range bits)
 {
   pretty_printer pp;
   bits.dump_to_pp (&pp);
-  state_node.set_attr ("bits", pp_formatted_text (&pp));
+  state_node.set_property (node_properties::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,
@@ -141,34 +158,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.m_node, dst_node.m_node);
+      auto &dst_node = get_or_create_state_node (item.m_dst_reg);
+      add_edge (nullptr, item.m_src_node, dst_node);
     }
 }
 
-state_node_ref
+diagnostics::digraphs::node &
 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 ref = create_and_add_state_node (reg);
-  m_region_to_state_node_map[&reg] = &ref.m_node;
-  return ref;
+  auto &state_node = create_and_add_state_node (reg);
+  m_region_to_state_node_map[&reg] = &state_node;
+  return state_node;
 }
 
-state_node_ref
+diagnostics::digraphs::node &
 analyzer_state_graph::create_and_add_state_node (const region &reg)
 {
   auto node = create_state_node (reg);
 
-  state_node_ref result = *node;
+  diagnostics::digraphs::node &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.m_node.add_child (std::move (node));
+       auto &parent_state_node = get_or_create_state_node (*parent_reg);
+       parent_state_node.add_child (std::move (node));
        return result;
       }
   add_node (std::move (node));
@@ -264,19 +281,18 @@ analyzer_state_graph::make_node_id (const region &reg)
 
 std::unique_ptr<diagnostics::digraphs::node>
 analyzer_state_graph::
-make_state_node (diagnostics::state_graphs::node_kind kind,
+make_state_node (enum node_properties::kind_t kind,
                 std::string id)
 {
   auto node = std::make_unique<diagnostics::digraphs::node> (*this, std::move (id));
-  state_node_ref node_ref (*node);
-  node_ref.set_node_kind (kind);
+  node->set_property (node_properties::kind_prop, kind);
   return node;
 }
 
 std::unique_ptr<diagnostics::digraphs::node>
 analyzer_state_graph::
 make_memspace_state_node (const region &reg,
-                         diagnostics::state_graphs::node_kind kind)
+                         enum node_properties::kind_t kind)
 {
   return make_state_node (kind, make_node_id (reg));
 }
@@ -296,7 +312,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 (diagnostics::state_graphs::node_kind::stack_frame,
+       node = make_state_node (node_properties::kind_t::stack_frame,
                                make_node_id (reg));
        node->set_logical_loc
          (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ()));
@@ -304,58 +320,59 @@ 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_attr (STATE_NODE_PREFIX, "function",
-                         pp_formatted_text (&pp));
+         node->set_property (node_properties::function,
+                             pp_formatted_text (&pp));
        }
       }
       break;
 
     case RK_GLOBALS:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::globals);
+                                      node_properties::kind_t::globals);
       break;
     case RK_CODE:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::code);
+                                      node_properties::kind_t::code);
       break;
     case RK_FUNCTION:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::function);
+                                      node_properties::kind_t::function);
       // TODO
       break;
 
     case RK_STACK:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::stack);
+                                      node_properties::kind_t::stack);
       break;
     case RK_HEAP:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::heap_);
+                                      node_properties::kind_t::heap_);
       break;
     case RK_THREAD_LOCAL:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::thread_local_);
+                                      node_properties::kind_t::thread_local_);
       break;
     case RK_ROOT:
       gcc_unreachable ();
       break;
     case RK_SYMBOLIC:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::other);
+                                      node_properties::kind_t::other);
       break;
 
     case RK_DECL:
       {
-       node = make_state_node (diagnostics::state_graphs::node_kind::variable,
+       node = make_state_node (node_properties::kind_t::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_ref.set_name (pp_formatted_text (&pp));
+         node->set_property (node_properties::name,
+                             pp_formatted_text (&pp));
        }
        set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ()));
       }
@@ -377,14 +394,15 @@ analyzer_state_graph::create_state_node (const region &reg)
     case RK_ERRNO:
     case RK_PRIVATE:
     case RK_UNKNOWN:
-      node = make_state_node (diagnostics::state_graphs::node_kind::other,
+      node = make_state_node (node_properties::kind_t::other,
                                make_node_id (reg));
       break;
 
     case RK_HEAP_ALLOCATED:
     case RK_ALLOCA:
-      node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::dynalloc_buffer);
+      node
+       = make_memspace_state_node (reg,
+                                   node_properties::kind_t::dynalloc_buffer);
       set_attr_for_dynamic_extents (reg, *node);
       break;
     }
@@ -425,9 +443,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.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings));
+  ref.add_child (create_state_node_for_conc_bindings (conc_bindings));
 
   const region *typed_reg = cluster.get_base_region ();
   if (!typed_reg->get_type ())
@@ -455,23 +473,18 @@ 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 (diagnostics::state_graphs::node_kind::other,
+  auto node = make_state_node (node_properties::kind_t::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 (diagnostics::state_graphs::node_kind::other,
+       = make_state_node (node_properties::kind_t::other,
                           make_node_id ("binding"));
       set_bits_attr (*binding_state_node, bits);
-      {
-       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));
-      }
+      gcc_assert (sval);
+      set_value_attrs (*binding_state_node, *sval);
       node->add_child (std::move (binding_state_node));
     }
   return node;
@@ -496,27 +509,28 @@ analyzer_state_graph::get_bit_range_within_base_region (const region &reg,
 
 void
 analyzer_state_graph::
-populate_state_node_for_typed_region (state_node_ref node,
+populate_state_node_for_typed_region (diagnostics::digraphs::node &state_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 (node, reg_type);
+  set_type_attr (state_node, reg_type);
 
   bit_range bits (0, 0);
   if (get_bit_range_within_base_region (reg, bits))
     {
-      set_bits_attr (node, bits);
+      set_bits_attr (state_node, bits);
 
       auto search = conc_bindings.find (bits);
       if (search != conc_bindings.end ())
        {
          const svalue *bound_sval = search->second;
-         node.set_json_attr ("value", bound_sval->to_json ());
+         gcc_assert (bound_sval);
+         set_value_attrs (state_node, *bound_sval);
          if (const region *dst_reg = bound_sval->maybe_get_region ())
-             m_pending_edges.push_back ({node, *dst_reg});
+           m_pending_edges.push_back ({state_node, *dst_reg});
        }
     }
 
@@ -555,9 +569,10 @@ populate_state_node_for_typed_region (state_node_ref node,
              {
                auto child_state_node
                  = make_state_node
-                     (diagnostics::state_graphs::node_kind::element,
+                     (node_properties::kind_t::element,
                       make_node_id (*child_reg));
-               set_wi_attr (*child_state_node, "index", idx, UNSIGNED);
+               set_wi_attr (*child_state_node,
+                            node_properties::index, idx, UNSIGNED);
 
                // Recurse:
                gcc_assert (element_type);
@@ -565,7 +580,7 @@ populate_state_node_for_typed_region (state_node_ref node,
                                                      *child_reg,
                                                      conc_bindings,
                                                      create_all);
-               node.m_node.add_child (std::move (child_state_node));
+               state_node.add_child (std::move (child_state_node));
              }
          }
       }
@@ -587,11 +602,12 @@ populate_state_node_for_typed_region (state_node_ref node,
                  {
                    auto child_state_node
                      = make_state_node
-                         (diagnostics::state_graphs::node_kind::padding,
+                         (node_properties::kind_t::padding,
                           make_node_id (*child_reg));
-                   set_wi_attr (*child_state_node, "num_bits",
+                   set_wi_attr (*child_state_node,
+                                node_properties::num_bits,
                                 item.m_bit_range.m_size_in_bits, SIGNED);
-                   node.m_node.add_child (std::move (child_state_node));
+                   state_node.add_child (std::move (child_state_node));
                  }
              }
            else
@@ -600,27 +616,27 @@ populate_state_node_for_typed_region (state_node_ref 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
-                         (diagnostics::state_graphs::node_kind::field,
+                         (node_properties::kind_t::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_attr (STATE_NODE_PREFIX, "name",
-                                                 pp_formatted_text (&pp));
+                     child_state_node->set_property (node_properties::name,
+                                                     pp_formatted_text (&pp));
                    }
 
                    // Recurse:
                    populate_state_node_for_typed_region (*child_state_node,
-                                                      *child_reg,
-                                                      conc_bindings,
-                                                      create_all);
-                   node.m_node.add_child (std::move (child_state_node));
+                                                         *child_reg,
+                                                         conc_bindings,
+                                                         create_all);
+                   state_node.add_child (std::move (child_state_node));
                  }
              }
          }
@@ -630,8 +646,9 @@ populate_state_node_for_typed_region (state_node_ref node,
 }
 
 void
-analyzer_state_graph::set_attr_for_dynamic_extents (const region &reg,
-                                                   state_node_ref node_ref)
+analyzer_state_graph::
+set_attr_for_dynamic_extents (const region &reg,
+                             diagnostics::digraphs::node &state_node)
 {
   const svalue *sval = m_state.m_region_model->get_dynamic_extents (&reg);
   if (sval)
@@ -642,15 +659,16 @@ analyzer_state_graph::set_attr_for_dynamic_extents (const region &reg,
        pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED);
       else
        sval->dump_to_pp (&pp, true);
-      node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp));
+      state_node.set_property (state_node_properties::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;
index 3a5ccc1b4306b27984e0a0e8366134ab82e18a3f..272a4d762b653b757c85e7f3ab6fd76739e97c3d 100644 (file)
@@ -23,34 +23,38 @@ 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::state_graphs::state_node_ref
+  diagnostics::digraphs::node &
   get_or_create_state_node (const region &reg);
 
 private:
+
   struct pending_edge
   {
-    diagnostics::state_graphs::state_node_ref m_src_node;
+    diagnostics::digraphs::node & m_src_node;
     const region &m_dst_reg;
   };
-  
-  diagnostics::state_graphs::state_node_ref
+
+  diagnostics::digraphs::node &
   create_and_add_state_node (const region &reg);
 
   std::unique_ptr<diagnostics::digraphs::node>
-  make_state_node (diagnostics::state_graphs::node_kind kind,
+  make_state_node (enum state_node_properties::kind_t kind,
                   std::string id);
 
   std::unique_ptr<diagnostics::digraphs::node>
   make_memspace_state_node (const region &reg,
-                           enum diagnostics::state_graphs::node_kind kind);
+                           enum state_node_properties::kind_t kind);
 
   std::unique_ptr<diagnostics::digraphs::node>
   create_state_node (const region &reg);
@@ -71,14 +75,14 @@ private:
                                    bit_range &out);
 
   void
-  populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref,
+  populate_state_node_for_typed_region (diagnostics::digraphs::node &,
                                        const region &reg,
                                        const concrete_bindings_t &conc_bindings,
                                        bool create_all);
 
   void
   set_attr_for_dynamic_extents (const region &reg,
-                               diagnostics::state_graphs::state_node_ref);
+                               diagnostics::digraphs::node &);
 
   bool
   show_child_state_node_for_child_region_p (const region &reg,
@@ -95,7 +99,8 @@ 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;
index 4eac9450469f97b3fa2a6e4952c11fabcafab07c..790ebc714380060d416aa015afe359a673b46770 100644 (file)
@@ -29,6 +29,7 @@ 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"
@@ -242,9 +243,11 @@ 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);
-      result->set_attr (STATE_GRAPH_PREFIX,
-                       "analyzer/program_state/",
-                       pp_formatted_text (&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));
     }
 
   return result;
index a6b14219068af249a41f24ed5a1b35d66bfa6f6f..8ce771062ca6ab24b84c000417a80057229d1f49 100644 (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 diagnostics::state_graphs::node_dynalloc_state
+static enum custom_sarif_properties::state_graphs::node::dynalloc_state_t
 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 diagnostics::state_graphs::node_dynalloc_state::unknown;
+      return state_node_properties::dynalloc_state_t::unknown;
 
     case RS_ASSUMED_NON_NULL:
-      return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+      return state_node_properties::dynalloc_state_t::nonnull;
 
     case RS_UNCHECKED:
-      return diagnostics::state_graphs::node_dynalloc_state::unchecked;
+      return state_node_properties::dynalloc_state_t::unchecked;
     case RS_NONNULL:
-      return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+      return state_node_properties::dynalloc_state_t::nonnull;
     case RS_FREED:
-      return diagnostics::state_graphs::node_dynalloc_state::freed;
+      return state_node_properties::dynalloc_state_t::freed;
     }
 }
 
@@ -2768,24 +2768,23 @@ 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_dynalloc_state
-       (get_dynalloc_state_for_state (alloc_state->m_rs));
+      reg_node.set_property (state_node_properties::dynalloc_state_prop,
+                            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.m_node.set_attr (STATE_NODE_PREFIX,
-                                   "expected-deallocators",
-                                   pp_formatted_text (&pp));
+         reg_node.set_property (state_node_properties::expected_deallocators,
+                                pp_formatted_text (&pp));
        }
       if (alloc_state->m_deallocator)
-       reg_node.m_node.set_attr (STATE_NODE_PREFIX,
-                                 "deallocator",
-                                 alloc_state->m_deallocator->m_name);
+       reg_node.set_property (state_node_properties::deallocator,
+                              alloc_state->m_deallocator->m_name);
     }
 }
 
index 485219bb80b2a4d40320aafc15572b490e199595..962511f666cf5ade7463ac5dc65db02b6b6a8db4 100755 (executable)
@@ -36891,7 +36891,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 diagnostics text-art rtl-ssa sym-exec
+  for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec
   do
       ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
   done ;;
index 04f86b52ef845dc6ad87c2875d7e047a1329d495..1e5f7c3c4d0016bdf07584b9d626b55ecc1697dd 100644 (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 diagnostics text-art rtl-ssa sym-exec
+  for lang in $subdirs c-family common analyzer custom-sarif-properties 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])
diff --git a/gcc/custom-sarif-properties/digraphs.cc b/gcc/custom-sarif-properties/digraphs.cc
new file mode 100644 (file)
index 0000000..30ca2b6
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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");
diff --git a/gcc/custom-sarif-properties/digraphs.h b/gcc/custom-sarif-properties/digraphs.h
new file mode 100644 (file)
index 0000000..93817ed
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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 */
diff --git a/gcc/custom-sarif-properties/state-graphs.cc b/gcc/custom-sarif-properties/state-graphs.cc
new file mode 100644 (file)
index 0000000..cc56138
--- /dev/null
@@ -0,0 +1,161 @@
+/* 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_t>
+  node::kind_prop (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_t>
+  node::dynalloc_state_prop (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_t
+
+namespace json {
+
+template<>
+enum node::kind_t
+json::enum_traits<enum node::kind_t>::get_unknown_value ()
+{
+  return node::kind_t::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_t>::
+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_t>::get_string_for_value (enum_t value)
+{
+  return node_kind_strs[static_cast<int> (value)];
+}
+
+// Traits for enum node:dynalloc_state_t
+
+template<>
+enum node::dynalloc_state_t
+json::enum_traits<enum node::dynalloc_state_t>::get_unknown_value ()
+{
+  return node::dynalloc_state_t::unknown;
+}
+
+static const char * const dynalloc_state_strs[] = {
+  "unknown",
+  "nonnull",
+  "unchecked",
+  "freed"
+};
+
+template<>
+bool
+json::enum_traits<enum node::dynalloc_state_t>::
+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_t>::
+get_string_for_value (enum_t value)
+{
+  return dynalloc_state_strs[static_cast <size_t> (value)];
+}
+
+} // namespace json
diff --git a/gcc/custom-sarif-properties/state-graphs.h b/gcc/custom-sarif-properties/state-graphs.h
new file mode 100644 (file)
index 0000000..aa18d6a
--- /dev/null
@@ -0,0 +1,98 @@
+/* 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_t
+      {
+       // 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_t
+      {
+       unknown,
+       nonnull,
+       unchecked,
+       freed
+      };
+
+      extern const json::enum_property<enum kind_t> kind_prop;
+
+      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_t>
+       dynalloc_state_prop;
+    }
+    namespace edge {
+      extern const char *const prefix;
+    }
+  }
+}
+
+#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */
index 94a212a6c93a376199d23748e9e1fd8f14afad28..757655bb1728196629942eb21068410a25c4ea7b 100644 (file)
@@ -46,7 +46,6 @@ 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 ();
index 994ebad5280499528b6a89a03e771afe3be91479..5a68a049d3e98f0bcacbc497a06bd1bc53098470 100644 (file)
@@ -44,7 +44,6 @@ 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.  */
 
index 4a2ea4fca3c7dd8e9fc9f01b633cc8141f6131aa..59a9af09d3a6266961fdf532a68bc2ab65b0d11f 100644 (file)
@@ -30,13 +30,15 @@ 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"
 
-#include "selftest.h"
-
+using digraph_object = diagnostics::digraphs::object;
 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
@@ -171,66 +173,145 @@ conversion_to_dot::has_edges_p (const digraph_node &input_node)
 
 // class object
 
+/* String properties.  */
+
 const char *
-diagnostics::digraphs::object::
-get_attr (const char *key_prefix, const char *key) const
+digraph_object::get_property (const json::string_property &property) const
 {
   if (!m_property_bag)
     return nullptr;
-  std::string prefixed_key = std::string (key_prefix) + key;
-  if (json::value *jv = m_property_bag->get (prefixed_key.c_str ()))
+  if (json::value *jv = m_property_bag->get (property.m_key.get ()))
     if (json::string *jstr = jv->dyn_cast_string ())
       return jstr->get_string ();
   return nullptr;
 }
 
 void
-diagnostics::digraphs::object::
-set_attr (const char *key_prefix, const char *key, const char *value)
+digraph_object::set_property (const json::string_property &property,
+                             const char *utf8_value)
+{
+  auto &bag = ensure_property_bag ();
+  bag.set_string (property.m_key.get (), utf8_value);
+}
+
+/* Integer properties.  */
+
+bool
+digraph_object::maybe_get_property (const json::integer_property &property,
+                                   long &out_value) const
+{
+  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
 {
-  set_json_attr (key_prefix, key, std::make_unique<json::string> (value));
+  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
-diagnostics::digraphs::object::
-set_json_attr (const char *key_prefix, const char *key, std::unique_ptr<json::value> value)
+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 ()
 {
-  std::string prefixed_key = std::string (key_prefix) + key;
   if (!m_property_bag)
-    m_property_bag = std::make_unique<json::object> ();
-  m_property_bag->set (prefixed_key.c_str (), std::move (value));
+    m_property_bag = std::make_unique<sarif_property_bag> ( );
+  return *m_property_bag;
 }
 
 // class digraph
 
 DEBUG_FUNCTION void
-diagnostics::digraphs::digraph::dump () const
+digraph::dump () const
 {
   make_json_sarif_graph ()->dump ();
 }
 
 std::unique_ptr<json::object>
-diagnostics::digraphs::digraph::make_json_sarif_graph () const
+digraph::make_json_sarif_graph () const
 {
   return make_sarif_graph (*this, nullptr, nullptr);
 }
 
 std::unique_ptr<dot::graph>
-diagnostics::digraphs::digraph::make_dot_graph () const
+digraph::make_dot_graph () const
 {
-  conversion_to_dot to_dot;
-  return to_dot.make_dot_graph_from_diagnostic_graph (*this);
+  conversion_to_dot converter;
+  return converter.make_dot_graph_from_diagnostic_graph (*this);
 }
 
-std::unique_ptr<diagnostics::digraphs::digraph>
-diagnostics::digraphs::digraph::clone () const
+std::unique_ptr<digraph>
+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<diagnostics::digraphs::node *, diagnostics::digraphs::node *> node_mapping;
+  std::map<digraph_node *, digraph_node *> node_mapping;
 
   for (auto &iter : m_nodes)
     result->add_node (iter->clone (*result, node_mapping));
@@ -241,10 +322,10 @@ diagnostics::digraphs::digraph::clone () const
 }
 
 void
-diagnostics::digraphs::digraph::add_edge (const char *id,
-                                         node &src_node,
-                                         node &dst_node,
-                                         const char *label)
+digraph::add_edge (const char *id,
+                  node &src_node,
+                  node &dst_node,
+                  const char *label)
 {
   auto e = std::make_unique<digraph_edge> (*this,
                                           id,
@@ -263,7 +344,7 @@ diagnostics::digraphs::digraph::add_edge (const char *id,
    to edges by id (SARIF 2.1.0's Â§3.43.2 edgeId property).  */
 
 std::string
-diagnostics::digraphs::digraph::make_edge_id (const char *edge_id)
+digraph::make_edge_id (const char *edge_id)
 {
   /* If we have an id, use it.  */
   if (edge_id)
@@ -284,27 +365,38 @@ diagnostics::digraphs::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
-diagnostics::digraphs::node::dump () const
+digraph_node::dump () const
 {
   to_json_sarif_node ()->dump ();
 }
 
 std::unique_ptr<json::object>
-diagnostics::digraphs::node::to_json_sarif_node () const
+digraph_node::to_json_sarif_node () const
 {
   return make_sarif_node (*this, nullptr, nullptr);
 }
 
-std::unique_ptr<diagnostics::digraphs::node>
-diagnostics::digraphs::node::clone (digraph &new_graph,
-                                   std::map<node *, node *> &node_mapping) const
+std::unique_ptr<digraph_node>
+digraph_node::clone (digraph &new_graph,
+                    std::map<node *, node *> &node_mapping) const
 {
   auto result
-    = std::make_unique<diagnostics::digraphs::node> (new_graph,
-                                                    get_id ());
+    = std::make_unique<digraph_node> (new_graph, get_id ());
   node_mapping.insert ({const_cast <node *> (this), result.get ()});
 
   result->set_logical_loc (m_logical_loc);
@@ -353,6 +445,9 @@ 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 {
 
@@ -391,16 +486,17 @@ test_simple_graph ()
 #define KEY_PREFIX "/placeholder/"
   auto g = std::make_unique<digraph> ();
   g->set_description ("test graph");
-  g->set_attr (KEY_PREFIX, "date", "1066");
+  g->set_property (json::string_property (KEY_PREFIX, "date"), "1066");
 
   auto a = std::make_unique<digraph_node> (*g, "a");
   auto b = std::make_unique<digraph_node> (*g, "b");
-  b->set_attr (KEY_PREFIX, "color", "red");
+  b->set_property (json::string_property (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_attr (KEY_PREFIX, "status", "copacetic");
+  e->set_property (json::string_property (KEY_PREFIX, "status"),
+                  "copacetic");
   e->set_label ("I am an edge label");
   g->add_edge (std::move (e));
 
@@ -449,6 +545,34 @@ 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_prop),
+            state_node_properties::kind_t::other);
+  node.set_property (state_node_properties::kind_prop,
+                    state_node_properties::kind_t::stack);
+  ASSERT_EQ (node.get_property (state_node_properties::kind_prop),
+            state_node_properties::kind_t::stack);
+
+  ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
+            state_node_properties::dynalloc_state_t::unknown);
+  node.set_property (state_node_properties::dynalloc_state_prop,
+                    state_node_properties::dynalloc_state_t::freed);
+  ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
+            state_node_properties::dynalloc_state_t::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
@@ -456,6 +580,7 @@ digraphs_cc_tests ()
 {
   test_empty_graph ();
   test_simple_graph ();
+  test_property_objects ();
 }
 
 } // namespace diagnostics::selftest
index 7193ee48c3fa71bd29436c285b659f5c3899855c..485a18917ca0af8a9de5a45c178b64c5f6977c8d 100644 (file)
@@ -22,6 +22,7 @@ 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;
@@ -55,23 +56,57 @@ class edge;
 class object
 {
 public:
-  const char *
-  get_attr (const char *key_prefix,
-           const char *key) const;
-
+  /* String properties.  */
+  const char *get_property (const json::string_property &property) const;
+  void set_property (const json::string_property &property,
+                    const char *utf8_value);
+
+  /* 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_attr (const char *key_prefix,
-           const char *key,
-           const char *value);
+  set_property (const json::enum_property<EnumType> &property,
+               EnumType value)
+  {
+    auto &bag = ensure_property_bag ();
+    bag.set_enum<EnumType> (property, value);
+  }
 
-  void
-  set_json_attr (const char *key_prefix,
-                const char *key,
-                std::unique_ptr<json::value> 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);
 
   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)
   {
@@ -188,6 +223,9 @@ 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)
@@ -300,7 +338,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;
index d3fb107e61454ca4a456b202f396ab9efa67dbf3..99d3b9d5dab54f9adae65f2d0df2606627a26aa8 100644 (file)
@@ -57,8 +57,8 @@ html_generation_options::html_generation_options ()
 : m_css (true),
   m_javascript (true),
   m_show_state_diagrams (false),
-  m_show_state_diagrams_sarif (false),
-  m_show_state_diagrams_dot_src (false)
+  m_show_graph_sarif (false),
+  m_show_graph_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_state_diagrams_sarif);
-  DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src);
+  DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_sarif);
+  DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_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_state_diagrams_sarif);
+       (m_html_gen_opts.m_show_graph_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_state_diagrams_sarif)
+  if (m_html_gen_opts.m_show_graph_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_state_diagrams_dot_src)
+  if (m_html_gen_opts.m_show_graph_dot_src)
     {
       // For debugging, show the dot src inline:
       pretty_printer pp;
@@ -1278,21 +1278,41 @@ 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 (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));
-      }
+    {
+      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));
+       }
+    }
 }
 
 void
index d25ceea4b832410314ec5f2755a52670029a9cb3..ad68e6fa3a673d3d688e0be980fd071c6a3d42cf 100644 (file)
@@ -40,11 +40,12 @@ 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
-  bool m_show_state_diagrams_sarif;
+  /* 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 .dot source used for the diagram
-  bool m_show_state_diagrams_dot_src;
+  // If true, show the .dot source used for such graphs
+  bool m_show_graph_dot_src;
 };
 
 extern diagnostics::output_file
index dfde7f0bdf3038b981018b16e195f226a7b872d9..f7cce0acce845e281d713b1a053f4d9d5a5f63fc 100644 (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-state-diagrams-dot-src")
+  if (key == "show-graph-dot-src")
     return parse_bool_value (ctxt, key, value,
-                            m_html_gen_opts.m_show_state_diagrams_dot_src);
-  if (key == "show-state-diagrams-sarif")
+                            m_html_gen_opts.m_show_graph_dot_src);
+  if (key == "show-graph-sarif")
     return parse_bool_value (ctxt, key, value,
-                            m_html_gen_opts.m_show_state_diagrams_sarif);
+                            m_html_gen_opts.m_show_graph_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-state-diagrams-dot-src");
-  out.safe_push ("show-state-diagrams-sarif");
+  out.safe_push ("show-graph-dot-src");
+  out.safe_push ("show-graph-sarif");
 }
 
 } // namespace output_spec
index 2d80e6b283f6c3a3e722455256dc0066824e495c..790576c71c402e45be4b27adf3b929fb319b421f 100644 (file)
@@ -27,6 +27,7 @@ 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"
@@ -36,6 +37,8 @@ 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)
 {
@@ -47,28 +50,28 @@ get_depth (const digraphs::node &n)
 }
 
 static const char *
-get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st)
+get_color_for_dynalloc_state (enum state_node_properties::dynalloc_state_t dynalloc_st)
 {
   switch (dynalloc_st)
     {
     default:
       gcc_unreachable ();
       break;
-    case node_dynalloc_state::unknown:
-    case node_dynalloc_state::nonnull:
+    case state_node_properties::dynalloc_state_t::unknown:
+    case state_node_properties::dynalloc_state_t::nonnull:
       return nullptr;
 
-    case node_dynalloc_state::unchecked:
+    case state_node_properties::dynalloc_state_t::unchecked:
       return "#ec7a08"; // pf-orange-400
 
-    case node_dynalloc_state::freed:
+    case state_node_properties::dynalloc_state_t::freed:
       return "#cc0000"; // pf-red-100
     }
 }
 
 static void
 set_color_for_dynalloc_state (dot::attr_list &attrs,
-                             enum node_dynalloc_state state)
+                             enum state_node_properties::dynalloc_state_t state)
 {
   if (const char *color = get_color_for_dynalloc_state (state))
     attrs.add (dot::id ("color"), dot::id (color));
@@ -106,7 +109,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,
-                          state_node_ref (input_state_graph.get_node (i)));
+                          input_state_graph.get_node (i));
     add_stmt (std::move (root_cluster));
 
     /* Now create dot edges for edges in input_stage_graph.  */
@@ -126,7 +129,8 @@ 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, state_node_ref (dst_node).get_dynalloc_state ());
+         (e->m_attrs,
+          dst_node.get_property (state_node_properties::dynalloc_state_prop));
 
        add_stmt (std::move (e));
       }
@@ -147,9 +151,9 @@ private:
   }
 
   dot::id
-  make_id (state_node_ref state_node, bool cluster)
+  make_id (const diagnostics::digraphs::node &state_node, bool cluster)
   {
-    std::string input_node_id = state_node.m_node.get_id ();
+    std::string input_node_id = state_node.get_id ();
     if (cluster)
       return std::string ("cluster_") + input_node_id;
     else
@@ -157,44 +161,44 @@ private:
   }
 
   bool
-  starts_node_p (state_node_ref state_node)
+  starts_node_p (const diagnostics::digraphs::node &state_node)
   {
-    switch (state_node.get_node_kind ())
+    switch (state_node.get_property (state_node_properties::kind_prop))
       {
       default:
        return false;
 
-      case node_kind::stack:
+      case state_node_properties::kind_t::stack:
        /* We want all frames in the stack in the same table,
           so they are grouped.  */
-      case node_kind::dynalloc_buffer:
-      case node_kind::variable:
+      case state_node_properties::kind_t::dynalloc_buffer:
+      case state_node_properties::kind_t::variable:
        return true;
       }
   }
 
   const char *
-  get_label_for_node (state_node_ref state_node)
+  get_label_for_node (const diagnostics::digraphs::node &state_node)
   {
-    switch (state_node.get_node_kind ())
+    switch (state_node.get_property (state_node_properties::kind_prop))
       {
       default:
        return nullptr;
 
-      case node_kind::globals:
+      case state_node_properties::kind_t::globals:
        return _("Globals");
-      case node_kind::code:
+      case state_node_properties::kind_t::code:
        return _("Code");
-      case node_kind::stack:
+      case state_node_properties::kind_t::stack:
        return _("Stack");
-      case node_kind::heap_:
+      case state_node_properties::kind_t::heap_:
        return _("Heap");
       }
   }
 
   void
   on_input_state_node (dot::subgraph &parent_subgraph,
-                      state_node_ref state_node)
+                      const diagnostics::digraphs::node &state_node)
   {
     dot::id sg_id = make_id (state_node, true);
 
@@ -207,7 +211,7 @@ private:
        xp.set_attr ("cellborder", "1");
        xp.set_attr ("cellspacing", "0");
 
-       const int max_depth = get_depth (state_node.m_node);
+       const int max_depth = get_depth (state_node);
        const int num_columns = max_depth + 2;
 
        dot::id id_of_dot_node = make_id (state_node, false);
@@ -233,9 +237,9 @@ private:
          child_subgraph->add_attr (dot::id ("label"), dot::id (label));
 
        // recurse:
-       for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
+       for (size_t i = 0; i < state_node.get_num_children (); ++i)
          on_input_state_node (*child_subgraph,
-                              state_node.m_node.get_child (i));
+                              state_node.get_child (i));
        parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph));
       }
   }
@@ -246,10 +250,10 @@ private:
   add_title_tr (const dot::id &id_of_dot_node,
                xml::printer &xp,
                int num_columns,
-               state_node_ref state_node,
+               const diagnostics::digraphs::node &state_node,
                std::string heading,
                enum style styl,
-               enum node_dynalloc_state dynalloc_state)
+               enum state_node_properties::dynalloc_state_t dynalloc_state)
   {
     xp.push_tag ("tr", true);
     xp.push_tag ("td", false);
@@ -298,48 +302,51 @@ private:
   void
   on_node_in_table (const dot::id &id_of_dot_node,
                    xml::printer &xp,
-                   state_node_ref state_node,
+                   const diagnostics::digraphs::node &state_node,
                    int max_depth,
                    int depth,
                    int num_columns)
   {
     bool recurse = true;
-    auto input_node_kind = state_node.get_node_kind ();
+    auto input_node_kind
+      = state_node.get_property (state_node_properties::kind_prop);
 
     switch (input_node_kind)
       {
-      case node_kind::padding:
-      case node_kind::other:
+      case state_node_properties::kind_t::padding:
+      case state_node_properties::kind_t::other:
        return;
 
-      case node_kind::stack:
+      case state_node_properties::kind_t::stack:
        add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack",
                      style::h1,
-                     node_dynalloc_state::unknown);
+                     state_node_properties::dynalloc_state_t::unknown);
        break;
-      case node_kind::stack_frame:
+      case state_node_properties::kind_t::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,
-                         node_dynalloc_state::unknown);
+                         state_node_properties::dynalloc_state_t::unknown);
        break;
-      case node_kind::dynalloc_buffer:
+      case state_node_properties::kind_t::dynalloc_buffer:
        {
-         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 ();
+         enum state_node_properties::dynalloc_state_t dynalloc_st
+           = state_node.get_property
+               (state_node_properties::dynalloc_state_prop);
+         const char *extents
+           = state_node.get_property (state_node_properties::dynamic_extents);
+         const char *type = state_node.get_property (state_node_properties::type);
          pretty_printer pp;
          switch (dynalloc_st)
            {
            default:
              gcc_unreachable ();
 
-           case node_dynalloc_state::unknown:
-           case node_dynalloc_state::nonnull:
+           case state_node_properties::dynalloc_state_t::unknown:
+           case state_node_properties::dynalloc_state_t::nonnull:
              if (type)
                {
                if (extents)
@@ -356,7 +363,7 @@ private:
                }
              break;
 
-           case node_dynalloc_state::unchecked:
+           case state_node_properties::dynalloc_state_t::unchecked:
              if (type)
                {
                  if (extents)
@@ -371,7 +378,7 @@ private:
                }
              break;
 
-           case node_dynalloc_state::freed:
+           case state_node_properties::dynalloc_state_t::freed:
              // TODO: show deallocator
              // TODO: show deallocation event
              pp_printf (&pp, "Freed buffer");
@@ -404,9 +411,10 @@ private:
            {
            default:
              break;
-           case node_kind::variable:
+           case state_node_properties::kind_t::variable:
              {
-               const char *name = state_node.get_name ();
+               const char *name
+                 = state_node.get_property (state_node_properties::name);
                gcc_assert (name);
                xp.push_tag ("td", false);
                maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -416,9 +424,10 @@ private:
                xp.pop_tag ("td");
              }
              break;
-           case node_kind::element:
+           case state_node_properties::kind_t::element:
              {
-               const char *index = state_node.get_index ();
+               const char *index
+                 = state_node.get_property (state_node_properties::index);
                gcc_assert (index);
                xp.push_tag ("td", false);
                maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -430,9 +439,10 @@ private:
                xp.pop_tag ("td");
              }
              break;
-           case node_kind::field:
+           case state_node_properties::kind_t::field:
              {
-               const char *name = state_node.get_name ();
+               const char *name
+                 = state_node.get_property (state_node_properties::name);
                gcc_assert (name);
                xp.push_tag ("td", false);
                maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -445,7 +455,8 @@ private:
              break;
            }
 
-         if (const char *type = state_node.get_type ())
+         if (const char *type
+               = state_node.get_property (state_node_properties::type))
            {
              xp.push_tag ("td", false);
              xp.set_attr ("align", "right");
@@ -455,7 +466,8 @@ private:
              xp.pop_tag ("td");
            }
 
-         if (const char *value = state_node.get_value ())
+         if (const char *value
+               = state_node.get_property (state_node_properties::value_str))
            {
              xp.push_tag ("td", false);
              xp.set_attr ("align", "left");
@@ -466,15 +478,16 @@ private:
              xp.pop_tag ("td");
              recurse = false;
            }
+
          xp.pop_tag ("tr");
        }
        break;
       }
 
     if (recurse)
-      for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
+      for (size_t i = 0; i < state_node.get_num_children (); ++i)
        on_node_in_table (id_of_dot_node, xp,
-                         state_node.m_node.get_child (i),
+                         state_node.get_child (i),
                          max_depth, depth + 1, num_columns);
   }
 
@@ -497,9 +510,9 @@ private:
   void
   maybe_add_src_port (const dot::id &id_of_dot_node,
                      xml::printer &xp,
-                     state_node_ref state_node)
+                     const diagnostics::digraphs::node &state_node)
   {
-    auto iter = m_src_nodes.find (&state_node.m_node);
+    auto iter = m_src_nodes.find (&state_node);
     if (iter == m_src_nodes.end ())
       return;
 
@@ -507,7 +520,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.m_node, node_id});
+    m_src_node_to_port_id.insert ({&state_node, node_id});
     xp.set_attr ("port", src_id.m_str);
   }
 
@@ -517,9 +530,9 @@ private:
   void
   maybe_add_dst_port (const dot::id &id_of_dot_node,
                      xml::printer &xp,
-                     state_node_ref state_node)
+                     const diagnostics::digraphs::node &state_node)
   {
-    auto iter = m_dst_nodes.find (&state_node.m_node);
+    auto iter = m_dst_nodes.find (&state_node);
     if (iter == m_dst_nodes.end ())
       return;
 
@@ -527,7 +540,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.m_node, node_id});
+    m_dst_node_to_port_id.insert ({&state_node, node_id});
     xp.set_attr ("port", dst_id.m_str);
   }
 
@@ -535,11 +548,11 @@ private:
   const logical_locations::manager &m_logical_loc_mgr;
 
   /* All nodes involved in edges (and thus will need a port).  */
-  std::set<digraphs::node *> m_src_nodes;
-  std::set<digraphs::node *> m_dst_nodes;
+  std::set<const digraphs::node *> m_src_nodes;
+  std::set<const digraphs::node *> m_dst_nodes;
 
-  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::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::unique_ptr<dot::graph>
diff --git a/gcc/diagnostics/state-graphs.cc b/gcc/diagnostics/state-graphs.cc
deleted file mode 100644 (file)
index 5941c41..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/* 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 */
index ad18f82b5b73a43f78c6380e121752596282627c..21aded03ff6bc4349e6d4f4e81e842bc024aa163 100644 (file)
@@ -22,7 +22,6 @@ 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.
 
@@ -34,118 +33,11 @@ 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);
index 3f5398646bf3165393fafd974511f58593b13654..9c64acb5daf10dd0528f917143bb6cba7c3a97c3 100644 (file)
@@ -6296,16 +6296,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-state-diagrams-dot-src=@r{[}yes@r{|}no@r{]}
+@item show-graph-dot-src=@r{[}yes@r{|}no@r{]}
 This is a debugging feature and defaults to @code{no}.
-If @code{show-state-diagrams-dot-src=yes}
+If @code{show-graph-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-state-diagrams-sarif=@r{[}yes@r{|}no@r{]}
+@item show-graph-sarif=@r{[}yes@r{|}no@r{]}
 This is a debugging feature and defaults to @code{no}.
-If @code{show-state-diagrams-sarif=yes}
+If @code{show-graph-sarif=yes}
 then if @code{show-state-diagrams=yes}, the generated state diagrams will
 also show a SARIF representation of the state.
 
index 7153f087a0011c60c6a088ecee5860e9f60aaf8d..14ff76bd03665b7c24fd6127e3fe8e0f4575d928 100644 (file)
@@ -394,6 +394,31 @@ 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
index c706f2a4fe9fa14c159b6eafdb745717b56fad60..c53715ecb2ca00997cedc7964a34814c6a8a86f0 100644 (file)
@@ -21,6 +21,8 @@ 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/
@@ -116,6 +118,41 @@ 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
@@ -130,6 +167,8 @@ 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);
@@ -183,6 +222,19 @@ 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 (); }
@@ -210,6 +262,8 @@ 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);
 
@@ -269,6 +323,8 @@ 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:
@@ -317,6 +373,32 @@ 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 <>
index 7398a2908258451fca882369f23a2c48a66e2fc5..8ba576ec81c4f5025545bc5815242873967299cc 100644 (file)
@@ -210,9 +210,8 @@ 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");
-#define KEY_PREFIX "/placeholder-prefix/"
-      b->set_attr (KEY_PREFIX, "color", "red");
-#undef KEY_PREFIX
+      const json::string_property color ("/placeholder-prefix/color");
+      b->set_property (color, "red");
       auto c = std::make_unique<diagnostic_node> (*g, "c");
       c->set_label ("I am a node label");