]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
diagnostics: generalize state graph code to use json::property instances
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 2 Oct 2025 22:19:13 +0000 (18:19 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Thu, 2 Oct 2025 22:19:13 +0000 (18:19 -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.

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 6a9d6204c8693353abdc7a721a63d747ecfe01da..098bafbce537515720b16892994be2e13ed9fdeb 100644 (file)
@@ -1852,6 +1852,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 \
@@ -1871,7 +1873,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 996538c378529a75ad76e9406830c3803b6fe72f..39acf26bdd50b0030623dab71da6c996b91c3aa2 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 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, 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 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::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::globals);
       break;
     case RK_CODE:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::code);
+                                      node_properties::kind::code);
       break;
     case RK_FUNCTION:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::function);
+                                      node_properties::kind::function);
       // TODO
       break;
 
     case RK_STACK:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::stack);
+                                      node_properties::kind::stack);
       break;
     case RK_HEAP:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::heap_);
+                                      node_properties::kind::heap_);
       break;
     case RK_THREAD_LOCAL:
       node = make_memspace_state_node (reg,
-                                      diagnostics::state_graphs::node_kind::thread_local_);
+                                      node_properties::kind::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::other);
       break;
 
     case RK_DECL:
       {
-       node = make_state_node (diagnostics::state_graphs::node_kind::variable,
+       node = make_state_node (node_properties::kind::variable,
                                make_node_id (reg));
        const decl_region &decl_reg
          = static_cast<const decl_region &> (reg);
-       state_node_ref node_ref (*node);
+
        {
          pretty_printer pp;
          pp_format_decoder (&pp) = default_tree_printer;
          pp_printf (&pp, "%E", decl_reg.get_decl ());
-         node_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,14 @@ 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::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_properties::kind::dynalloc_buffer);
       set_attr_for_dynamic_extents (reg, *node);
       break;
     }
@@ -425,9 +442,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 +472,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::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::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 +508,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 +568,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::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 +579,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 +601,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::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 +615,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::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 +645,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 +658,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..eec3d56b3011d5ecb31ebb44716cfb6ac30f2b72 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 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 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..b25e2adf015e196ebd71e78db9693d253a1316e3 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
 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::unknown;
 
     case RS_ASSUMED_NON_NULL:
-      return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+      return state_node_properties::dynalloc_state::nonnull;
 
     case RS_UNCHECKED:
-      return diagnostics::state_graphs::node_dynalloc_state::unchecked;
+      return state_node_properties::dynalloc_state::unchecked;
     case RS_NONNULL:
-      return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+      return state_node_properties::dynalloc_state::nonnull;
     case RS_FREED:
-      return diagnostics::state_graphs::node_dynalloc_state::freed;
+      return state_node_properties::dynalloc_state::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,
+                            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 38d8cd919cba81accca9cf86ad17f15d9a8a1dd5..a742ad7e64cb9cf122032532f835992fde1ce606 100755 (executable)
@@ -36868,7 +36868,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
     "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;;
     "gccdepdir":C)
   ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
-  for lang in $subdirs c-family common analyzer 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 19975fa5be5b200b62f10a1ed7e02cca6dc7f76f..253d3ff28e9be18c11e945bbcfaca1e0bd28784b 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..3e0e58a
--- /dev/null
@@ -0,0 +1,157 @@
+/* Properties for capturing state graphs in SARIF property bags.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "custom-sarif-properties/state-graphs.h"
+
+/* graph.  */
+namespace graph = custom_sarif_properties::state_graphs::graph;
+#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/"
+const char *const graph::prefix = STATE_GRAPH_PREFIX;
+#undef STATE_GRAPH_PREFIX
+
+/* node.  */
+namespace node = custom_sarif_properties::state_graphs::node;
+#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/"
+
+const json::enum_property<enum node::kind> node::kind
+  (STATE_NODE_PREFIX "kind");
+
+const json::string_property node::function (STATE_NODE_PREFIX "function");
+
+const json::string_property node::dynamic_extents
+  (STATE_NODE_PREFIX "dynamic-extents");
+
+const json::string_property node::name (STATE_NODE_PREFIX "name");
+const json::string_property node::type (STATE_NODE_PREFIX "type");
+const json::json_property node::value (STATE_NODE_PREFIX "value");
+const json::string_property node::value_str (STATE_NODE_PREFIX "value_str");
+
+const json::string_property node::index (STATE_NODE_PREFIX "index");
+
+const json::string_property node::bits (STATE_NODE_PREFIX "bits");
+
+const json::string_property node::num_bits (STATE_NODE_PREFIX "num_bits");
+
+const json::string_property node::deallocator (STATE_NODE_PREFIX "deallocator");
+
+const json::string_property node::expected_deallocators
+  (STATE_NODE_PREFIX "expected-deallocators");
+
+const json::enum_property<enum node::dynalloc_state> node::dynalloc_state
+  (STATE_NODE_PREFIX "dynalloc-state");
+
+#undef STATE_NODE_PREFIX
+
+
+/* edge.  */
+namespace edge_props = custom_sarif_properties::state_graphs::edge;
+#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/"
+extern const char *const edge_props::prefix = STATE_EDGE_PREFIX;
+#undef STATE_EDGE_PREFIX
+
+// Traits for enum node:kind
+
+template<>
+enum node::kind
+json::enum_traits<enum node::kind>::get_unknown_value ()
+{
+  return node::kind::other;
+}
+
+static const char * const node_kind_strs[] = {
+  "globals",
+  "code",
+  "function",
+  "stack",
+  "stack-frame",
+  "heap",
+  "thread-local",
+  "dynalloc-buffer",
+  "variable",
+  "field",
+  "padding",
+  "element",
+  "other",
+};
+
+template<>
+bool
+json::enum_traits<enum node::kind>::
+maybe_get_value_from_string (const char *str,
+                            enum_t &out)
+{
+  for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i)
+    if (!strcmp (node_kind_strs[i], str))
+      {
+       out = static_cast<enum_t> (i);
+       return true;
+      }
+  return false;
+}
+
+template<>
+const char *
+json::enum_traits<enum node::kind>::get_string_for_value (enum_t value)
+{
+  return node_kind_strs[static_cast<int> (value)];
+}
+
+// Traits for enum node:dynalloc_state
+
+template<>
+enum node::dynalloc_state
+json::enum_traits<enum node::dynalloc_state>::get_unknown_value ()
+{
+  return node::dynalloc_state::unknown;
+}
+
+static const char * const dynalloc_state_strs[] = {
+  "unknown",
+  "nonnull",
+  "unchecked",
+  "freed"
+};
+
+template<>
+bool
+json::enum_traits<enum node::dynalloc_state>::
+maybe_get_value_from_string (const char *str,
+                            enum_t &out)
+{
+  for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i)
+    if (!strcmp (dynalloc_state_strs[i], str))
+      {
+       out = static_cast<enum_t> (i);
+       return true;
+      }
+  return false;
+}
+
+template<>
+const char *
+json::enum_traits<enum node::dynalloc_state>::
+get_string_for_value (enum_t value)
+{
+  return dynalloc_state_strs[static_cast <size_t> (value)];
+}
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..6ae9ad8
--- /dev/null
@@ -0,0 +1,97 @@
+/* Properties for capturing state graphs in SARIF property bags.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "json.h"
+
+#ifndef GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
+#define GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
+
+/* SARIF property names relating to GCC's CFGs.  */
+
+namespace custom_sarif_properties {
+  namespace state_graphs {
+    namespace graph {
+      extern const char *const prefix;
+    }
+    namespace node {
+
+      enum class kind
+      {
+       // Memory regions
+       globals,
+       code,
+       function, // code within a particular function
+       stack,
+       stack_frame,
+       heap_,
+       thread_local_,
+
+       /* Dynamically-allocated buffer,
+          on heap or stack (depending on parent).  */
+       dynalloc_buffer,
+
+       variable,
+
+       field, // field within a struct or union
+       padding, // padding bits in a struct or union
+       element, // element within an array
+
+       other // anything else
+      };
+
+      enum class dynalloc_state
+      {
+       unknown,
+       nonnull,
+       unchecked,
+       freed
+      };
+
+      extern const json::enum_property<enum kind> kind;
+
+      extern const json::string_property function;
+      extern const json::string_property dynamic_extents;
+      extern const json::string_property name;
+      extern const json::string_property type;
+      /* The value of a memory region, expressed as a json::value.  */
+      extern const json::json_property value;
+      /* The value of a memory region, expressed as a string.  */
+      extern const json::string_property value_str;
+
+      /* For element nodes, the index within the array.  */
+      extern const json::string_property index;
+
+      /* The range of bits or bytes within the base region.  */
+      extern const json::string_property bits;
+
+      /* The size of a padding region.  */
+      extern const json::string_property num_bits;
+
+      extern const json::string_property deallocator;
+      extern const json::string_property expected_deallocators;
+      extern const json::enum_property<enum dynalloc_state> dynalloc_state;
+    }
+    namespace edge {
+      extern const char *const prefix;
+    }
+  }
+}
+
+#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */
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..60d3e8ccfc7a4b8370061a753904d1024e27b429 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),
+            state_node_properties::kind::other);
+  node.set_property (state_node_properties::kind,
+                    state_node_properties::kind::stack);
+  ASSERT_EQ (node.get_property (state_node_properties::kind),
+            state_node_properties::kind::stack);
+
+  ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
+            state_node_properties::dynalloc_state::unknown);
+  node.set_property (state_node_properties::dynalloc_state,
+                    state_node_properties::dynalloc_state::freed);
+  ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
+            state_node_properties::dynalloc_state::freed);
+
+  ASSERT_EQ (node.get_property (state_node_properties::type), nullptr);
+  node.set_property (state_node_properties::type, "const char *");
+  ASSERT_STREQ (node.get_property (state_node_properties::type),
+               "const char *");
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -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..8a3ad246d1aaae6e6b7c2ab15c30a970ec12d66a 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 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::unknown:
+    case state_node_properties::dynalloc_state::nonnull:
       return nullptr;
 
-    case node_dynalloc_state::unchecked:
+    case state_node_properties::dynalloc_state::unchecked:
       return "#ec7a08"; // pf-orange-400
 
-    case node_dynalloc_state::freed:
+    case state_node_properties::dynalloc_state::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 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));
 
        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))
       {
       default:
        return false;
 
-      case node_kind::stack:
+      case state_node_properties::kind::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::dynalloc_buffer:
+      case state_node_properties::kind::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))
       {
       default:
        return nullptr;
 
-      case node_kind::globals:
+      case state_node_properties::kind::globals:
        return _("Globals");
-      case node_kind::code:
+      case state_node_properties::kind::code:
        return _("Code");
-      case node_kind::stack:
+      case state_node_properties::kind::stack:
        return _("Stack");
-      case node_kind::heap_:
+      case state_node_properties::kind::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 dynalloc_state)
   {
     xp.push_tag ("tr", true);
     xp.push_tag ("td", false);
@@ -298,48 +302,49 @@ 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);
 
     switch (input_node_kind)
       {
-      case node_kind::padding:
-      case node_kind::other:
+      case state_node_properties::kind::padding:
+      case state_node_properties::kind::other:
        return;
 
-      case node_kind::stack:
+      case state_node_properties::kind::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::unknown);
        break;
-      case node_kind::stack_frame:
+      case state_node_properties::kind::stack_frame:
        if (auto logical_loc = state_node.get_logical_loc ())
          if (const char *function
                = m_logical_loc_mgr.get_short_name (logical_loc))
            add_title_tr (id_of_dot_node, xp, num_columns, state_node,
                          std::string ("Frame: ") + function,
                          style::h2,
-                         node_dynalloc_state::unknown);
+                         state_node_properties::dynalloc_state::unknown);
        break;
-      case node_kind::dynalloc_buffer:
+      case state_node_properties::kind::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 dynalloc_st
+           = state_node.get_property (state_node_properties::dynalloc_state);
+         const char *extents
+           = state_node.get_property (state_node_properties::dynamic_extents);
+         const char *type = state_node.get_property (state_node_properties::type);
          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::unknown:
+           case state_node_properties::dynalloc_state::nonnull:
              if (type)
                {
                if (extents)
@@ -356,7 +361,7 @@ private:
                }
              break;
 
-           case node_dynalloc_state::unchecked:
+           case state_node_properties::dynalloc_state::unchecked:
              if (type)
                {
                  if (extents)
@@ -371,7 +376,7 @@ private:
                }
              break;
 
-           case node_dynalloc_state::freed:
+           case state_node_properties::dynalloc_state::freed:
              // TODO: show deallocator
              // TODO: show deallocation event
              pp_printf (&pp, "Freed buffer");
@@ -404,9 +409,10 @@ private:
            {
            default:
              break;
-           case node_kind::variable:
+           case state_node_properties::kind::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 +422,10 @@ private:
                xp.pop_tag ("td");
              }
              break;
-           case node_kind::element:
+           case state_node_properties::kind::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 +437,10 @@ private:
                xp.pop_tag ("td");
              }
              break;
-           case node_kind::field:
+           case state_node_properties::kind::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 +453,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 +464,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 +476,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 +508,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 +518,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 +528,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 +538,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 +546,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 492ca2914323f0a2d771322e47991acc6ae02727..81a495b416f0b0f4c32a17f4bd280d2071cc21c9 100644 (file)
@@ -6271,16 +6271,16 @@ These are visible by pressing ``j'' and ``k'' to single-step forward and
 backward through events.  Enabling this option will slow down
 HTML generation.
 
-@item show-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");