gcc/gcc/diagnostics/digraphs.h

418 lines
9.0 KiB
C++

/* Directed graphs associated with a diagnostic.
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_DIAGNOSTICS_DIGRAPHS_H
#define GCC_DIAGNOSTICS_DIGRAPHS_H
#include "json.h"
#include "tristate.h"
#include "diagnostics/logical-locations.h"
class graphviz_out;
class sarif_graph;
class sarif_node;
class sarif_edge;
namespace dot { class graph; }
namespace diagnostics {
namespace digraphs {
/* A family of classes: digraph, node, and edge, closely related to
SARIF's graph, node, and edge types (SARIF v2.1.0 sections 3.39-3.41).
Nodes can have child nodes, allowing for arbitrarily deep nesting.
Edges can be between any pair of nodes (potentially at different
nesting levels).
Digraphs, nodes, and edges also optionally have a JSON property bag,
allowing round-tripping of arbitrary key/value pairs through SARIF. */
class digraph;
class node;
class edge;
/* A base class for digraph, node, and edge to allow them to have
an optional JSON property bag. */
class object
{
public:
/* 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_property (const json::enum_property<EnumType> &property,
EnumType value)
{
auto &bag = ensure_property_bag ();
bag.set_enum<EnumType> (property, 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)
{
m_property_bag = std::move (property_bag);
}
private:
std::unique_ptr<json::object> m_property_bag;
};
// A directed graph, corresponding to SARIF v2.1.0 section 3.39.
class digraph : public object
{
public:
friend class node;
friend class edge;
digraph () : m_next_edge_id_index (0) {}
virtual ~digraph () {}
const char *
get_description () const
{
if (!m_description)
return nullptr;
return m_description->c_str ();
}
void
set_description (const char *desc)
{
if (desc)
m_description = std::make_unique<std::string> (desc);
else
m_description = nullptr;
}
void
set_description (std::string desc)
{
m_description = std::make_unique<std::string> (std::move (desc));
}
node *
get_node_by_id (const char *id) const
{
auto iter = m_id_to_node_map.find (id);
if (iter == m_id_to_node_map.end ())
return nullptr;
return iter->second;
}
edge *
get_edge_by_id (const char *id) const
{
auto iter = m_id_to_edge_map.find (id);
if (iter == m_id_to_edge_map.end ())
return nullptr;
return iter->second;
}
size_t
get_num_nodes () const
{
return m_nodes.size ();
}
node &
get_node (size_t idx) const
{
return *m_nodes[idx].get ();
}
size_t
get_num_edges () const
{
return m_edges.size ();
}
edge &
get_edge (size_t idx) const
{
return *m_edges[idx].get ();
}
void
dump () const;
std::unique_ptr<json::object>
make_json_sarif_graph () const;
std::unique_ptr<dot::graph>
make_dot_graph () const;
void
add_node (std::unique_ptr<node> n)
{
gcc_assert (n);
m_nodes.push_back (std::move (n));
}
void
add_edge (std::unique_ptr<edge> e)
{
gcc_assert (e);
m_edges.push_back (std::move (e));
}
void
add_edge (const char *id,
node &src_node,
node &dst_node,
const char *label = nullptr);
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)
{
m_id_to_node_map.insert ({std::move (node_id), &new_node});
}
void
add_edge_id (std::string edge_id, edge &new_edge)
{
m_id_to_edge_map.insert ({std::move (edge_id), &new_edge});
}
std::string
make_edge_id (const char *edge_id);
std::unique_ptr<std::string> m_description;
std::map<std::string, node *> m_id_to_node_map;
std::map<std::string, edge *> m_id_to_edge_map;
std::vector<std::unique_ptr<node>> m_nodes;
std::vector<std::unique_ptr<edge>> m_edges;
size_t m_next_edge_id_index;
};
// A node in a directed graph, corresponding to SARIF v2.1.0 section 3.40.
class node : public object
{
public:
virtual ~node () {}
node (digraph &g, std::string id)
: m_id (id),
m_physical_loc (UNKNOWN_LOCATION)
{
g.add_node_id (std::move (id), *this);
}
node (const node &) = delete;
std::string
get_id () const { return m_id; }
const char *
get_label () const
{
if (!m_label)
return nullptr;
return m_label->c_str ();
}
void
set_label (const char *label)
{
if (label)
m_label = std::make_unique<std::string> (label);
else
m_label = nullptr;
}
void
set_label (std::string label)
{
m_label = std::make_unique<std::string> (std::move (label));
}
size_t
get_num_children () const { return m_children.size (); }
node &
get_child (size_t idx) const { return *m_children[idx].get (); }
void
add_child (std::unique_ptr<node> child)
{
gcc_assert (child);
m_children.push_back (std::move (child));
}
location_t
get_physical_loc () const
{
return m_physical_loc;
}
void
set_physical_loc (location_t physical_loc)
{
m_physical_loc = physical_loc;
}
logical_locations::key
get_logical_loc () const
{
return m_logical_loc;
}
void
set_logical_loc (logical_locations::key logical_loc)
{
m_logical_loc = logical_loc;
}
void print (graphviz_out &gv) const;
void
dump () const;
std::unique_ptr<json::object>
to_json_sarif_node () const;
std::unique_ptr<node>
clone (digraph &new_graph,
std::map<node *, node *> &node_mapping) const;
private:
std::string m_id;
std::unique_ptr<std::string> m_label;
std::vector<std::unique_ptr<node>> m_children;
location_t m_physical_loc;
logical_locations::key m_logical_loc;
};
// An edge in a directed graph, corresponding to SARIF v2.1.0 section 3.41.
class edge : public object
{
public:
virtual ~edge () {}
/* SARIF requires us to provide unique edge IDs within a graph,
but otherwise we don't need them.
Pass in nullptr for the id to get the graph to generate a unique
edge id for us. */
edge (digraph &g,
const char *id,
node &src_node,
node &dst_node)
: m_id (g.make_edge_id (id)),
m_src_node (src_node),
m_dst_node (dst_node)
{
g.add_edge_id (m_id, *this);
}
std::string
get_id () const { return m_id; }
const char *
get_label () const
{
if (!m_label)
return nullptr;
return m_label->c_str ();
}
void
set_label (const char *label)
{
if (label)
m_label = std::make_unique<std::string> (label);
else
m_label = nullptr;
}
node &
get_src_node () const { return m_src_node; }
node &
get_dst_node () const { return m_dst_node; }
void
dump () const;
std::unique_ptr<json::object>
to_json_sarif_edge () const;
std::unique_ptr<edge>
clone (digraph &new_graph,
const std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> &node_mapping) const;
private:
std::string m_id;
std::unique_ptr<std::string> m_label;
node &m_src_node;
node &m_dst_node;
};
} // namespace digraphs
} // namespace diagnostics
#endif /* ! GCC_DIAGNOSTICS_DIGRAPHS_H */