aboutsummaryrefslogtreecommitdiff
path: root/gcc/libsarifreplay.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/libsarifreplay.cc')
-rw-r--r--gcc/libsarifreplay.cc521
1 files changed, 466 insertions, 55 deletions
diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index 2d6c394..e8fc6d0 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "libgdiagnostics++.h"
+#include "libgdiagnostics-private.h"
#include "json-parsing.h"
#include "intl.h"
#include "sarif-spec-urls.def"
@@ -281,16 +282,18 @@ class annotation
{
public:
annotation (libgdiagnostics::physical_location phys_loc,
- label_text label)
+ libgdiagnostics::message_buffer label)
: m_phys_loc (phys_loc),
m_label (std::move (label))
{
}
libgdiagnostics::physical_location m_phys_loc;
- label_text m_label;
+ libgdiagnostics::message_buffer m_label;
};
+using id_map = std::map<std::string, const json::string *>;
+
class sarif_replayer
{
public:
@@ -330,7 +333,7 @@ private:
enum status emit_sarif_as_diagnostics (const json::value &jv);
- label_text
+ libgdiagnostics::message_buffer
make_plain_text_within_result_message (const json::object *tool_component_obj,
const json::object &message_obj,
const json::object *rule_obj);
@@ -413,6 +416,30 @@ private:
const json::object &run_obj,
libgdiagnostics::execution_path &out);
+ // "graph" object (§3.39)
+ enum status
+ handle_graph_object (const json::object &graph_obj,
+ const json::object &run_obj,
+ libgdiagnostics::graph &out);
+ // "node" object (§3.40)
+ libgdiagnostics::node
+ handle_node_object (const json::object &node_obj,
+ const json::object &run_obj,
+ libgdiagnostics::graph &graph,
+ libgdiagnostics::node parent_node,
+ id_map &node_id_map);
+
+ // "edge" object (§3.41)
+ libgdiagnostics::edge
+ handle_edge_object (const json::object &edge_obj,
+ libgdiagnostics::graph &graph,
+ id_map &edge_id_map);
+
+ libgdiagnostics::node
+ get_graph_node_by_id_property (const json::object &edge_json_object,
+ const property_spec_ref &id_prop,
+ libgdiagnostics::graph &graph);
+
// reportingDescriptor lookup (§3.52.3)
const json::object *
lookup_rule_by_id_in_tool (const char *rule_id,
@@ -446,7 +473,7 @@ private:
{
va_list ap;
va_start (ap, gmsgid);
- report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR);
+ report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR);
va_end (ap);
return status::err_invalid_sarif;
}
@@ -462,14 +489,25 @@ private:
{
va_list ap;
va_start (ap, gmsgid);
- report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY);
+ report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY);
va_end (ap);
return status::err_unhandled_sarif;
}
void
+ report_note (const json::value &jv,
+ const char *gmsgid, ...)
+ LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (3, 4)
+ {
+ va_list ap;
+ va_start (ap, gmsgid);
+ report_problem (jv, nullptr, gmsgid, &ap, DIAGNOSTIC_LEVEL_NOTE);
+ va_end (ap);
+ }
+
+ void
report_problem (const json::value &jv,
- const spec_ref &ref,
+ const spec_ref *ref,
const char *gmsgid,
va_list *args,
enum diagnostic_level level)
@@ -481,11 +519,14 @@ private:
There doesn't seem to be a systematic mapping from spec sections to
HTML anchors, so we can't provide URLs
(filed as https://github.com/oasis-tcs/sarif-spec/issues/533 ). */
- char *ref_desc = ref.make_description ();
- char *ref_url = ref.make_url ();
- diag.add_rule (ref_desc, ref_url);
- free (ref_desc);
- free (ref_url);
+ if (ref)
+ {
+ char *ref_desc = ref->make_description ();
+ char *ref_url = ref->make_url ();
+ diag.add_rule (ref_desc, ref_url);
+ free (ref_desc);
+ free (ref_url);
+ }
auto loc_range
= make_physical_location (m_control_mgr,
@@ -651,6 +692,38 @@ private:
const string_property_value<ValueType> *value_arr,
size_t num_values);
+ const json::object *
+ maybe_get_property_bag (const json::object &obj)
+ {
+ const property_spec_ref properties
+ ("object", "properties", "3.8.1");
+ return get_optional_property<json::object> (obj, properties);
+ }
+
+ /* Look for a property bag within OBJ.
+ If found, look for a property within it named PROPERTY_NAME
+ of the given type.
+ If successful, return the property's value.
+ Otherwise, return nullptr without complaining (unless the property bag
+ is itself not an object). */
+ template <typename JsonType>
+ const JsonType *
+ maybe_get_property_bag_value (const json::object &obj,
+ const char *property_name)
+ {
+ auto property_bag_obj = maybe_get_property_bag (obj);
+ if (!property_bag_obj)
+ return nullptr;
+ const json::value *property_val = property_bag_obj->get (property_name);
+ if (!property_val)
+ return nullptr;
+ const JsonType *sub = dyn_cast<const JsonType *> (property_val);
+ if (!sub)
+ /* Property is wrong kind of value. Don't treat this as an error. */
+ return nullptr;
+ return sub;
+ }
+
/* The manager to replay the SARIF files to. */
libgdiagnostics::manager m_output_mgr;
@@ -929,6 +1002,31 @@ sarif_replayer::handle_run_obj (const json::object &run_obj)
break;
}
+ // §3.14.20 "graphs"
+ const property_spec_ref prop_graphs ("run", "graphs", "3.14.20");
+ if (const json::array *graphs
+ = get_optional_property<json::array> (run_obj,
+ prop_graphs))
+ {
+ for (auto element : *graphs)
+ {
+ if (const json::object *graph_json_obj
+ = require_object_for_element (*element, prop_graphs))
+ {
+ libgdiagnostics::graph graph;
+ enum status s = handle_graph_object (*graph_json_obj,
+ run_obj,
+ graph);
+ if (s != status::ok)
+ return s;
+
+ m_output_mgr.take_global_graph (std::move (graph));
+ }
+ else
+ return status::err_invalid_sarif;
+ }
+ }
+
return status::ok;
}
@@ -1083,12 +1181,12 @@ sarif_replayer::get_level_from_level_str (const json::string &level_str)
static void
add_any_annotations (libgdiagnostics::diagnostic &diag,
- const std::vector<annotation> &annotations)
+ std::vector<annotation> &annotations)
{
for (auto &annotation : annotations)
- if (annotation.m_label.get ())
+ if (annotation.m_label.m_inner)
diag.add_location_with_label (annotation.m_phys_loc,
- annotation.m_label.get ());
+ std::move (annotation.m_label));
else
diag.add_location (annotation.m_phys_loc);
}
@@ -1151,13 +1249,13 @@ sarif_replayer::handle_result_obj (const json::object &result_obj,
}
// §3.27.11 "message" property
- label_text text;
+ libgdiagnostics::message_buffer msg_buf;
if (auto message_obj
= get_optional_property<json::object> (result_obj, PROP_result_message))
- text = make_plain_text_within_result_message (nullptr, // TODO: tool_component_obj,
- *message_obj,
- rule_obj);
- if (!text.get ())
+ msg_buf = make_plain_text_within_result_message (nullptr, // TODO: tool_component_obj,
+ *message_obj,
+ rule_obj);
+ if (!msg_buf.m_inner)
return status::err_invalid_sarif;
// §3.27.12 "locations" property
@@ -1244,8 +1342,34 @@ sarif_replayer::handle_result_obj (const json::object &result_obj,
if (path.m_inner)
err.take_execution_path (std::move (path));
+ // §3.27.19 "graphs" property
+ const property_spec_ref prop_graphs ("result", "graphs", "3.27.19");
+ if (const json::array *graphs
+ = get_optional_property<json::array> (result_obj,
+ prop_graphs))
+ {
+ for (auto element : *graphs)
+ {
+ if (const json::object *graph_json_obj
+ = require_object_for_element (*element, prop_graphs))
+ {
+ libgdiagnostics::graph graph;
+ enum status s = handle_graph_object (*graph_json_obj,
+ run_obj,
+ graph);
+ if (s != status::ok)
+ return s;
+
+ err.take_graph (std::move (graph));
+ }
+ else
+ return status::err_invalid_sarif;
+ }
+ }
+
// §3.27.22 relatedLocations property
- std::vector<std::pair<libgdiagnostics::diagnostic, label_text>> notes;
+ std::vector<std::pair<libgdiagnostics::diagnostic,
+ libgdiagnostics::message_buffer>> notes;
const property_spec_ref prop_related_locations
("result", "relatedLocations", "3.27.22");
if (auto related_locations_arr
@@ -1277,18 +1401,30 @@ sarif_replayer::handle_result_obj (const json::object &result_obj,
prop_message))
{
/* Treat related locations with a message as a "note". */
- label_text text
+ libgdiagnostics::message_buffer msg_buf
(make_plain_text_within_result_message
(tool_component_obj,
*message_obj,
rule_obj));
- if (!text.get ())
+ if (!msg_buf.m_inner)
return status::err_invalid_sarif;
+
auto note (m_output_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE));
note.set_location (physical_loc);
note.set_logical_location (logical_loc);
add_any_annotations (note, annotations);
- notes.push_back ({std::move (note), std::move (text)});
+
+ /* Look for "nestingLevel" property, as per
+ "P3358R0 SARIF for Structured Diagnostics"
+ https://wg21.link/P3358R0 */
+ if (auto nesting_level
+ = maybe_get_property_bag_value<json::integer_number>
+ (*location_obj,
+ "nestingLevel"))
+ private_diagnostic_set_nesting_level (note.m_inner,
+ nesting_level->get ());
+
+ notes.push_back ({std::move (note), std::move (msg_buf)});
}
else
{
@@ -1311,14 +1447,14 @@ sarif_replayer::handle_result_obj (const json::object &result_obj,
handle_fix_object (err, *fix_obj);
}
- err.finish ("%s", text.get ());
+ err.finish_via_msg_buf (std::move (msg_buf));
// Flush any notes
for (auto &iter : notes)
{
auto &note = iter.first;
- auto &text = iter.second;
- note.finish ("%s", text.get ());
+ auto &msg_buf = iter.second;
+ note.finish_via_msg_buf (std::move (msg_buf));
}
return status::ok;
@@ -1446,13 +1582,10 @@ maybe_consume_embedded_link (const char *&iter_src)
and substitute for any placeholders (§3.11.5) and handle any
embedded links (§3.11.6).
- Limitations:
- - we don't preserve destinations within embedded links
-
MESSAGE_OBJ is "theMessage"
RULE_OBJ is "theRule". */
-label_text
+libgdiagnostics::message_buffer
sarif_replayer::
make_plain_text_within_result_message (const json::object *tool_component_obj,
const json::object &message_obj,
@@ -1465,7 +1598,7 @@ make_plain_text_within_result_message (const json::object *tool_component_obj,
rule_obj,
js_str);
if (!original_text)
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
gcc_assert (js_str);
@@ -1475,7 +1608,7 @@ make_plain_text_within_result_message (const json::object *tool_component_obj,
= get_optional_property<json::array> (message_obj, arguments_prop);
/* Duplicate original_text, substituting any placeholders. */
- std::string accum;
+ libgdiagnostics::message_buffer result (diagnostic_message_buffer_new ());
const char *iter_src = original_text;
while (char ch = *iter_src)
@@ -1491,7 +1624,7 @@ make_plain_text_within_result_message (const json::object *tool_component_obj,
" but message object has no %qs property",
(int)arg_idx,
arguments_prop.get_property_name ());
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
if (arg_idx >= arguments->length ())
{
@@ -1502,20 +1635,20 @@ make_plain_text_within_result_message (const json::object *tool_component_obj,
arguments_prop.get_property_name (),
(int)arg_idx);
// TODO: might be nice to add a note showing the args
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
auto replacement_jstr
= require_string (*arguments->get (arg_idx), arguments_prop);
if (!replacement_jstr)
- return label_text::borrow (nullptr);
- accum += replacement_jstr->get_string ();
+ return libgdiagnostics::message_buffer ();
+ result += replacement_jstr->get_string ();
}
else if (ch == '{' || ch == '}')
{
/* '{' and '}' are escaped by repeating them. */
if (iter_src[1] == ch)
{
- accum += ch;
+ result += ch;
iter_src += 2;
}
else
@@ -1525,24 +1658,25 @@ make_plain_text_within_result_message (const json::object *tool_component_obj,
report_invalid_sarif (*js_str, msgs_with_placeholders,
"unescaped '%c' within message string",
ch);
- return label_text::borrow (nullptr);
+ return libgdiagnostics::message_buffer ();
}
}
else if (auto link = maybe_consume_embedded_link (iter_src))
{
- accum += link->text;
- /* TODO: use the destination. */
+ result.begin_url (link->destination.c_str ());
+ result += link->text.c_str ();
+ result.end_url ();
/* TODO: potentially could try to convert
intra-sarif links into event ids. */
}
else
{
- accum += ch;
+ result += ch;
iter_src++;
}
}
- return label_text::take (xstrdup (accum.c_str ()));
+ return result;
}
/* Handle a value that should be a multiformatMessageString object (§3.12).
@@ -1676,7 +1810,7 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj,
{
libgdiagnostics::physical_location physical_loc;
libgdiagnostics::logical_location logical_loc;
- label_text message;
+ libgdiagnostics::message_buffer msg_buf;
int stack_depth = 0;
const property_spec_ref location_prop
@@ -1698,7 +1832,7 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj,
= get_optional_property<json::object> (*location_obj,
location_message))
{
- message = make_plain_text_within_result_message
+ msg_buf = make_plain_text_within_result_message
(nullptr,
*message_obj,
nullptr/* TODO. */);
@@ -1740,16 +1874,29 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj,
}
}
- if (message.get ())
- path.add_event (physical_loc,
- logical_loc,
- stack_depth,
- "%s", message.get ());
- else
- path.add_event (physical_loc,
- logical_loc,
- stack_depth,
- "");
+ libgdiagnostics::graph state_graph;
+ if (auto sarif_state_graph
+ = maybe_get_property_bag_value<json::object> (tflow_loc_obj,
+ "gcc/diagnostics/paths/event/state_graph"))
+ {
+ enum status s
+ = handle_graph_object (*sarif_state_graph, run_obj, state_graph);
+ if (s != status::ok)
+ return s;
+ }
+
+ if (!msg_buf.m_inner)
+ msg_buf.m_inner = diagnostic_message_buffer_new ();
+
+ private_diagnostic_execution_path_add_event_3 (path.m_inner,
+ physical_loc.m_inner,
+ logical_loc.m_inner,
+ stack_depth,
+ state_graph.m_inner,
+ msg_buf.m_inner);
+
+ state_graph.m_owned = false;
+ msg_buf.m_inner = nullptr;
return status::ok;
}
@@ -1828,7 +1975,7 @@ handle_location_object (const json::object &location_obj,
if (s != status::ok)
return s;
- label_text label;
+ libgdiagnostics::message_buffer label;
// §3.30.14 message property
{
@@ -2142,6 +2289,270 @@ handle_logical_location_object (const json::object &logical_loc_obj,
return status::ok;
}
+// "graph" object (§3.39)
+
+enum status
+sarif_replayer::handle_graph_object (const json::object &graph_json_obj,
+ const json::object &run_obj,
+ libgdiagnostics::graph &out_graph)
+{
+ out_graph = libgdiagnostics::graph
+ (diagnostic_manager_new_graph (m_output_mgr.m_inner));
+
+ id_map node_id_map;
+ id_map edge_id_map;
+
+ if (auto properties = maybe_get_property_bag (graph_json_obj))
+ private_diagnostic_graph_set_property_bag (*out_graph.m_inner,
+ properties->clone_as_object ());
+
+ // §3.39.2: MAY contain a "description" property
+ const property_spec_ref description_prop
+ ("graph", "description", "3.39.2");
+ if (auto description_obj
+ = get_optional_property<json::object> (graph_json_obj, description_prop))
+ {
+ auto msg_buf
+ = make_plain_text_within_result_message (&run_obj,
+ *description_obj,
+ nullptr);
+ if (!msg_buf.m_inner)
+ return status::err_invalid_sarif;
+ out_graph.set_description (std::move (msg_buf));
+ }
+
+ // §3.39.3: MAY contain a "nodes" property
+ const property_spec_ref nodes_prop
+ ("graph", "nodes", "3.39.3");
+ if (auto nodes_arr
+ = get_optional_property<json::array> (graph_json_obj, nodes_prop))
+ {
+ for (auto element : *nodes_arr)
+ {
+ const json::object *node_json_obj
+ = require_object_for_element (*element, nodes_prop);
+ if (!node_json_obj)
+ return status::err_invalid_sarif;
+ libgdiagnostics::node node
+ = handle_node_object (*node_json_obj, run_obj, out_graph,
+ nullptr, node_id_map);
+ if (node.m_inner == nullptr)
+ return status::err_invalid_sarif;
+ }
+ }
+ else
+ // If we have no nodes, we can't handle the edges.
+ return status::ok;
+
+ // §3.39.4 edges property
+ const property_spec_ref edges_prop
+ ("graph", "edges", "3.39.4");
+ if (auto edges_arr
+ = get_optional_property<json::array> (graph_json_obj, edges_prop))
+ {
+ for (auto element : *edges_arr)
+ {
+ const json::object *edge_json_obj
+ = require_object_for_element (*element, edges_prop);
+ if (!edge_json_obj)
+ return status::err_invalid_sarif;
+ libgdiagnostics::edge edge
+ = handle_edge_object (*edge_json_obj, out_graph, edge_id_map);
+ if (edge.m_inner == nullptr)
+ return status::err_invalid_sarif;
+ }
+ }
+
+ return status::ok;
+}
+
+// "node" object (§3.40)
+
+libgdiagnostics::node
+sarif_replayer::handle_node_object (const json::object &node_json_obj,
+ const json::object &run_obj,
+ libgdiagnostics::graph &graph,
+ libgdiagnostics::node parent_node,
+ id_map &node_id_map)
+{
+ // §3.40.2 "id" property
+ const property_spec_ref id_prop ("node", "id", "3.40.2");
+ auto id_str = get_required_property<json::string> (node_json_obj, id_prop);
+ if (!id_str)
+ return nullptr;
+ const char *id = id_str->get_string ();
+ if (diagnostic_graph_get_node_by_id (graph.m_inner, id))
+ {
+ // Duplicate id; fail:
+ libgdiagnostics::group g (m_control_mgr);
+ report_invalid_sarif (*id_str,
+ id_prop,
+ "duplicate node id %qs within graph",
+ id);
+ gcc_assert (node_id_map[id]);
+ report_note (*node_id_map[id],
+ "%qs already used as node id within graph here",
+ id);
+ return nullptr;
+ }
+ node_id_map[id] = id_str;
+
+ libgdiagnostics::node new_node
+ = libgdiagnostics::node (diagnostic_graph_add_node (graph.m_inner,
+ id,
+ parent_node.m_inner));
+ if (auto properties = maybe_get_property_bag (node_json_obj))
+ private_diagnostic_node_set_property_bag (*new_node.m_inner,
+ properties->clone_as_object ());
+
+ // §3.40.3 "label" property
+ const property_spec_ref label_prop
+ ("node", "label", "3.40.3");
+ if (auto label_obj
+ = get_optional_property<json::object> (node_json_obj, label_prop))
+ {
+ auto msg_buf
+ = make_plain_text_within_result_message (&run_obj,
+ *label_obj,
+ nullptr);
+ if (!msg_buf.m_inner)
+ return nullptr;
+ new_node.set_label (std::move (msg_buf));
+ }
+
+ // §3.40.4 "location" property
+ const property_spec_ref location_prop ("node", "location", "3.40.4");
+ if (auto location_json_obj
+ = get_optional_property<json::object> (node_json_obj, location_prop))
+ {
+ libgdiagnostics::physical_location physical_loc;
+ libgdiagnostics::logical_location logical_loc;
+ enum status s = handle_location_object (*location_json_obj,
+ run_obj,
+ physical_loc,
+ logical_loc,
+ nullptr); // annotations
+ if (s != status::ok)
+ return nullptr;
+
+ new_node.set_location (physical_loc);
+ new_node.set_logical_location (logical_loc);
+ }
+
+ // §3.40.5: MAY contain a "children" property
+ const property_spec_ref children_prop
+ ("graph", "children", "3.40.5");
+ if (auto children_json_arr
+ = get_optional_property<json::array> (node_json_obj, children_prop))
+ {
+ for (auto json_element : *children_json_arr)
+ {
+ const json::object *child_json_obj
+ = require_object_for_element (*json_element, children_prop);
+ if (!child_json_obj)
+ return nullptr;
+ libgdiagnostics::node child_node
+ = handle_node_object (*child_json_obj, run_obj, graph,
+ new_node, node_id_map);
+ if (child_node.m_inner == nullptr)
+ return nullptr;
+ }
+ }
+
+ return new_node;
+}
+
+// "edge" object (§3.41)
+
+libgdiagnostics::edge
+sarif_replayer::handle_edge_object (const json::object &edge_json_obj,
+ libgdiagnostics::graph &graph,
+ id_map &edge_id_map)
+{
+ // §3.41.2 "id" property
+ const property_spec_ref id_prop ("edge", "id", "3.41.2");
+ auto id_str = get_required_property<json::string> (edge_json_obj, id_prop);
+ if (!id_str)
+ return nullptr;
+ const char *id = id_str->get_string ();
+ if (diagnostic_graph_get_edge_by_id (graph.m_inner, id))
+ {
+ // Duplicate id; fail:
+ libgdiagnostics::group g (m_control_mgr);
+ report_invalid_sarif (*id_str,
+ id_prop,
+ "duplicate edge id %qs within graph",
+ id);
+ gcc_assert (edge_id_map[id]);
+ report_note (*edge_id_map[id],
+ "%qs already used as edge id within graph here",
+ id);
+ return nullptr;
+ }
+ edge_id_map[id] = id_str;
+
+ // §3.41.3 "label" property
+ libgdiagnostics::message_buffer label;
+ const property_spec_ref label_prop
+ ("edge", "label", "3.41.3");
+ if (auto label_obj
+ = get_optional_property<json::object> (edge_json_obj, label_prop))
+ {
+ label = make_plain_text_within_result_message (nullptr,
+ *label_obj,
+ nullptr);
+ if (!label.m_inner)
+ return nullptr;
+ }
+
+ // §3.41.4 "sourceNodeId" property
+ const property_spec_ref src_id_prop ("edge", "sourceNodeId", "3.41.4");
+ auto src_node = get_graph_node_by_id_property (edge_json_obj,
+ src_id_prop,
+ graph);
+ if (!src_node.m_inner)
+ return nullptr;
+
+ // §3.41.5 "targetNodeId" property
+ const property_spec_ref dst_id_prop ("edge", "targetNodeId", "3.41.5");
+ auto dst_node = get_graph_node_by_id_property (edge_json_obj,
+ dst_id_prop,
+ graph);
+ if (!dst_node.m_inner)
+ return nullptr;
+
+ auto result = graph.add_edge (id, src_node, dst_node, std::move (label));
+
+ if (auto properties = maybe_get_property_bag (edge_json_obj))
+ private_diagnostic_edge_set_property_bag (*result.m_inner,
+ properties->clone_as_object ());
+
+ return result;
+}
+
+libgdiagnostics::node
+sarif_replayer::
+get_graph_node_by_id_property (const json::object &edge_json_obj,
+ const property_spec_ref &id_prop,
+ libgdiagnostics::graph &graph)
+{
+ auto id_str = get_required_property<json::string> (edge_json_obj, id_prop);
+ if (!id_str)
+ return nullptr;
+ const char *id = id_str->get_string ();
+ auto node = graph.get_node_by_id (id);
+ if (!node.m_inner)
+ {
+ // id not found; complain:
+ report_invalid_sarif (*id_str,
+ id_prop,
+ "no node with id %qs in graph",
+ id);
+ return nullptr;
+ }
+ return node;
+}
+
// 3.52.3 reportingDescriptor lookup
// "For an example of the interaction between ruleId and rule.id, see §3.52.4."