Branch data Line data Source code
1 : : /* Directed graphs associated with a diagnostic.
2 : : Copyright (C) 2025 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 : 4480 : 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 : 594 : set_property (const json::enum_property<EnumType> &property,
93 : : EnumType value)
94 : : {
95 : 594 : auto &bag = ensure_property_bag ();
96 : 594 : bag.set_enum<EnumType> (property, value);
97 : 594 : }
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 : 1425 : 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 : 40 : digraph () : m_next_edge_id_index (0) {}
129 : 56 : virtual ~digraph () {}
130 : :
131 : : const char *
132 : 52 : get_description () const
133 : : {
134 : 52 : if (!m_description)
135 : : return nullptr;
136 : 17 : return m_description->c_str ();
137 : : }
138 : :
139 : : void
140 : 4 : set_description (const char *desc)
141 : : {
142 : 4 : if (desc)
143 : 4 : m_description = std::make_unique<std::string> (desc);
144 : : else
145 : 0 : m_description = nullptr;
146 : 4 : }
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 : 51 : get_num_nodes () const
173 : : {
174 : 51 : return m_nodes.size ();
175 : : }
176 : :
177 : : node &
178 : 104 : get_node (size_t idx) const
179 : : {
180 : 104 : return *m_nodes[idx].get ();
181 : : }
182 : :
183 : : size_t
184 : 51 : get_num_edges () const
185 : : {
186 : 51 : return m_edges.size ();
187 : : }
188 : :
189 : : edge &
190 : 1229 : get_edge (size_t idx) const
191 : : {
192 : 1229 : 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 : 78 : add_node (std::unique_ptr<node> n)
206 : : {
207 : 78 : gcc_assert (n);
208 : 78 : m_nodes.push_back (std::move (n));
209 : 78 : }
210 : :
211 : : void
212 : 827 : add_edge (std::unique_ptr<edge> e)
213 : : {
214 : 827 : gcc_assert (e);
215 : 827 : m_edges.push_back (std::move (e));
216 : 827 : }
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 : : private:
230 : : void
231 : 1366 : add_node_id (std::string node_id, node &new_node)
232 : : {
233 : 1366 : m_id_to_node_map.insert ({std::move (node_id), &new_node});
234 : 1366 : }
235 : : void
236 : 831 : add_edge_id (std::string edge_id, edge &new_edge)
237 : : {
238 : 831 : m_id_to_edge_map.insert ({std::move (edge_id), &new_edge});
239 : 831 : }
240 : :
241 : : std::string
242 : : make_edge_id (const char *edge_id);
243 : :
244 : : std::unique_ptr<std::string> m_description;
245 : : std::map<std::string, node *> m_id_to_node_map;
246 : : std::map<std::string, edge *> m_id_to_edge_map;
247 : : std::vector<std::unique_ptr<node>> m_nodes;
248 : : std::vector<std::unique_ptr<edge>> m_edges;
249 : : size_t m_next_edge_id_index;
250 : : };
251 : :
252 : : // A node in a directed graph, corresponding to SARIF v2.1.0 section 3.40.
253 : :
254 : : class node : public object
255 : : {
256 : : public:
257 : 2728 : virtual ~node () {}
258 : :
259 : 1366 : node (digraph &g, std::string id)
260 : 2732 : : m_id (id),
261 : 2732 : m_physical_loc (UNKNOWN_LOCATION)
262 : : {
263 : 1366 : g.add_node_id (std::move (id), *this);
264 : 1366 : }
265 : : node (const node &) = delete;
266 : :
267 : : std::string
268 : 5186 : get_id () const { return m_id; }
269 : :
270 : : const char *
271 : 1362 : get_label () const
272 : : {
273 : 1362 : if (!m_label)
274 : : return nullptr;
275 : 808 : return m_label->c_str ();
276 : : }
277 : :
278 : : void
279 : 4 : set_label (const char *label)
280 : : {
281 : 4 : if (label)
282 : 4 : m_label = std::make_unique<std::string> (label);
283 : : else
284 : 0 : m_label = nullptr;
285 : 4 : }
286 : : void
287 : : set_label (std::string label)
288 : : {
289 : : m_label = std::make_unique<std::string> (std::move (label));
290 : : }
291 : :
292 : : size_t
293 : 999 : get_num_children () const { return m_children.size (); }
294 : :
295 : : node &
296 : 1280 : get_child (size_t idx) const { return *m_children[idx].get (); }
297 : :
298 : : void
299 : 476 : add_child (std::unique_ptr<node> child)
300 : : {
301 : 476 : gcc_assert (child);
302 : 476 : m_children.push_back (std::move (child));
303 : 476 : }
304 : :
305 : : location_t
306 : 946 : get_physical_loc () const
307 : : {
308 : 946 : return m_physical_loc;
309 : : }
310 : :
311 : : void
312 : : set_physical_loc (location_t physical_loc)
313 : : {
314 : : m_physical_loc = physical_loc;
315 : : }
316 : :
317 : : logical_locations::key
318 : 985 : get_logical_loc () const
319 : : {
320 : 985 : return m_logical_loc;
321 : : }
322 : :
323 : : void
324 : 39 : set_logical_loc (logical_locations::key logical_loc)
325 : : {
326 : 39 : m_logical_loc = logical_loc;
327 : : }
328 : :
329 : : void print (graphviz_out &gv) const;
330 : :
331 : : void
332 : : dump () const;
333 : :
334 : : std::unique_ptr<json::object>
335 : : to_json_sarif_node () const;
336 : :
337 : : std::unique_ptr<node>
338 : : clone (digraph &new_graph,
339 : : std::map<node *, node *> &node_mapping) const;
340 : :
341 : : private:
342 : : std::string m_id;
343 : : std::unique_ptr<std::string> m_label;
344 : : std::vector<std::unique_ptr<node>> m_children;
345 : : location_t m_physical_loc;
346 : : logical_locations::key m_logical_loc;
347 : : };
348 : :
349 : : // An edge in a directed graph, corresponding to SARIF v2.1.0 section 3.41.
350 : :
351 : : class edge : public object
352 : : {
353 : : public:
354 : 1662 : virtual ~edge () {}
355 : :
356 : : /* SARIF requires us to provide unique edge IDs within a graph,
357 : : but otherwise we don't need them.
358 : : Pass in nullptr for the id to get the graph to generate a unique
359 : : edge id for us. */
360 : 831 : edge (digraph &g,
361 : : const char *id,
362 : : node &src_node,
363 : : node &dst_node)
364 : 831 : : m_id (g.make_edge_id (id)),
365 : 831 : m_src_node (src_node),
366 : 831 : m_dst_node (dst_node)
367 : : {
368 : 1662 : g.add_edge_id (m_id, *this);
369 : 831 : }
370 : :
371 : : std::string
372 : 882 : get_id () const { return m_id; }
373 : :
374 : : const char *
375 : 835 : get_label () const
376 : : {
377 : 835 : if (!m_label)
378 : : return nullptr;
379 : 788 : return m_label->c_str ();
380 : : }
381 : :
382 : : void
383 : 780 : set_label (const char *label)
384 : : {
385 : 780 : if (label)
386 : 780 : m_label = std::make_unique<std::string> (label);
387 : : else
388 : 0 : m_label = nullptr;
389 : 780 : }
390 : :
391 : : node &
392 : 1229 : get_src_node () const { return m_src_node; }
393 : :
394 : : node &
395 : 1229 : get_dst_node () const { return m_dst_node; }
396 : :
397 : : void
398 : : dump () const;
399 : :
400 : : std::unique_ptr<json::object>
401 : : to_json_sarif_edge () const;
402 : :
403 : : std::unique_ptr<edge>
404 : : clone (digraph &new_graph,
405 : : const std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> &node_mapping) const;
406 : :
407 : : private:
408 : : std::string m_id;
409 : : std::unique_ptr<std::string> m_label;
410 : : node &m_src_node;
411 : : node &m_dst_node;
412 : : };
413 : :
414 : : } // namespace digraphs
415 : : } // namespace diagnostics
416 : :
417 : : #endif /* ! GCC_DIAGNOSTICS_DIGRAPHS_H */
|