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 under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : 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 : #ifndef GCC_DIAGNOSTICS_DIGRAPHS_H
22 : #define GCC_DIAGNOSTICS_DIGRAPHS_H
23 :
24 : #include "json.h"
25 : #include "tristate.h"
26 : #include "diagnostics/logical-locations.h"
27 :
28 : class graphviz_out;
29 :
30 : class sarif_graph;
31 : class sarif_node;
32 : class sarif_edge;
33 :
34 : namespace dot { class graph; }
35 :
36 : namespace diagnostics {
37 : namespace digraphs {
38 :
39 : /* A family of classes: digraph, node, and edge, closely related to
40 : SARIF's graph, node, and edge types (SARIF v2.1.0 sections 3.39-3.41).
41 :
42 : Nodes can have child nodes, allowing for arbitrarily deep nesting.
43 : Edges can be between any pair of nodes (potentially at different
44 : nesting levels).
45 :
46 : Digraphs, nodes, and edges also optionally have a JSON property bag,
47 : allowing round-tripping of arbitrary key/value pairs through SARIF. */
48 :
49 : class digraph;
50 : class node;
51 : class edge;
52 :
53 : /* A base class for digraph, node, and edge to allow them to have
54 : an optional JSON property bag. */
55 :
56 6940 : class object
57 : {
58 : public:
59 : /* String properties. */
60 : const char *get_property (const json::string_property &property) const;
61 : void set_property (const json::string_property &property,
62 : const char *utf8_value);
63 :
64 : /* Integer properties. */
65 : bool maybe_get_property (const json::integer_property &property, long &out) const;
66 : void set_property (const json::integer_property &property, long value);
67 :
68 : /* Bool properties. */
69 : tristate
70 : get_property_as_tristate (const json::bool_property &property) const;
71 : void set_property (const json::bool_property &property, bool value);
72 :
73 : /* Array-of-string properties. */
74 : json::array *
75 : get_property (const json::array_of_string_property &property) const;
76 :
77 : /* enum properties. */
78 : template <typename EnumType>
79 : EnumType
80 28 : get_property (const json::enum_property<EnumType> &property) const
81 : {
82 28 : if (m_property_bag)
83 : {
84 : EnumType result;
85 24 : if (m_property_bag->maybe_get_enum<EnumType> (property, result))
86 20 : return result;
87 : }
88 8 : return json::enum_traits<EnumType>::get_unknown_value ();
89 : }
90 : template <typename EnumType>
91 : void
92 555 : set_property (const json::enum_property<EnumType> &property,
93 : EnumType value)
94 : {
95 555 : auto &bag = ensure_property_bag ();
96 555 : bag.set_enum<EnumType> (property, value);
97 555 : }
98 :
99 : /* json::value properties. */
100 : const json::value *get_property (const json::json_property &property) const;
101 : void set_property (const json::json_property &property,
102 : std::unique_ptr<json::value> value);
103 :
104 : json::object *
105 2021 : get_property_bag () const { return m_property_bag.get (); }
106 :
107 : json::object &
108 : ensure_property_bag ();
109 :
110 : void
111 0 : set_property_bag (std::unique_ptr<json::object> property_bag)
112 : {
113 0 : m_property_bag = std::move (property_bag);
114 : }
115 :
116 : private:
117 : std::unique_ptr<json::object> m_property_bag;
118 : };
119 :
120 : // A directed graph, corresponding to SARIF v2.1.0 section 3.39.
121 :
122 : class digraph : public object
123 : {
124 : public:
125 : friend class node;
126 : friend class edge;
127 :
128 130 : digraph () : m_next_edge_id_index (0) {}
129 236 : virtual ~digraph () {}
130 :
131 : const char *
132 187 : get_description () const
133 : {
134 187 : if (!m_description)
135 : return nullptr;
136 152 : return m_description->c_str ();
137 : }
138 :
139 : void
140 94 : set_description (const char *desc)
141 : {
142 94 : if (desc)
143 94 : m_description = std::make_unique<std::string> (desc);
144 : else
145 0 : m_description = nullptr;
146 94 : }
147 : void
148 : set_description (std::string desc)
149 : {
150 : m_description = std::make_unique<std::string> (std::move (desc));
151 : }
152 :
153 : node *
154 : get_node_by_id (const char *id) const
155 : {
156 : auto iter = m_id_to_node_map.find (id);
157 : if (iter == m_id_to_node_map.end ())
158 : return nullptr;
159 : return iter->second;
160 : }
161 :
162 : edge *
163 : get_edge_by_id (const char *id) const
164 : {
165 : auto iter = m_id_to_edge_map.find (id);
166 : if (iter == m_id_to_edge_map.end ())
167 : return nullptr;
168 : return iter->second;
169 : }
170 :
171 : size_t
172 141 : get_num_nodes () const
173 : {
174 141 : return m_nodes.size ();
175 : }
176 :
177 : node &
178 194 : get_node (size_t idx) const
179 : {
180 194 : return *m_nodes[idx].get ();
181 : }
182 :
183 : size_t
184 141 : get_num_edges () const
185 : {
186 141 : return m_edges.size ();
187 : }
188 :
189 : edge &
190 2046 : get_edge (size_t idx) const
191 : {
192 2046 : return *m_edges[idx].get ();
193 : }
194 :
195 : void
196 : dump () const;
197 :
198 : std::unique_ptr<json::object>
199 : make_json_sarif_graph () const;
200 :
201 : std::unique_ptr<dot::graph>
202 : make_dot_graph () const;
203 :
204 : void
205 168 : add_node (std::unique_ptr<node> n)
206 : {
207 168 : gcc_assert (n);
208 168 : m_nodes.push_back (std::move (n));
209 168 : }
210 :
211 : void
212 1372 : add_edge (std::unique_ptr<edge> e)
213 : {
214 1372 : gcc_assert (e);
215 1372 : m_edges.push_back (std::move (e));
216 1372 : }
217 :
218 : void
219 : add_edge (const char *id,
220 : node &src_node,
221 : node &dst_node,
222 : const char *label = nullptr);
223 :
224 : std::unique_ptr<digraph> clone () const;
225 :
226 : const char *get_graph_kind () const;
227 : void set_graph_kind (const char *);
228 :
229 : const std::map<std::string, node *> &
230 : get_all_nodes () const
231 : {
232 90 : return m_id_to_node_map;
233 : }
234 :
235 : private:
236 : void
237 1961 : add_node_id (std::string node_id, node &new_node)
238 : {
239 1961 : m_id_to_node_map.insert ({std::move (node_id), &new_node});
240 1961 : }
241 : void
242 1376 : add_edge_id (std::string edge_id, edge &new_edge)
243 : {
244 1376 : m_id_to_edge_map.insert ({std::move (edge_id), &new_edge});
245 1376 : }
246 :
247 : std::string
248 : make_edge_id (const char *edge_id);
249 :
250 : std::unique_ptr<std::string> m_description;
251 : std::map<std::string, node *> m_id_to_node_map;
252 : std::map<std::string, edge *> m_id_to_edge_map;
253 : std::vector<std::unique_ptr<node>> m_nodes;
254 : std::vector<std::unique_ptr<edge>> m_edges;
255 : size_t m_next_edge_id_index;
256 : };
257 :
258 : // A node in a directed graph, corresponding to SARIF v2.1.0 section 3.40.
259 :
260 : class node : public object
261 : {
262 : public:
263 3918 : virtual ~node () {}
264 :
265 1961 : node (digraph &g, std::string id)
266 3922 : : m_id (id),
267 3922 : m_physical_loc (UNKNOWN_LOCATION)
268 : {
269 1961 : g.add_node_id (std::move (id), *this);
270 1961 : }
271 : node (const node &) = delete;
272 :
273 : std::string
274 8190 : get_id () const { return m_id; }
275 :
276 : const char *
277 1957 : get_label () const
278 : {
279 1957 : if (!m_label)
280 : return nullptr;
281 812 : return m_label->c_str ();
282 : }
283 :
284 : void
285 4 : set_label (const char *label)
286 : {
287 4 : if (label)
288 4 : m_label = std::make_unique<std::string> (label);
289 : else
290 0 : m_label = nullptr;
291 4 : }
292 : void
293 : set_label (std::string label)
294 : {
295 : m_label = std::make_unique<std::string> (std::move (label));
296 : }
297 :
298 : size_t
299 1322 : get_num_children () const { return m_children.size (); }
300 :
301 : node &
302 1785 : get_child (size_t idx) const { return *m_children[idx].get (); }
303 :
304 : void
305 977 : add_child (std::unique_ptr<node> child)
306 : {
307 977 : gcc_assert (child);
308 977 : m_children.push_back (std::move (child));
309 977 : }
310 :
311 : location_t
312 1224 : get_physical_loc () const
313 : {
314 1224 : return m_physical_loc;
315 : }
316 :
317 : void
318 : set_physical_loc (location_t physical_loc)
319 : {
320 : m_physical_loc = physical_loc;
321 : }
322 :
323 : logical_locations::key
324 1263 : get_logical_loc () const
325 : {
326 1263 : return m_logical_loc;
327 : }
328 :
329 : void
330 39 : set_logical_loc (logical_locations::key logical_loc)
331 : {
332 39 : m_logical_loc = logical_loc;
333 : }
334 :
335 : void print (graphviz_out &gv) const;
336 :
337 : void
338 : dump () const;
339 :
340 : std::unique_ptr<json::object>
341 : to_json_sarif_node () const;
342 :
343 : std::unique_ptr<node>
344 : clone (digraph &new_graph,
345 : std::map<node *, node *> &node_mapping) const;
346 :
347 : private:
348 : std::string m_id;
349 : std::unique_ptr<std::string> m_label;
350 : std::vector<std::unique_ptr<node>> m_children;
351 : location_t m_physical_loc;
352 : logical_locations::key m_logical_loc;
353 : };
354 :
355 : // An edge in a directed graph, corresponding to SARIF v2.1.0 section 3.41.
356 :
357 : class edge : public object
358 : {
359 : public:
360 2752 : virtual ~edge () {}
361 :
362 : /* SARIF requires us to provide unique edge IDs within a graph,
363 : but otherwise we don't need them.
364 : Pass in nullptr for the id to get the graph to generate a unique
365 : edge id for us. */
366 1376 : edge (digraph &g,
367 : const char *id,
368 : node &src_node,
369 : node &dst_node)
370 1376 : : m_id (g.make_edge_id (id)),
371 1376 : m_src_node (src_node),
372 1376 : m_dst_node (dst_node)
373 : {
374 2752 : g.add_edge_id (m_id, *this);
375 1376 : }
376 :
377 : std::string
378 1428 : get_id () const { return m_id; }
379 :
380 : const char *
381 1380 : get_label () const
382 : {
383 1380 : if (!m_label)
384 : return nullptr;
385 792 : return m_label->c_str ();
386 : }
387 :
388 : void
389 784 : set_label (const char *label)
390 : {
391 784 : if (label)
392 784 : m_label = std::make_unique<std::string> (label);
393 : else
394 0 : m_label = nullptr;
395 784 : }
396 :
397 : node &
398 2046 : get_src_node () const { return m_src_node; }
399 :
400 : node &
401 2046 : get_dst_node () const { return m_dst_node; }
402 :
403 : void
404 : dump () const;
405 :
406 : std::unique_ptr<json::object>
407 : to_json_sarif_edge () const;
408 :
409 : std::unique_ptr<edge>
410 : clone (digraph &new_graph,
411 : const std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> &node_mapping) const;
412 :
413 : private:
414 : std::string m_id;
415 : std::unique_ptr<std::string> m_label;
416 : node &m_src_node;
417 : node &m_dst_node;
418 : };
419 :
420 : } // namespace digraphs
421 : } // namespace diagnostics
422 :
423 : #endif /* ! GCC_DIAGNOSTICS_DIGRAPHS_H */
|