#include "selftest-diagnostic-show-locus.h"
#include "selftest-json.h"
#include "text-range-label.h"
+#include "pretty-print-format-impl.h"
+#include "pretty-print-urlifier.h"
/* Forward decls. */
class sarif_builder;
class sarif_result : public sarif_location_manager
{
public:
- sarif_result () : m_related_locations_arr (nullptr) {}
+ sarif_result (unsigned idx_within_parent)
+ : m_related_locations_arr (nullptr),
+ m_idx_within_parent (idx_within_parent)
+ {}
+
+ unsigned get_index_within_parent () const { return m_idx_within_parent; }
void
on_nested_diagnostic (diagnostic_context &context,
private:
json::array *m_related_locations_arr; // borrowed
+ const unsigned m_idx_within_parent;
};
/* Subclass of sarif_object for SARIF "location" objects
/* Subclass of sarif_object for SARIF "codeFlow" objects
(SARIF v2.1.0 section 3.36). */
-class sarif_code_flow : public sarif_object {};
+class sarif_code_flow : public sarif_object
+{
+public:
+ sarif_code_flow (sarif_result &parent,
+ unsigned idx_within_parent);
+
+ sarif_result &get_parent () const { return m_parent; }
+ unsigned get_index_within_parent () const { return m_idx_within_parent; }
+
+ sarif_thread_flow &
+ get_or_append_thread_flow (const diagnostic_thread &thread,
+ diagnostic_thread_id_t thread_id);
+
+ sarif_thread_flow &
+ get_thread_flow (diagnostic_thread_id_t thread_id);
+
+ void add_location (sarif_thread_flow_location &);
+
+ sarif_thread_flow_location &
+ get_thread_flow_loc_obj (diagnostic_event_id_t event_id) const;
+
+private:
+ sarif_result &m_parent;
+ const unsigned m_idx_within_parent;
+
+ hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
+ sarif_thread_flow *> m_thread_id_map; // borrowed ptr
+ json::array *m_thread_flows_arr; // borrowed
+
+ /* Vec of borrowed ptr, allowing for going easily from
+ an event_id to the corresponding threadFlowLocation object. */
+ std::vector<sarif_thread_flow_location *> m_all_tfl_objs;
+};
/* Subclass of sarif_object for SARIF "threadFlow" objects
(SARIF v2.1.0 section 3.37). */
class sarif_thread_flow : public sarif_object
{
public:
- sarif_thread_flow (const diagnostic_thread &thread);
+ sarif_thread_flow (sarif_code_flow &parent,
+ const diagnostic_thread &thread,
+ unsigned idx_within_parent);
- void
- add_location
- (std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj);
+ sarif_code_flow &get_parent () const { return m_parent; }
+ unsigned get_index_within_parent () const { return m_idx_within_parent; }
+
+ sarif_thread_flow_location &add_location ();
private:
+ sarif_code_flow &m_parent;
json::array *m_locations_arr; // borrowed
+ const unsigned m_idx_within_parent;
};
/* Subclass of sarif_object for SARIF "threadFlowLocation" objects
(SARIF v2.1.0 section 3.38). */
-class sarif_thread_flow_location : public sarif_object {};
+class sarif_thread_flow_location : public sarif_object
+{
+public:
+ sarif_thread_flow_location (sarif_thread_flow &parent,
+ unsigned idx_within_parent)
+ : m_parent (parent),
+ m_idx_within_parent (idx_within_parent)
+ {
+ }
+
+ sarif_thread_flow &get_parent () const { return m_parent; }
+ unsigned get_index_within_parent () const { return m_idx_within_parent; }
+
+private:
+ sarif_thread_flow &m_parent;
+ const unsigned m_idx_within_parent;
+};
/* Subclass of sarif_object for SARIF "reportingDescriptor" objects
(SARIF v2.1.0 section 3.49). */
std::unique_ptr<sarif_artifact_location>
make_artifact_location_object (const char *filename);
+ const sarif_code_flow *
+ get_code_flow_for_event_ids () const
+ {
+ return m_current_code_flow;
+ }
+
+ token_printer &get_token_printer () { return m_token_printer; }
+
private:
+ class sarif_token_printer : public token_printer
+ {
+ public:
+ sarif_token_printer (sarif_builder &builder)
+ : m_builder (builder)
+ {
+ }
+ void print_tokens (pretty_printer *pp,
+ const pp_token_list &tokens) final override;
+ private:
+ sarif_builder &m_builder;
+ };
+
std::unique_ptr<sarif_result>
make_result_object (diagnostic_context &context,
const diagnostic_info &diagnostic,
- diagnostic_t orig_diag_kind);
+ diagnostic_t orig_diag_kind,
+ unsigned idx_within_parent);
void
add_any_include_chain (sarif_location_manager &loc_mgr,
sarif_location &location_obj,
enum diagnostic_artifact_role role);
std::unique_ptr<sarif_code_flow>
make_code_flow_object (sarif_result &result,
+ unsigned idx_within_parent,
const diagnostic_path &path);
- std::unique_ptr<sarif_thread_flow_location>
- make_thread_flow_location_object (sarif_result &result,
- const diagnostic_event &event,
- int path_event_idx);
+ void
+ populate_thread_flow_location_object (sarif_result &result,
+ sarif_thread_flow_location &thread_flow_loc_obj,
+ const diagnostic_event &event,
+ int event_execution_idx);
std::unique_ptr<json::array>
maybe_make_kinds_array (diagnostic_event::meaning m) const;
std::unique_ptr<sarif_physical_location>
diagnostic_context &m_context;
const line_maps *m_line_maps;
+ sarif_token_printer m_token_printer;
/* The JSON object for the invocation object. */
std::unique_ptr<sarif_invocation> m_invocation_obj;
int m_tabstop;
bool m_formatted;
+
+ unsigned m_next_result_idx;
+ sarif_code_flow *m_current_code_flow;
};
/* class sarif_object : public json::object. */
kinds_arr->append_string (kind_str);
}
+/* class sarif_code_flow : public sarif_object. */
+
+sarif_code_flow::sarif_code_flow (sarif_result &parent,
+ unsigned idx_within_parent)
+: m_parent (parent),
+ m_idx_within_parent (idx_within_parent)
+{
+ /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
+ auto thread_flows_arr = ::make_unique<json::array> ();
+ m_thread_flows_arr = thread_flows_arr.get (); // borrowed
+ set<json::array> ("threadFlows", std::move (thread_flows_arr));
+}
+
+sarif_thread_flow &
+sarif_code_flow::get_or_append_thread_flow (const diagnostic_thread &thread,
+ diagnostic_thread_id_t thread_id)
+{
+ sarif_thread_flow **slot = m_thread_id_map.get (thread_id);
+ if (slot)
+ return **slot;
+
+ unsigned next_thread_flow_idx = m_thread_flows_arr->size ();
+ auto thread_flow_obj
+ = ::make_unique<sarif_thread_flow> (*this, thread, next_thread_flow_idx);
+ m_thread_id_map.put (thread_id, thread_flow_obj.get ()); // borrowed
+ sarif_thread_flow *result = thread_flow_obj.get ();
+ m_thread_flows_arr->append<sarif_thread_flow> (std::move (thread_flow_obj));
+ return *result;
+}
+
+sarif_thread_flow &
+sarif_code_flow::get_thread_flow (diagnostic_thread_id_t thread_id)
+{
+ sarif_thread_flow **slot = m_thread_id_map.get (thread_id);
+ gcc_assert (slot); // it must already have one
+ return **slot;
+}
+
+void
+sarif_code_flow::add_location (sarif_thread_flow_location &tfl_obj)
+{
+ m_all_tfl_objs.push_back (&tfl_obj);
+}
+
+sarif_thread_flow_location &
+sarif_code_flow::get_thread_flow_loc_obj (diagnostic_event_id_t event_id) const
+{
+ gcc_assert (event_id.known_p ());
+ gcc_assert ((size_t)event_id.zero_based () < m_all_tfl_objs.size ());
+ sarif_thread_flow_location *tfl_obj = m_all_tfl_objs[event_id.zero_based ()];
+ gcc_assert (tfl_obj);
+ return *tfl_obj;
+}
+
/* class sarif_thread_flow : public sarif_object. */
-sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
+sarif_thread_flow::sarif_thread_flow (sarif_code_flow &parent,
+ const diagnostic_thread &thread,
+ unsigned idx_within_parent)
+: m_parent (parent),
+ m_idx_within_parent (idx_within_parent)
{
/* "id" property (SARIF v2.1.0 section 3.37.2). */
label_text name (thread.get_name (false));
set ("locations", m_locations_arr);
}
-void
-sarif_thread_flow::
-add_location (std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj)
+/* Add a sarif_thread_flow_location to this threadFlow object, but
+ don't populate it yet. */
+
+sarif_thread_flow_location &
+sarif_thread_flow::add_location ()
{
- m_locations_arr->append (std::move (thread_flow_loc_obj));
+ const unsigned thread_flow_location_idx = m_locations_arr->size ();
+ sarif_thread_flow_location *thread_flow_loc_obj
+ = new sarif_thread_flow_location (*this, thread_flow_location_idx);
+ m_locations_arr->append (thread_flow_loc_obj);
+ m_parent.add_location (*thread_flow_loc_obj);
+ return *thread_flow_loc_obj;
}
/* class sarif_builder. */
bool formatted)
: m_context (context),
m_line_maps (line_maps),
+ m_token_printer (*this),
m_invocation_obj
(::make_unique<sarif_invocation> (*this,
context.get_original_argv ())),
m_rule_id_set (),
m_rules_arr (new json::array ()),
m_tabstop (context.m_tabstop),
- m_formatted (formatted)
+ m_formatted (formatted),
+ m_next_result_idx (0),
+ m_current_code_flow (nullptr)
{
gcc_assert (m_line_maps);
{
/* Top-level diagnostic. */
m_cur_group_result
- = make_result_object (context, diagnostic, orig_diag_kind);
+ = make_result_object (context, diagnostic, orig_diag_kind,
+ m_next_result_idx++);
}
}
std::unique_ptr<sarif_result>
sarif_builder::make_result_object (diagnostic_context &context,
const diagnostic_info &diagnostic,
- diagnostic_t orig_diag_kind)
+ diagnostic_t orig_diag_kind,
+ unsigned idx_within_parent)
{
- auto result_obj = ::make_unique<sarif_result> ();
+ auto result_obj = ::make_unique<sarif_result> (idx_within_parent);
/* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
/* Ideally we'd have an option_name for these. */
if (const diagnostic_path *path = diagnostic.richloc->get_path ())
{
auto code_flows_arr = ::make_unique<json::array> ();
+ const unsigned code_flow_index = 0;
code_flows_arr->append<sarif_code_flow>
(make_code_flow_object (*result_obj.get (),
+ code_flow_index,
*path));
result_obj->set<json::array> ("codeFlows", std::move (code_flows_arr));
}
return logical_loc_obj;
}
+label_text
+make_sarif_url_for_event (const sarif_code_flow *code_flow,
+ diagnostic_event_id_t event_id)
+{
+ gcc_assert (event_id.known_p ());
+
+ if (!code_flow)
+ return label_text ();
+
+ const sarif_thread_flow_location &tfl_obj
+ = code_flow->get_thread_flow_loc_obj (event_id);
+ const int location_idx = tfl_obj.get_index_within_parent ();
+
+ const sarif_thread_flow &thread_flow_obj = tfl_obj.get_parent ();
+ const int thread_flow_idx = thread_flow_obj.get_index_within_parent ();
+
+ const sarif_code_flow &code_flow_obj = thread_flow_obj.get_parent ();
+ const int code_flow_idx = code_flow_obj.get_index_within_parent ();
+
+ const sarif_result &result_obj = code_flow_obj.get_parent ();
+ const int result_idx = result_obj.get_index_within_parent ();
+
+ /* We only support a single run object in the log. */
+ const int run_idx = 0;
+
+ char *buf = xasprintf
+ ("sarif:/runs/%i/results/%i/codeFlows/%i/threadFlows/%i/locations/%i",
+ run_idx, result_idx, code_flow_idx, thread_flow_idx, location_idx);
+ return label_text::take (buf);
+}
+
/* Make a "codeFlow" object (SARIF v2.1.0 section 3.36) for PATH. */
std::unique_ptr<sarif_code_flow>
sarif_builder::make_code_flow_object (sarif_result &result,
+ unsigned idx_within_parent,
const diagnostic_path &path)
{
- auto code_flow_obj = ::make_unique <sarif_code_flow> ();
+ auto code_flow_obj
+ = ::make_unique <sarif_code_flow> (result, idx_within_parent);
- /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
- auto thread_flows_arr = ::make_unique<json::array> ();
-
- /* Walk the events, consolidating into per-thread threadFlow objects,
- using the index with PATH as the overall executionOrder. */
- hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
- sarif_thread_flow *> thread_id_map; // borrowed
+ /* First pass:
+ Create threadFlows and threadFlowLocation objects within them,
+ effectively recording a mapping from event_id to threadFlowLocation
+ so that we can later go from an event_id to a URI within the
+ SARIF file. */
for (unsigned i = 0; i < path.num_events (); i++)
{
const diagnostic_event &event = path.get_event (i);
const diagnostic_thread_id_t thread_id = event.get_thread_id ();
- sarif_thread_flow *thread_flow_obj;
- if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
- thread_flow_obj = *slot;
- else
- {
- const diagnostic_thread &thread = path.get_thread (thread_id);
- thread_flow_obj = new sarif_thread_flow (thread);
- thread_id_map.put (thread_id, thread_flow_obj); // borrowed
- thread_flows_arr->append (thread_flow_obj);
- }
+ sarif_thread_flow &thread_flow_obj
+ = code_flow_obj->get_or_append_thread_flow (path.get_thread (thread_id),
+ thread_id);
+ thread_flow_obj.add_location ();
+ }
- /* Add event to thread's threadFlow object. */
- std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj
- = make_thread_flow_location_object (result, event, i);
- thread_flow_obj->add_location (std::move (thread_flow_loc_obj));
+ /* Second pass: walk the events, populating the tfl objs. */
+ m_current_code_flow = code_flow_obj.get ();
+ for (unsigned i = 0; i < path.num_events (); i++)
+ {
+ const diagnostic_event &event = path.get_event (i);
+ sarif_thread_flow_location &thread_flow_loc_obj
+ = code_flow_obj->get_thread_flow_loc_obj (i);
+ populate_thread_flow_location_object (result,
+ thread_flow_loc_obj,
+ event,
+ i);
}
- code_flow_obj->set<json::array> ("threadFlows", std::move (thread_flows_arr));
+ m_current_code_flow = nullptr;
return code_flow_obj;
}
-/* Make a "threadFlowLocation" object (SARIF v2.1.0 section 3.38) for EVENT. */
+/* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38)
+ based on EVENT. */
-std::unique_ptr<sarif_thread_flow_location>
-sarif_builder::make_thread_flow_location_object (sarif_result &result,
- const diagnostic_event &ev,
- int path_event_idx)
+void
+sarif_builder::
+populate_thread_flow_location_object (sarif_result &result,
+ sarif_thread_flow_location &tfl_obj,
+ const diagnostic_event &ev,
+ int event_execution_idx)
{
- auto thread_flow_loc_obj = ::make_unique<sarif_thread_flow_location> ();
-
/* Give diagnostic_event subclasses a chance to add custom properties
via a property bag. */
- ev.maybe_add_sarif_properties (*thread_flow_loc_obj);
+ ev.maybe_add_sarif_properties (tfl_obj);
/* "location" property (SARIF v2.1.0 section 3.38.3). */
- thread_flow_loc_obj->set<sarif_location>
+ tfl_obj.set<sarif_location>
("location",
make_location_object (result, ev, diagnostic_artifact_role::traced_file));
/* "kinds" property (SARIF v2.1.0 section 3.38.8). */
diagnostic_event::meaning m = ev.get_meaning ();
if (auto kinds_arr = maybe_make_kinds_array (m))
- thread_flow_loc_obj->set<json::array> ("kinds", std::move (kinds_arr));
+ tfl_obj.set<json::array> ("kinds", std::move (kinds_arr));
/* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
- thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
+ tfl_obj.set_integer ("nestingLevel", ev.get_stack_depth ());
/* "executionOrder" property (SARIF v2.1.0 3.38.11).
Offset by 1 to match the human-readable values emitted by %@. */
- thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
+ tfl_obj.set_integer ("executionOrder", event_execution_idx + 1);
/* It might be nice to eventually implement the following for -fanalyzer:
- the "stack" property (SARIF v2.1.0 section 3.38.5)
- the "state" property (SARIF v2.1.0 section 3.38.9)
- the "importance" property (SARIF v2.1.0 section 3.38.13). */
-
- return thread_flow_loc_obj;
}
/* If M has any known meaning, make a json array suitable for the "kinds"
m_builder.emit_diagram (m_context, diagram);
}
+ sarif_builder &get_builder () { return m_builder; }
+
protected:
sarif_output_format (diagnostic_context &context,
const line_maps *line_maps,
char *m_base_file_name;
};
+/* Print the start of an embedded link to PP, as per 3.11.6. */
+
+static void
+sarif_begin_embedded_link (pretty_printer *pp)
+{
+ pp_character (pp, '[');
+}
+
+/* Print the end of an embedded link to PP, as per 3.11.6. */
+
+static void
+sarif_end_embedded_link (pretty_printer *pp,
+ const char *url)
+{
+ pp_string (pp, "](");
+ /* TODO: does the URI need escaping?
+ See https://github.com/oasis-tcs/sarif-spec/issues/657 */
+ pp_string (pp, url);
+ pp_character (pp, ')');
+}
+
+/* class sarif_token_printer : public token_printer. */
+
+/* Implementation of pretty_printer::token_printer for SARIF output.
+ Emit URLs as per 3.11.6 ("Messages with embedded links"). */
+
+void
+sarif_builder::sarif_token_printer::print_tokens (pretty_printer *pp,
+ const pp_token_list &tokens)
+{
+ /* Convert to text, possibly with colorization, URLs, etc. */
+ label_text current_url;
+ for (auto iter = tokens.m_first; iter; iter = iter->m_next)
+ switch (iter->m_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case pp_token::kind::text:
+ {
+ const pp_token_text *sub = as_a <const pp_token_text *> (iter);
+ const char * const str = sub->m_value.get ();
+ if (current_url.get ())
+ {
+ /* Write iter->m_value, but escaping any
+ escaped link characters as per 3.11.6. */
+ for (const char *ptr = str; *ptr; ptr++)
+ {
+ const char ch = *ptr;
+ switch (ch)
+ {
+ default:
+ pp_character (pp, ch);
+ break;
+ case '\\':
+ case '[':
+ case ']':
+ pp_character (pp, '\\');
+ pp_character (pp, ch);
+ break;
+ }
+ }
+ }
+ else
+ /* TODO: is other escaping needed? (e.g. of '[')
+ See https://github.com/oasis-tcs/sarif-spec/issues/658 */
+ pp_string (pp, str);
+ }
+ break;
+
+ case pp_token::kind::begin_color:
+ case pp_token::kind::end_color:
+ /* These are no-ops. */
+ break;
+
+ case pp_token::kind::begin_quote:
+ pp_begin_quote (pp, pp_show_color (pp));
+ break;
+ case pp_token::kind::end_quote:
+ pp_end_quote (pp, pp_show_color (pp));
+ break;
+
+ /* Emit URLs as per 3.11.6 ("Messages with embedded links"). */
+ case pp_token::kind::begin_url:
+ {
+ pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
+ sarif_begin_embedded_link (pp);
+ current_url = std::move (sub->m_value);
+ }
+ break;
+ case pp_token::kind::end_url:
+ gcc_assert (current_url.get ());
+ sarif_end_embedded_link (pp, current_url.get ());
+ current_url = label_text::borrow (nullptr);
+ break;
+
+ case pp_token::kind::event_id:
+ {
+ pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
+ gcc_assert (sub->m_event_id.known_p ());
+ const sarif_code_flow *code_flow
+ = m_builder.get_code_flow_for_event_ids ();
+ label_text url = make_sarif_url_for_event (code_flow,
+ sub->m_event_id);
+ if (url.get ())
+ sarif_begin_embedded_link (pp);
+ pp_character (pp, '(');
+ pp_decimal_int (pp, sub->m_event_id.one_based ());
+ pp_character (pp, ')');
+ if (url.get ())
+ sarif_end_embedded_link (pp, url.get ());
+ }
+ break;
+ }
+}
+
/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
to a file). */
static void
-diagnostic_output_format_init_sarif (diagnostic_context &context)
+diagnostic_output_format_init_sarif (diagnostic_context &context,
+ std::unique_ptr<sarif_output_format> fmt)
{
/* Suppress normal textual path output. */
context.set_path_format (DPF_NONE);
/* Don't colorize the text. */
pp_show_color (context.printer) = false;
context.set_show_highlight_colors (false);
+
+ context.printer->set_token_printer
+ (&fmt->get_builder ().get_token_printer ());
+ context.set_output_format (fmt.release ());
}
/* Populate CONTEXT in preparation for SARIF output to stderr. */
bool formatted)
{
gcc_assert (line_maps);
- diagnostic_output_format_init_sarif (context);
- context.set_output_format
- (new sarif_stream_output_format (context,
- line_maps,
- main_input_filename_,
- formatted,
- stderr));
+ diagnostic_output_format_init_sarif
+ (context,
+ ::make_unique<sarif_stream_output_format> (context,
+ line_maps,
+ main_input_filename_,
+ formatted,
+ stderr));
}
/* Populate CONTEXT in preparation for SARIF output to a file named
const char *base_file_name)
{
gcc_assert (line_maps);
- diagnostic_output_format_init_sarif (context);
- context.set_output_format
- (new sarif_file_output_format (context,
- line_maps,
- main_input_filename_,
- formatted,
- base_file_name));
+ diagnostic_output_format_init_sarif
+ (context,
+ ::make_unique<sarif_file_output_format> (context,
+ line_maps,
+ main_input_filename_,
+ formatted,
+ base_file_name));
}
/* Populate CONTEXT in preparation for SARIF output to STREAM. */
FILE *stream)
{
gcc_assert (line_maps);
- diagnostic_output_format_init_sarif (context);
- context.set_output_format
- (new sarif_stream_output_format (context,
- line_maps,
- main_input_filename_,
- formatted,
- stream));
+ diagnostic_output_format_init_sarif
+ (context,
+ ::make_unique<sarif_stream_output_format> (context,
+ line_maps,
+ main_input_filename_,
+ formatted,
+ stream));
}
#if CHECKING_P
public:
test_sarif_diagnostic_context (const char *main_input_filename)
{
- diagnostic_output_format_init_sarif (*this);
-
- m_format = new buffered_output_format (*this,
- line_table,
- main_input_filename,
- true);
- set_output_format (m_format); // give ownership;
+ auto format = ::make_unique<buffered_output_format> (*this,
+ line_table,
+ main_input_filename,
+ true);
+ m_format = format.get (); // borrowed
+ diagnostic_output_format_init_sarif (*this, std::move (format));
}
std::unique_ptr<sarif_log> flush_to_object ()
richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
richloc.set_escape_on_output (true);
- sarif_result result;
+ sarif_result result (0);
std::unique_ptr<sarif_location> location_obj
= builder.make_location_object
}
}
+/* Assuming that a single diagnostic has been emitted within
+ LOG, get a json::object for the result object. */
+
+static const json::object *
+get_result_from_log (const sarif_log *log)
+{
+ auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4
+ ASSERT_EQ (runs->size (), 1);
+
+ // 3.14 "run" object:
+ auto run = (*runs)[0];
+
+ // 3.14.23:
+ auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results");
+ ASSERT_EQ (results->size (), 1);
+
+ // 3.27 "result" object:
+ auto result = (*results)[0];
+ return expect_json_object (SELFTEST_LOCATION, result);
+}
+
+/* Assuming that a single diagnostic has been emitted to
+ DC, get a json::object for the messsage object within
+ the result. */
+
+static const json::object *
+get_message_from_log (const sarif_log *log)
+{
+ auto result_obj = get_result_from_log (log);
+
+ // 3.27.11:
+ auto message_obj
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result_obj, "message");
+ return message_obj;
+}
+
+/* Tests of messages with embedded links; see SARIF v2.1.0 3.11.6. */
+
+static void
+test_message_with_embedded_link ()
+{
+ auto_fix_quotes fix_quotes;
+ {
+ test_sarif_diagnostic_context dc ("test.c");
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+ dc.report (DK_ERROR, richloc, nullptr, 0,
+ "before %{text%} after",
+ "http://example.com");
+ std::unique_ptr<sarif_log> log = dc.flush_to_object ();
+
+ auto message_obj = get_message_from_log (log.get ());
+ ASSERT_JSON_STRING_PROPERTY_EQ
+ (message_obj, "text",
+ "before [text](http://example.com) after");
+ }
+
+ /* Escaping in message text.
+ This is "EXAMPLE 1" from 3.11.6. */
+ {
+ test_sarif_diagnostic_context dc ("test.c");
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+
+ /* Disable "unquoted sequence of 2 consecutive punctuation
+ characters `]\' in format" warning. */
+#if __GNUC__ >= 10
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+ dc.report (DK_ERROR, richloc, nullptr, 0,
+ "Prohibited term used in %{para[0]\\spans[2]%}.",
+ "1");
+#if __GNUC__ >= 10
+# pragma GCC diagnostic pop
+#endif
+
+ std::unique_ptr<sarif_log> log = dc.flush_to_object ();
+
+ auto message_obj = get_message_from_log (log.get ());
+ ASSERT_JSON_STRING_PROPERTY_EQ
+ (message_obj, "text",
+ "Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1).");
+ /* This isn't exactly what EXAMPLE 1 of the spec has; reported as
+ https://github.com/oasis-tcs/sarif-spec/issues/656 */
+ }
+
+ /* Urlifier. */
+ {
+ class test_urlifier : public urlifier
+ {
+ public:
+ char *
+ get_url_for_quoted_text (const char *p, size_t sz) const final override
+ {
+ if (!strncmp (p, "-foption", sz))
+ return xstrdup ("http://example.com");
+ return nullptr;
+ }
+ };
+
+ test_sarif_diagnostic_context dc ("test.c");
+ dc.set_urlifier (new test_urlifier ());
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+ dc.report (DK_ERROR, richloc, nullptr, 0,
+ "foo %<-foption%> %<unrecognized%> bar");
+ std::unique_ptr<sarif_log> log = dc.flush_to_object ();
+
+ auto message_obj = get_message_from_log (log.get ());
+ ASSERT_JSON_STRING_PROPERTY_EQ
+ (message_obj, "text",
+ "foo `[-foption](http://example.com)' `unrecognized' bar");
+ }
+}
+
/* Run all of the selftests within this file. */
void
for_each_line_table_case (test_make_location_object);
test_simple_log ();
for_each_line_table_case (test_simple_log_2);
+ test_message_with_embedded_link ();
}
} // namespace selftest