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 */
|