diff options
Diffstat (limited to 'gcc/libsarifreplay.cc')
-rw-r--r-- | gcc/libsarifreplay.cc | 521 |
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 ¬e = 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." |