LCOV - code coverage report
Current view: top level - gcc/diagnostics - digraphs.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 66.3 % 187 124
Test Date: 2026-02-28 14:20:25 Functions: 60.7 % 28 17
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Directed graphs associated with a diagnostic.
       2              :    Copyright (C) 2025-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #define INCLUDE_ALGORITHM
      22              : #define INCLUDE_MAP
      23              : #define INCLUDE_SET
      24              : #define INCLUDE_STRING
      25              : #define INCLUDE_VECTOR
      26              : #include "config.h"
      27              : #include "system.h"
      28              : #include "coretypes.h"
      29              : 
      30              : #include "graphviz.h"
      31              : #include "diagnostics/digraphs.h"
      32              : #include "diagnostics/digraphs-to-dot.h"
      33              : #include "diagnostics/sarif-sink.h"
      34              : #include "custom-sarif-properties/digraphs.h"
      35              : 
      36              : using digraph_object = diagnostics::digraphs::object;
      37              : using digraph = diagnostics::digraphs::digraph;
      38              : using digraph_node = diagnostics::digraphs::node;
      39              : using digraph_edge = diagnostics::digraphs::edge;
      40              : 
      41              : namespace properties = custom_sarif_properties::digraphs;
      42              : 
      43              : // class object
      44              : 
      45              : /* String properties.  */
      46              : 
      47              : const char *
      48          943 : digraph_object::get_property (const json::string_property &property) const
      49              : {
      50          943 :   if (!m_property_bag)
      51              :     return nullptr;
      52          936 :   if (json::value *jv = m_property_bag->get (property.m_key.get ()))
      53          741 :     if (json::string *jstr = jv->dyn_cast_string ())
      54          741 :       return jstr->get_string ();
      55              :   return nullptr;
      56              : }
      57              : 
      58              : void
      59         1886 : digraph_object::set_property (const json::string_property &property,
      60              :                               const char *utf8_value)
      61              : {
      62         1886 :   auto &bag = ensure_property_bag ();
      63         1886 :   bag.set_string (property.m_key.get (), utf8_value);
      64         1886 : }
      65              : 
      66              : /* Integer properties.  */
      67              : 
      68              : bool
      69          450 : digraph_object::maybe_get_property (const json::integer_property &property,
      70              :                                     long &out_value) const
      71              : {
      72          450 :   if (!m_property_bag)
      73              :     return false;
      74          450 :   if (json::value *jv = m_property_bag->get (property.m_key.get ()))
      75          128 :     if (json::integer_number *jnum = jv->dyn_cast_integer_number ())
      76              :       {
      77          128 :         out_value = jnum->get ();
      78          128 :         return true;
      79              :       }
      80              :   return false;
      81              : }
      82              : 
      83              : void
      84          186 : digraph_object::set_property (const json::integer_property &property, long value)
      85              : {
      86          186 :   auto &bag = ensure_property_bag ();
      87          186 :   bag.set_integer (property.m_key.get (), value);
      88          186 : }
      89              : 
      90              : /* Bool properties.  */
      91              : void
      92            0 : digraph_object::set_property (const json::bool_property &property, bool value)
      93              : {
      94            0 :   auto &bag = ensure_property_bag ();
      95            0 :   bag.set_bool (property.m_key.get (), value);  
      96            0 : }
      97              : 
      98              : tristate
      99            0 : digraph_object::
     100              : get_property_as_tristate (const json::bool_property &property) const
     101              : {
     102            0 :   if (m_property_bag)
     103              :     {
     104            0 :       if (json::value *jv = m_property_bag->get (property.m_key.get ()))
     105            0 :         switch (jv->get_kind ())
     106              :           {
     107              :           default:
     108              :             break;
     109            0 :           case json::JSON_TRUE:
     110            0 :             return tristate (true);
     111            0 :           case json::JSON_FALSE:
     112            0 :             return tristate (false);
     113              :           }
     114              :     }
     115            0 :   return tristate::unknown ();
     116              : }
     117              : 
     118              : /* Array-of-string properties.  */
     119              : json::array *
     120         2380 : digraph_object::get_property (const json::array_of_string_property &property) const
     121              : {
     122         2380 :   if (m_property_bag)
     123         2380 :     if (json::value *jv = m_property_bag->get (property.m_key.get ()))
     124         2100 :       if (json::array *arr = jv->dyn_cast_array ())
     125              :         return arr;
     126              :   return nullptr;
     127              : }
     128              : 
     129              : /* json::value properties.  */
     130              : const json::value *
     131            0 : digraph_object::get_property (const json::json_property &property) const
     132              : {
     133            0 :   if (m_property_bag)
     134            0 :     return m_property_bag->get (property.m_key.get ());
     135              :   return nullptr;
     136              : }
     137              : 
     138              : void
     139          224 : digraph_object::set_property (const json::json_property &property,
     140              :                               std::unique_ptr<json::value> value)
     141              : {
     142          224 :   auto &bag = ensure_property_bag ();
     143          224 :   bag.set (property.m_key.get (), std::move (value));
     144          224 : }
     145              : 
     146              : json::object &
     147         3931 : digraph_object::ensure_property_bag ()
     148              : {
     149         3931 :   if (!m_property_bag)
     150         1810 :     m_property_bag = std::make_unique<sarif_property_bag> ( );
     151         3931 :   return *m_property_bag;
     152              : }
     153              : 
     154              : // class digraph
     155              : 
     156              : DEBUG_FUNCTION void
     157            0 : digraph::dump () const
     158              : {
     159            0 :   make_json_sarif_graph ()->dump ();
     160            0 : }
     161              : 
     162              : std::unique_ptr<json::object>
     163            8 : digraph::make_json_sarif_graph () const
     164              : {
     165            8 :   return make_sarif_graph (*this, nullptr, nullptr);
     166              : }
     167              : 
     168              : std::unique_ptr<dot::graph>
     169           56 : digraph::make_dot_graph () const
     170              : {
     171           56 :   auto converter = to_dot::converter::make (*this);
     172           56 :   return converter->make_dot_graph_from_diagnostic_graph (*this);
     173           56 : }
     174              : 
     175              : std::unique_ptr<digraph>
     176            0 : digraph::clone () const
     177              : {
     178            0 :   auto result = std::make_unique<diagnostics::digraphs::digraph> ();
     179              : 
     180            0 :   if (get_property_bag ())
     181            0 :     result->set_property_bag (get_property_bag ()->clone_as_object ());
     182              : 
     183            0 :   std::map<digraph_node *, digraph_node *> node_mapping;
     184              : 
     185            0 :   for (auto &iter : m_nodes)
     186            0 :     result->add_node (iter->clone (*result, node_mapping));
     187            0 :   for (auto &iter : m_edges)
     188            0 :     result->add_edge (iter->clone (*result, node_mapping));
     189              : 
     190            0 :   return result;
     191            0 : }
     192              : 
     193              : void
     194          828 : digraph::add_edge (const char *id,
     195              :                    node &src_node,
     196              :                    node &dst_node,
     197              :                    const char *label)
     198              : {
     199          828 :   auto e = std::make_unique<digraph_edge> (*this,
     200              :                                            id,
     201              :                                            src_node,
     202          828 :                                            dst_node);
     203          828 :   if (label)
     204          780 :     e->set_label (label);
     205          828 :   add_edge (std::move (e));
     206          828 : }
     207              : 
     208              : /* Utility function for edge ids: either use EDGE_ID, or
     209              :    generate a unique one for when we don't care about the name.
     210              : 
     211              :    Edges in SARIF "SHALL" have an id that's unique within the graph
     212              :    (SARIF 2.1.0 §3.41.2).  This is so that graph traversals can refer
     213              :    to edges by id (SARIF 2.1.0's §3.43.2 edgeId property).  */
     214              : 
     215              : std::string
     216         1376 : digraph::make_edge_id (const char *edge_id)
     217              : {
     218              :   /* If we have an id, use it.  */
     219         1376 :   if (edge_id)
     220            4 :     return edge_id;
     221              : 
     222              :   /* Otherwise, generate a unique one of the form "edgeN".  */
     223         1372 :   while (true)
     224              :     {
     225         2744 :       auto candidate (std::string ("edge")
     226         1372 :                       + std::to_string (m_next_edge_id_index++));
     227         1372 :       auto iter = m_id_to_edge_map.find (candidate);
     228         1372 :       if (iter != m_id_to_edge_map.end ())
     229              :         {
     230              :           // Try again with the next index...
     231            0 :           continue;
     232              :         }
     233         1372 :       return candidate;
     234         1372 :     }
     235              : }
     236              : 
     237              : const char *
     238           56 : digraph::get_graph_kind () const
     239              : {
     240           56 :   return get_property (properties::digraph::kind);
     241              : }
     242              : 
     243              : void
     244           90 : digraph::set_graph_kind (const char *kind)
     245              : {
     246           90 :   set_property (properties::digraph::kind, kind);
     247           90 : }
     248              : 
     249              : // class node
     250              : 
     251              : DEBUG_FUNCTION void
     252            0 : digraph_node::dump () const
     253              : {
     254            0 :   to_json_sarif_node ()->dump ();
     255            0 : }
     256              : 
     257              : std::unique_ptr<json::object>
     258            0 : digraph_node::to_json_sarif_node () const
     259              : {
     260            0 :   return make_sarif_node (*this, nullptr, nullptr);
     261              : }
     262              : 
     263              : std::unique_ptr<digraph_node>
     264            0 : digraph_node::clone (digraph &new_graph,
     265              :                      std::map<node *, node *> &node_mapping) const
     266              : {
     267            0 :   auto result
     268            0 :     = std::make_unique<digraph_node> (new_graph, get_id ());
     269            0 :   node_mapping.insert ({const_cast <node *> (this), result.get ()});
     270              : 
     271            0 :   result->set_logical_loc (m_logical_loc);
     272              : 
     273            0 :   if (get_property_bag ())
     274            0 :     result->set_property_bag (get_property_bag ()->clone_as_object ());
     275              : 
     276            0 :   for (auto &iter : m_children)
     277            0 :     result->add_child (iter->clone (new_graph, node_mapping));
     278              : 
     279            0 :   return result;
     280              : }
     281              : 
     282              : // class edge
     283              : 
     284              : std::unique_ptr<digraph_edge>
     285            0 : digraph_edge::clone (digraph &new_graph,
     286              :                      const std::map<node *, node *> &node_mapping) const
     287              : {
     288            0 :   auto iter_new_src = node_mapping.find (&m_src_node);
     289            0 :   gcc_assert (iter_new_src != node_mapping.end ());
     290            0 :   auto iter_new_dst = node_mapping.find (&m_dst_node);
     291            0 :   gcc_assert (iter_new_dst != node_mapping.end ());
     292            0 :   auto result
     293              :     = std::make_unique<digraph_edge> (new_graph,
     294            0 :                                       m_id.c_str (),
     295            0 :                                       *iter_new_src->second,
     296            0 :                                       *iter_new_dst->second);
     297            0 :   if (get_property_bag ())
     298            0 :     result->set_property_bag (get_property_bag ()->clone_as_object ());
     299              : 
     300            0 :   return result;
     301              : }
     302              : 
     303              : DEBUG_FUNCTION void
     304            0 : diagnostics::digraphs::edge::dump () const
     305              : {
     306            0 :   to_json_sarif_edge ()->dump ();
     307            0 : }
     308              : 
     309              : std::unique_ptr<json::object>
     310            0 : diagnostics::digraphs::edge::to_json_sarif_edge () const
     311              : {
     312            0 :   return make_sarif_edge (*this, nullptr);
     313              : }
     314              : 
     315              : #if CHECKING_P
     316              : 
     317              : #include "selftest.h"
     318              : #include "custom-sarif-properties/state-graphs.h"
     319              : 
     320              : namespace diagnostics {
     321              : namespace selftest {
     322              : 
     323              : static void
     324            4 : test_empty_graph ()
     325              : {
     326            4 :   digraph g;
     327              : 
     328            4 :   {
     329            4 :     auto sarif = g.make_json_sarif_graph ();
     330              : 
     331            4 :     pretty_printer pp;
     332            4 :     sarif->print (&pp, true);
     333            4 :     ASSERT_STREQ
     334              :       (pp_formatted_text (&pp),
     335              :        ("{\"nodes\": [],\n"
     336              :         " \"edges\": []}"));
     337            4 :   }
     338              : 
     339            4 :   {
     340            4 :     auto dg = g.make_dot_graph ();
     341              : 
     342            4 :     pretty_printer pp;
     343            4 :     dot::writer w (pp);
     344            4 :     dg->print (w);
     345            4 :     ASSERT_STREQ
     346              :       (pp_formatted_text (&pp),
     347              :        ("digraph {\n"
     348              :         "}\n"));
     349            4 :   }
     350            4 : }
     351              : 
     352              : static void
     353            4 : test_simple_graph ()
     354              : {
     355              : #define KEY_PREFIX "/placeholder/"
     356            4 :   auto g = std::make_unique<digraph> ();
     357            4 :   g->set_description ("test graph");
     358            4 :   g->set_property (json::string_property (KEY_PREFIX, "date"), "1066");
     359              : 
     360            4 :   auto a = std::make_unique<digraph_node> (*g, "a");
     361            4 :   auto b = std::make_unique<digraph_node> (*g, "b");
     362            4 :   b->set_property (json::string_property (KEY_PREFIX, "color"), "red");
     363            4 :   auto c = std::make_unique<digraph_node> (*g, "c");
     364            4 :   c->set_label ("I am a node label");
     365              : 
     366            4 :   auto e = std::make_unique<digraph_edge> (*g, nullptr, *a, *c);
     367            4 :   e->set_property (json::string_property (KEY_PREFIX, "status"),
     368              :                    "copacetic");
     369            4 :   e->set_label ("I am an edge label");
     370            4 :   g->add_edge (std::move (e));
     371              : 
     372            4 :   g->add_node (std::move (a));
     373              : 
     374            4 :   b->add_child (std::move (c));
     375            4 :   g->add_node (std::move (b));
     376              : #undef KEY_PREFIX
     377              : 
     378            4 :   {
     379            4 :     auto sarif = g->make_json_sarif_graph ();
     380              : 
     381            4 :     pretty_printer pp;
     382            4 :     sarif->print (&pp, true);
     383            4 :     ASSERT_STREQ
     384              :       (pp_formatted_text (&pp),
     385              :        ("{\"properties\": {\"/placeholder/date\": \"1066\"},\n"
     386              :         " \"nodes\": [{\"id\": \"a\"},\n"
     387              :         "           {\"id\": \"b\",\n"
     388              :         "            \"properties\": {\"/placeholder/color\": \"red\"},\n"
     389              :         "            \"children\": [{\"id\": \"c\"}]}],\n"
     390              :         " \"edges\": [{\"id\": \"edge0\",\n"
     391              :         "            \"properties\": {\"/placeholder/status\": \"copacetic\"},\n"
     392              :         "            \"sourceNodeId\": \"a\",\n"
     393              :         "            \"targetNodeId\": \"c\"}]}"));
     394            4 :   }
     395              : 
     396            4 :   {
     397            4 :     auto dg = g->make_dot_graph ();
     398              : 
     399            4 :     pretty_printer pp;
     400            4 :     dot::writer w (pp);
     401            4 :     dg->print (w);
     402            4 :     ASSERT_STREQ
     403              :       (pp_formatted_text (&pp),
     404              :        ("digraph {\n"
     405              :         "    label=\"test graph\";\n"
     406              :         "    a;\n"
     407              :         "    \n"
     408              :         "    subgraph cluster_b {\n"
     409              :         "        c [label=\"I am a node label\"];\n"
     410              :         "\n"
     411              :         "    };\n"
     412              :         "    a -> c [label=\"I am an edge label\"];\n"
     413              :         "}\n"));
     414            4 :   }
     415            4 : }
     416              : 
     417              : static void
     418            4 : test_property_objects ()
     419              : {
     420            4 :   namespace state_node_properties = custom_sarif_properties::state_graphs::node;
     421              : 
     422            4 :   digraph g;
     423            4 :   digraph_node node (g, "a");
     424              : 
     425            4 :   ASSERT_EQ (node.get_property (state_node_properties::kind_prop),
     426              :              state_node_properties::kind_t::other);
     427            4 :   node.set_property (state_node_properties::kind_prop,
     428              :                      state_node_properties::kind_t::stack);
     429            4 :   ASSERT_EQ (node.get_property (state_node_properties::kind_prop),
     430              :              state_node_properties::kind_t::stack);
     431              : 
     432            4 :   ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
     433              :              state_node_properties::dynalloc_state_t::unknown);
     434            4 :   node.set_property (state_node_properties::dynalloc_state_prop,
     435              :                      state_node_properties::dynalloc_state_t::freed);
     436            4 :   ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
     437              :              state_node_properties::dynalloc_state_t::freed);
     438              : 
     439            4 :   ASSERT_EQ (node.get_property (state_node_properties::type), nullptr);
     440            4 :   node.set_property (state_node_properties::type, "const char *");
     441            4 :   ASSERT_STREQ (node.get_property (state_node_properties::type),
     442              :                 "const char *");
     443            4 : }
     444              : 
     445              : /* Run all of the selftests within this file.  */
     446              : 
     447              : void
     448            4 : digraphs_cc_tests ()
     449              : {
     450            4 :   test_empty_graph ();
     451            4 :   test_simple_graph ();
     452            4 :   test_property_objects ();
     453            4 : }
     454              : 
     455              : } // namespace diagnostics::selftest
     456              : } // namespace diagnostics
     457              : 
     458              : #endif /* CHECKING_P */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.