Skip several gcc.dg/builtin-dynamic-object-size tests on hppa*-*-hpux*
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blob307b2f56c2801053a898960d5011b44e75a3cebb
1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
22 #include "config.h"
23 #define INCLUDE_VECTOR
24 #include "system.h"
25 #include "coretypes.h"
26 #include "diagnostic.h"
27 #include "diagnostic-metadata.h"
28 #include "diagnostic-path.h"
29 #include "json.h"
30 #include "cpplib.h"
31 #include "logical-location.h"
32 #include "diagnostic-client-data-hooks.h"
33 #include "diagnostic-diagram.h"
34 #include "text-art/canvas.h"
35 #include "diagnostic-format-sarif.h"
37 class sarif_builder;
39 /* Subclass of json::object for SARIF invocation objects
40 (SARIF v2.1.0 section 3.20). */
42 class sarif_invocation : public sarif_object
44 public:
45 sarif_invocation ()
46 : m_notifications_arr (new json::array ()),
47 m_success (true)
50 void add_notification_for_ice (diagnostic_context *context,
51 const diagnostic_info &diagnostic,
52 sarif_builder *builder);
53 void prepare_to_flush (diagnostic_context *context);
55 private:
56 json::array *m_notifications_arr;
57 bool m_success;
60 /* Subclass of sarif_object for SARIF result objects
61 (SARIF v2.1.0 section 3.27). */
63 class sarif_result : public sarif_object
65 public:
66 sarif_result () : m_related_locations_arr (NULL) {}
68 void
69 on_nested_diagnostic (diagnostic_context *context,
70 const diagnostic_info &diagnostic,
71 diagnostic_t orig_diag_kind,
72 sarif_builder *builder);
73 void on_diagram (diagnostic_context *context,
74 const diagnostic_diagram &diagram,
75 sarif_builder *builder);
77 private:
78 void add_related_location (json::object *location_obj);
80 json::array *m_related_locations_arr;
83 /* Subclass of sarif_object for SARIF notification objects
84 (SARIF v2.1.0 section 3.58).
86 This subclass is specifically for notifying when an
87 internal compiler error occurs. */
89 class sarif_ice_notification : public sarif_object
91 public:
92 sarif_ice_notification (diagnostic_context *context,
93 const diagnostic_info &diagnostic,
94 sarif_builder *builder);
97 /* Subclass of sarif_object for SARIF threadFlow objects
98 (SARIF v2.1.0 section 3.37) for PATH. */
100 class sarif_thread_flow : public sarif_object
102 public:
103 sarif_thread_flow (const diagnostic_thread &thread);
105 void add_location (json::object *thread_flow_loc_obj)
107 m_locations_arr->append (thread_flow_loc_obj);
110 private:
111 json::array *m_locations_arr;
114 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
115 and -fdiagnostics-format=sarif-file).
117 As diagnostics occur, we build "result" JSON objects, and
118 accumulate state:
119 - which source files are referenced
120 - which warnings are emitted
121 - which CWEs are used
123 At the end of the compile, we use the above to build the full SARIF
124 object tree, adding the result objects to the correct place, and
125 creating objects for the various source files, warnings and CWEs
126 referenced.
128 Implemented:
129 - fix-it hints
130 - CWE metadata
131 - diagnostic groups (see limitations below)
132 - logical locations (e.g. cfun)
134 Known limitations:
135 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
136 but we only capture location and message information from such nested
137 diagnostics (e.g. we ignore fix-it hints on them)
138 - doesn't yet capture command-line arguments: would be run.invocations
139 property (SARIF v2.1.0 section 3.14.11), as invocation objects
140 (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
141 toplev::main, and the response files.
142 - doesn't capture escape_on_output_p
143 - doesn't capture secondary locations within a rich_location
144 (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
145 section 3.27.22)
146 - doesn't capture "artifact.encoding" property
147 (SARIF v2.1.0 section 3.24.9).
148 - doesn't capture hashes of the source files
149 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
150 - doesn't capture the "analysisTarget" property
151 (SARIF v2.1.0 section 3.27.13).
152 - doesn't capture labelled ranges
153 - doesn't capture -Werror cleanly
154 - doesn't capture inlining information (can SARIF handle this?)
155 - doesn't capture macro expansion information (can SARIF handle this?). */
157 class sarif_builder
159 public:
160 sarif_builder (diagnostic_context *context,
161 bool formatted);
163 void end_diagnostic (diagnostic_context *context,
164 const diagnostic_info &diagnostic,
165 diagnostic_t orig_diag_kind);
166 void emit_diagram (diagnostic_context *context,
167 const diagnostic_diagram &diagram);
168 void end_group ();
170 void flush_to_file (FILE *outf);
172 json::array *make_locations_arr (const diagnostic_info &diagnostic);
173 json::object *make_location_object (const rich_location &rich_loc,
174 const logical_location *logical_loc);
175 json::object *make_message_object (const char *msg) const;
176 json::object *
177 make_message_object_for_diagram (diagnostic_context *context,
178 const diagnostic_diagram &diagram);
180 private:
181 sarif_result *make_result_object (diagnostic_context *context,
182 const diagnostic_info &diagnostic,
183 diagnostic_t orig_diag_kind);
184 void set_any_logical_locs_arr (json::object *location_obj,
185 const logical_location *logical_loc);
186 json::object *make_location_object (const diagnostic_event &event);
187 json::object *make_code_flow_object (const diagnostic_path &path);
188 json::object *
189 make_thread_flow_location_object (const diagnostic_event &event,
190 int path_event_idx);
191 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
192 json::object *maybe_make_physical_location_object (location_t loc);
193 json::object *make_artifact_location_object (location_t loc);
194 json::object *make_artifact_location_object (const char *filename);
195 json::object *make_artifact_location_object_for_pwd () const;
196 json::object *maybe_make_region_object (location_t loc) const;
197 json::object *maybe_make_region_object_for_context (location_t loc) const;
198 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
199 json::object *make_multiformat_message_string (const char *msg) const;
200 json::object *make_top_level_object (sarif_invocation *invocation_obj,
201 json::array *results);
202 json::object *make_run_object (sarif_invocation *invocation_obj,
203 json::array *results);
204 json::object *make_tool_object () const;
205 json::object *make_driver_tool_component_object () const;
206 json::array *maybe_make_taxonomies_array () const;
207 json::object *maybe_make_cwe_taxonomy_object () const;
208 json::object *make_tool_component_reference_object_for_cwe () const;
209 json::object *
210 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
211 const diagnostic_info &diagnostic,
212 diagnostic_t orig_diag_kind,
213 const char *option_text);
214 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
215 json::object *
216 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
217 json::object *make_artifact_object (const char *filename);
218 char *get_source_lines (const char *filename,
219 int start_line,
220 int end_line) const;
221 json::object *maybe_make_artifact_content_object (const char *filename) const;
222 json::object *maybe_make_artifact_content_object (const char *filename,
223 int start_line,
224 int end_line) const;
225 json::object *make_fix_object (const rich_location &rich_loc);
226 json::object *make_artifact_change_object (const rich_location &richloc);
227 json::object *make_replacement_object (const fixit_hint &hint) const;
228 json::object *make_artifact_content_object (const char *text) const;
229 int get_sarif_column (expanded_location exploc) const;
231 diagnostic_context *m_context;
233 /* The JSON object for the invocation object. */
234 sarif_invocation *m_invocation_obj;
236 /* The JSON array of pending diagnostics. */
237 json::array *m_results_array;
239 /* The JSON object for the result object (if any) in the current
240 diagnostic group. */
241 sarif_result *m_cur_group_result;
243 hash_set <const char *> m_filenames;
244 bool m_seen_any_relative_paths;
245 hash_set <free_string_hash> m_rule_id_set;
246 json::array *m_rules_arr;
248 /* The set of all CWE IDs we've seen, if any. */
249 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
251 int m_tabstop;
253 bool m_formatted;
256 /* class sarif_object : public json::object. */
258 sarif_property_bag &
259 sarif_object::get_or_create_properties ()
261 json::value *properties_val = get ("properties");
262 if (properties_val)
264 if (properties_val->get_kind () == json::JSON_OBJECT)
265 return *static_cast <sarif_property_bag *> (properties_val);
268 sarif_property_bag *bag = new sarif_property_bag ();
269 set ("properties", bag);
270 return *bag;
273 /* class sarif_invocation : public sarif_object. */
275 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
276 Add an object representing the ICE to the notifications array. */
278 void
279 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
280 const diagnostic_info &diagnostic,
281 sarif_builder *builder)
283 m_success = false;
285 sarif_ice_notification *notification_obj
286 = new sarif_ice_notification (context, diagnostic, builder);
287 m_notifications_arr->append (notification_obj);
290 void
291 sarif_invocation::prepare_to_flush (diagnostic_context *context)
293 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
294 set_bool ("executionSuccessful", m_success);
296 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
297 set ("toolExecutionNotifications", m_notifications_arr);
299 /* Call client hook, allowing it to create a custom property bag for
300 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
301 if (auto client_data_hooks = context->get_client_data_hooks ())
302 client_data_hooks->add_sarif_invocation_properties (*this);
305 /* class sarif_result : public sarif_object. */
307 /* Handle secondary diagnostics that occur within a diagnostic group.
308 The closest SARIF seems to have to nested diagnostics is the
309 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
310 so we lazily set this property and populate the array if and when
311 secondary diagnostics occur (such as notes to a warning). */
313 void
314 sarif_result::on_nested_diagnostic (diagnostic_context *context,
315 const diagnostic_info &diagnostic,
316 diagnostic_t /*orig_diag_kind*/,
317 sarif_builder *builder)
319 /* We don't yet generate meaningful logical locations for notes;
320 sometimes these will related to current_function_decl, but
321 often they won't. */
322 json::object *location_obj
323 = builder->make_location_object (*diagnostic.richloc, NULL);
324 json::object *message_obj
325 = builder->make_message_object (pp_formatted_text (context->printer));
326 pp_clear_output_area (context->printer);
327 location_obj->set ("message", message_obj);
329 add_related_location (location_obj);
332 /* Handle diagrams that occur within a diagnostic group.
333 The closest thing in SARIF seems to be to add a location to the
334 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
335 and to put the diagram into the "message" property of that location
336 (SARIF v2.1.0 section 3.28.5). */
338 void
339 sarif_result::on_diagram (diagnostic_context *context,
340 const diagnostic_diagram &diagram,
341 sarif_builder *builder)
343 json::object *location_obj = new json::object ();
344 json::object *message_obj
345 = builder->make_message_object_for_diagram (context, diagram);
346 location_obj->set ("message", message_obj);
348 add_related_location (location_obj);
351 /* Add LOCATION_OBJ to this result's "relatedLocations" array,
352 creating it if it doesn't yet exist. */
354 void
355 sarif_result::add_related_location (json::object *location_obj)
357 if (!m_related_locations_arr)
359 m_related_locations_arr = new json::array ();
360 set ("relatedLocations", m_related_locations_arr);
362 m_related_locations_arr->append (location_obj);
365 /* class sarif_ice_notification : public sarif_object. */
367 /* sarif_ice_notification's ctor.
368 DIAGNOSTIC is an internal compiler error. */
370 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
371 const diagnostic_info &diagnostic,
372 sarif_builder *builder)
374 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
375 json::array *locations_arr = builder->make_locations_arr (diagnostic);
376 set ("locations", locations_arr);
378 /* "message" property (SARIF v2.1.0 section 3.85.5). */
379 json::object *message_obj
380 = builder->make_message_object (pp_formatted_text (context->printer));
381 pp_clear_output_area (context->printer);
382 set ("message", message_obj);
384 /* "level" property (SARIF v2.1.0 section 3.58.6). */
385 set_string ("level", "error");
388 /* class sarif_thread_flow : public sarif_object. */
390 sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
392 /* "id" property (SARIF v2.1.0 section 3.37.2). */
393 label_text name (thread.get_name (false));
394 set_string ("id", name.get ());
396 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
397 m_locations_arr = new json::array ();
398 set ("locations", m_locations_arr);
401 /* class sarif_builder. */
403 /* sarif_builder's ctor. */
405 sarif_builder::sarif_builder (diagnostic_context *context,
406 bool formatted)
407 : m_context (context),
408 m_invocation_obj (new sarif_invocation ()),
409 m_results_array (new json::array ()),
410 m_cur_group_result (NULL),
411 m_seen_any_relative_paths (false),
412 m_rule_id_set (),
413 m_rules_arr (new json::array ()),
414 m_tabstop (context->m_tabstop),
415 m_formatted (formatted)
419 /* Implementation of "end_diagnostic" for SARIF output. */
421 void
422 sarif_builder::end_diagnostic (diagnostic_context *context,
423 const diagnostic_info &diagnostic,
424 diagnostic_t orig_diag_kind)
426 if (diagnostic.kind == DK_ICE || diagnostic.kind == DK_ICE_NOBT)
428 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
429 return;
432 if (m_cur_group_result)
433 /* Nested diagnostic. */
434 m_cur_group_result->on_nested_diagnostic (context,
435 diagnostic,
436 orig_diag_kind,
437 this);
438 else
440 /* Top-level diagnostic. */
441 sarif_result *result_obj
442 = make_result_object (context, diagnostic, orig_diag_kind);
443 m_results_array->append (result_obj);
444 m_cur_group_result = result_obj;
448 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
449 for SARIF output. */
451 void
452 sarif_builder::emit_diagram (diagnostic_context *context,
453 const diagnostic_diagram &diagram)
455 /* We must be within the emission of a top-level diagnostic. */
456 gcc_assert (m_cur_group_result);
457 m_cur_group_result->on_diagram (context, diagram, this);
460 /* Implementation of "end_group_cb" for SARIF output. */
462 void
463 sarif_builder::end_group ()
465 m_cur_group_result = NULL;
468 /* Create a top-level object, and add it to all the results
469 (and other entities) we've seen so far.
471 Flush it all to OUTF. */
473 void
474 sarif_builder::flush_to_file (FILE *outf)
476 m_invocation_obj->prepare_to_flush (m_context);
477 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
478 top->dump (outf, m_formatted);
479 m_invocation_obj = NULL;
480 m_results_array = NULL;
481 fprintf (outf, "\n");
482 delete top;
485 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
486 property (SARIF v2.1.0 section 3.27.10).
488 Return NULL if there isn't one. */
490 static const char *
491 maybe_get_sarif_level (diagnostic_t diag_kind)
493 switch (diag_kind)
495 case DK_WARNING:
496 return "warning";
497 case DK_ERROR:
498 return "error";
499 case DK_NOTE:
500 case DK_ANACHRONISM:
501 return "note";
502 default:
503 return NULL;
507 /* Make a string for DIAG_KIND suitable for use a ruleId
508 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
509 have anything better to use. */
511 static char *
512 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
514 static const char *const diagnostic_kind_text[] = {
515 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
516 #include "diagnostic.def"
517 #undef DEFINE_DIAGNOSTIC_KIND
518 "must-not-happen"
520 /* Lose the trailing ": ". */
521 const char *kind_text = diagnostic_kind_text[diag_kind];
522 size_t len = strlen (kind_text);
523 gcc_assert (len > 2);
524 gcc_assert (kind_text[len - 2] == ':');
525 gcc_assert (kind_text[len - 1] == ' ');
526 char *rstrip = xstrdup (kind_text);
527 rstrip[len - 2] = '\0';
528 return rstrip;
531 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
533 sarif_result *
534 sarif_builder::make_result_object (diagnostic_context *context,
535 const diagnostic_info &diagnostic,
536 diagnostic_t orig_diag_kind)
538 sarif_result *result_obj = new sarif_result ();
540 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
541 /* Ideally we'd have an option_name for these. */
542 if (char *option_text
543 = context->make_option_name (diagnostic.option_index,
544 orig_diag_kind, diagnostic.kind))
546 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
547 Set ruleId referencing them. */
548 result_obj->set_string ("ruleId", option_text);
549 if (m_rule_id_set.contains (option_text))
550 free (option_text);
551 else
553 /* This is the first time we've seen this ruleId. */
554 /* Add to set, taking ownership. */
555 m_rule_id_set.add (option_text);
557 json::object *reporting_desc_obj
558 = make_reporting_descriptor_object_for_warning (context,
559 diagnostic,
560 orig_diag_kind,
561 option_text);
562 m_rules_arr->append (reporting_desc_obj);
565 else
567 /* Otherwise, we have an "error" or a stray "note"; use the
568 diagnostic kind as the ruleId, so that the result object at least
569 has a ruleId.
570 We don't bother creating reportingDescriptor objects for these. */
571 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
572 result_obj->set_string ("ruleId", rule_id);
573 free (rule_id);
576 if (diagnostic.metadata)
578 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
579 if (int cwe_id = diagnostic.metadata->get_cwe ())
581 json::array *taxa_arr = new json::array ();
582 json::object *cwe_id_obj
583 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
584 taxa_arr->append (cwe_id_obj);
585 result_obj->set ("taxa", taxa_arr);
588 diagnostic.metadata->maybe_add_sarif_properties (*result_obj);
591 /* "level" property (SARIF v2.1.0 section 3.27.10). */
592 if (const char *sarif_level = maybe_get_sarif_level (diagnostic.kind))
593 result_obj->set_string ("level", sarif_level);
595 /* "message" property (SARIF v2.1.0 section 3.27.11). */
596 json::object *message_obj
597 = make_message_object (pp_formatted_text (context->printer));
598 pp_clear_output_area (context->printer);
599 result_obj->set ("message", message_obj);
601 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
602 json::array *locations_arr = make_locations_arr (diagnostic);
603 result_obj->set ("locations", locations_arr);
605 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
606 if (const diagnostic_path *path = diagnostic.richloc->get_path ())
608 json::array *code_flows_arr = new json::array ();
609 json::object *code_flow_obj = make_code_flow_object (*path);
610 code_flows_arr->append (code_flow_obj);
611 result_obj->set ("codeFlows", code_flows_arr);
614 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
615 set up later, if any nested diagnostics occur within this diagnostic
616 group. */
618 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
619 const rich_location *richloc = diagnostic.richloc;
620 if (richloc->get_num_fixit_hints ())
622 json::array *fix_arr = new json::array ();
623 json::object *fix_obj = make_fix_object (*richloc);
624 fix_arr->append (fix_obj);
625 result_obj->set ("fixes", fix_arr);
628 return result_obj;
631 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
632 for a GCC warning. */
634 json::object *
635 sarif_builder::
636 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
637 const diagnostic_info &diagnostic,
638 diagnostic_t /*orig_diag_kind*/,
639 const char *option_text)
641 json::object *reporting_desc = new json::object ();
643 /* "id" property (SARIF v2.1.0 section 3.49.3). */
644 reporting_desc->set_string ("id", option_text);
646 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
647 it seems redundant compared to "id". */
649 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
650 if (char *option_url = context->make_option_url (diagnostic.option_index))
652 reporting_desc->set_string ("helpUri", option_url);
653 free (option_url);
656 return reporting_desc;
659 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
660 for CWE_ID, for use within the CWE taxa array. */
662 json::object *
663 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
665 json::object *reporting_desc = new json::object ();
667 /* "id" property (SARIF v2.1.0 section 3.49.3). */
669 pretty_printer pp;
670 pp_printf (&pp, "%i", cwe_id);
671 reporting_desc->set_string ("id", pp_formatted_text (&pp));
674 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
676 char *url = get_cwe_url (cwe_id);
677 reporting_desc->set_string ("helpUri", url);
678 free (url);
681 return reporting_desc;
684 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
685 referencing CWE_ID, for use within a result object.
686 Also, add CWE_ID to m_cwe_id_set. */
688 json::object *
689 sarif_builder::
690 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
692 json::object *desc_ref_obj = new json::object ();
694 /* "id" property (SARIF v2.1.0 section 3.52.4). */
696 pretty_printer pp;
697 pp_printf (&pp, "%i", cwe_id);
698 desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
701 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
702 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
703 desc_ref_obj->set ("toolComponent", comp_ref_obj);
705 /* Add CWE_ID to our set. */
706 gcc_assert (cwe_id > 0);
707 m_cwe_id_set.add (cwe_id);
709 return desc_ref_obj;
712 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
713 references the CWE taxonomy. */
715 json::object *
716 sarif_builder::
717 make_tool_component_reference_object_for_cwe () const
719 json::object *comp_ref_obj = new json::object ();
721 /* "name" property (SARIF v2.1.0 section 3.54.3). */
722 comp_ref_obj->set_string ("name", "cwe");
724 return comp_ref_obj;
727 /* Make an array suitable for use as the "locations" property of:
728 - a "result" object (SARIF v2.1.0 section 3.27.12), or
729 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
731 json::array *
732 sarif_builder::make_locations_arr (const diagnostic_info &diagnostic)
734 json::array *locations_arr = new json::array ();
735 const logical_location *logical_loc = NULL;
736 if (auto client_data_hooks = m_context->get_client_data_hooks ())
737 logical_loc = client_data_hooks->get_current_logical_location ();
739 json::object *location_obj
740 = make_location_object (*diagnostic.richloc, logical_loc);
741 locations_arr->append (location_obj);
742 return locations_arr;
745 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
746 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
748 void
749 sarif_builder::
750 set_any_logical_locs_arr (json::object *location_obj,
751 const logical_location *logical_loc)
753 if (!logical_loc)
754 return;
755 json::object *logical_loc_obj = make_sarif_logical_location_object (*logical_loc);
756 json::array *location_locs_arr = new json::array ();
757 location_locs_arr->append (logical_loc_obj);
758 location_obj->set ("logicalLocations", location_locs_arr);
761 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
762 and LOGICAL_LOC. */
764 json::object *
765 sarif_builder::make_location_object (const rich_location &rich_loc,
766 const logical_location *logical_loc)
768 json::object *location_obj = new json::object ();
770 /* Get primary loc from RICH_LOC. */
771 location_t loc = rich_loc.get_loc ();
773 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
774 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
775 location_obj->set ("physicalLocation", phs_loc_obj);
777 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
778 set_any_logical_locs_arr (location_obj, logical_loc);
780 return location_obj;
783 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
784 within a diagnostic_path. */
786 json::object *
787 sarif_builder::make_location_object (const diagnostic_event &event)
789 json::object *location_obj = new json::object ();
791 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
792 location_t loc = event.get_location ();
793 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
794 location_obj->set ("physicalLocation", phs_loc_obj);
796 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
797 const logical_location *logical_loc = event.get_logical_location ();
798 set_any_logical_locs_arr (location_obj, logical_loc);
800 /* "message" property (SARIF v2.1.0 section 3.28.5). */
801 label_text ev_desc = event.get_desc (false);
802 json::object *message_obj = make_message_object (ev_desc.get ());
803 location_obj->set ("message", message_obj);
805 return location_obj;
808 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
809 or return NULL;
810 Add any filename to the m_artifacts. */
812 json::object *
813 sarif_builder::maybe_make_physical_location_object (location_t loc)
815 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
816 return NULL;
818 json::object *phys_loc_obj = new json::object ();
820 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
821 json::object *artifact_loc_obj = make_artifact_location_object (loc);
822 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
823 m_filenames.add (LOCATION_FILE (loc));
825 /* "region" property (SARIF v2.1.0 section 3.29.4). */
826 if (json::object *region_obj = maybe_make_region_object (loc))
827 phys_loc_obj->set ("region", region_obj);
829 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
830 if (json::object *context_region_obj
831 = maybe_make_region_object_for_context (loc))
832 phys_loc_obj->set ("contextRegion", context_region_obj);
834 /* Instead, we add artifacts to the run as a whole,
835 with artifact.contents.
836 Could do both, though. */
838 return phys_loc_obj;
841 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
842 or return NULL. */
844 json::object *
845 sarif_builder::make_artifact_location_object (location_t loc)
847 return make_artifact_location_object (LOCATION_FILE (loc));
850 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
851 for when we need to express paths relative to PWD. */
853 #define PWD_PROPERTY_NAME ("PWD")
855 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
856 or return NULL. */
858 json::object *
859 sarif_builder::make_artifact_location_object (const char *filename)
861 json::object *artifact_loc_obj = new json::object ();
863 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
864 artifact_loc_obj->set_string ("uri", filename);
866 if (filename[0] != '/')
868 /* If we have a relative path, set the "uriBaseId" property
869 (SARIF v2.1.0 section 3.4.4). */
870 artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
871 m_seen_any_relative_paths = true;
874 return artifact_loc_obj;
877 /* Get the PWD, or NULL, as an absolute file-based URI,
878 adding a trailing forward slash (as required by SARIF v2.1.0
879 section 3.14.14). */
881 static char *
882 make_pwd_uri_str ()
884 /* The prefix of a file-based URI, up to, but not including the path. */
885 #define FILE_PREFIX ("file://")
887 const char *pwd = getpwd ();
888 if (!pwd)
889 return NULL;
890 size_t len = strlen (pwd);
891 if (len == 0 || pwd[len - 1] != '/')
892 return concat (FILE_PREFIX, pwd, "/", NULL);
893 else
895 gcc_assert (pwd[len - 1] == '/');
896 return concat (FILE_PREFIX, pwd, NULL);
900 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
901 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
902 section 3.14.14) when we have any relative paths. */
904 json::object *
905 sarif_builder::make_artifact_location_object_for_pwd () const
907 json::object *artifact_loc_obj = new json::object ();
909 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
910 if (char *pwd = make_pwd_uri_str ())
912 gcc_assert (strlen (pwd) > 0);
913 gcc_assert (pwd[strlen (pwd) - 1] == '/');
914 artifact_loc_obj->set_string ("uri", pwd);
915 free (pwd);
918 return artifact_loc_obj;
921 /* Get the column number within EXPLOC. */
924 sarif_builder::get_sarif_column (expanded_location exploc) const
926 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
927 return location_compute_display_column (m_context->get_file_cache (),
928 exploc, policy);
931 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
932 or return NULL. */
934 json::object *
935 sarif_builder::maybe_make_region_object (location_t loc) const
937 location_t caret_loc = get_pure_location (loc);
939 if (caret_loc <= BUILTINS_LOCATION)
940 return NULL;
942 location_t start_loc = get_start (loc);
943 location_t finish_loc = get_finish (loc);
945 expanded_location exploc_caret = expand_location (caret_loc);
946 expanded_location exploc_start = expand_location (start_loc);
947 expanded_location exploc_finish = expand_location (finish_loc);
949 if (exploc_start.file !=exploc_caret.file)
950 return NULL;
951 if (exploc_finish.file !=exploc_caret.file)
952 return NULL;
954 json::object *region_obj = new json::object ();
956 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
957 region_obj->set_integer ("startLine", exploc_start.line);
959 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
960 region_obj->set_integer ("startColumn", get_sarif_column (exploc_start));
962 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
963 if (exploc_finish.line != exploc_start.line)
964 region_obj->set_integer ("endLine", exploc_finish.line);
966 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
967 This expresses the column immediately beyond the range. */
969 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
970 region_obj->set_integer ("endColumn", next_column);
973 return region_obj;
976 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
977 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
979 This is similar to maybe_make_region_object, but ignores column numbers,
980 covering the line(s) as a whole, and including a "snippet" property
981 embedding those source lines, making it easier for consumers to show
982 the pertinent source. */
984 json::object *
985 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
987 location_t caret_loc = get_pure_location (loc);
989 if (caret_loc <= BUILTINS_LOCATION)
990 return NULL;
992 location_t start_loc = get_start (loc);
993 location_t finish_loc = get_finish (loc);
995 expanded_location exploc_caret = expand_location (caret_loc);
996 expanded_location exploc_start = expand_location (start_loc);
997 expanded_location exploc_finish = expand_location (finish_loc);
999 if (exploc_start.file !=exploc_caret.file)
1000 return NULL;
1001 if (exploc_finish.file !=exploc_caret.file)
1002 return NULL;
1004 json::object *region_obj = new json::object ();
1006 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1007 region_obj->set_integer ("startLine", exploc_start.line);
1009 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1010 if (exploc_finish.line != exploc_start.line)
1011 region_obj->set_integer ("endLine", exploc_finish.line);
1013 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1014 if (json::object *artifact_content_obj
1015 = maybe_make_artifact_content_object (exploc_start.file,
1016 exploc_start.line,
1017 exploc_finish.line))
1018 region_obj->set ("snippet", artifact_content_obj);
1020 return region_obj;
1023 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1024 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1026 json::object *
1027 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1029 location_t start_loc = hint.get_start_loc ();
1030 location_t next_loc = hint.get_next_loc ();
1032 expanded_location exploc_start = expand_location (start_loc);
1033 expanded_location exploc_next = expand_location (next_loc);
1035 json::object *region_obj = new json::object ();
1037 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1038 region_obj->set_integer ("startLine", exploc_start.line);
1040 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1041 int start_col = get_sarif_column (exploc_start);
1042 region_obj->set_integer ("startColumn", start_col);
1044 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1045 if (exploc_next.line != exploc_start.line)
1046 region_obj->set_integer ("endLine", exploc_next.line);
1048 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1049 This expresses the column immediately beyond the range. */
1050 int next_col = get_sarif_column (exploc_next);
1051 region_obj->set_integer ("endColumn", next_col);
1053 return region_obj;
1056 /* Attempt to get a string for a logicalLocation's "kind" property
1057 (SARIF v2.1.0 section 3.33.7).
1058 Return NULL if unknown. */
1060 static const char *
1061 maybe_get_sarif_kind (enum logical_location_kind kind)
1063 switch (kind)
1065 default:
1066 gcc_unreachable ();
1067 case LOGICAL_LOCATION_KIND_UNKNOWN:
1068 return NULL;
1070 case LOGICAL_LOCATION_KIND_FUNCTION:
1071 return "function";
1072 case LOGICAL_LOCATION_KIND_MEMBER:
1073 return "member";
1074 case LOGICAL_LOCATION_KIND_MODULE:
1075 return "module";
1076 case LOGICAL_LOCATION_KIND_NAMESPACE:
1077 return "namespace";
1078 case LOGICAL_LOCATION_KIND_TYPE:
1079 return "type";
1080 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1081 return "returnType";
1082 case LOGICAL_LOCATION_KIND_PARAMETER:
1083 return "parameter";
1084 case LOGICAL_LOCATION_KIND_VARIABLE:
1085 return "variable";
1089 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1090 or return NULL. */
1092 json::object *
1093 make_sarif_logical_location_object (const logical_location &logical_loc)
1095 json::object *logical_loc_obj = new json::object ();
1097 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1098 if (const char *short_name = logical_loc.get_short_name ())
1099 logical_loc_obj->set_string ("name", short_name);
1101 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1102 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1103 logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope);
1105 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1106 if (const char *internal_name = logical_loc.get_internal_name ())
1107 logical_loc_obj->set_string ("decoratedName", internal_name);
1109 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1110 enum logical_location_kind kind = logical_loc.get_kind ();
1111 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1112 logical_loc_obj->set_string ("kind", sarif_kind_str);
1114 return logical_loc_obj;
1117 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1119 json::object *
1120 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1122 json::object *code_flow_obj = new json::object ();
1124 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1125 json::array *thread_flows_arr = new json::array ();
1127 /* Walk the events, consolidating into per-thread threadFlow objects,
1128 using the index with PATH as the overall executionOrder. */
1129 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1130 sarif_thread_flow *> thread_id_map;
1131 for (unsigned i = 0; i < path.num_events (); i++)
1133 const diagnostic_event &event = path.get_event (i);
1134 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1135 sarif_thread_flow *thread_flow_obj;
1137 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1138 thread_flow_obj = *slot;
1139 else
1141 const diagnostic_thread &thread = path.get_thread (thread_id);
1142 thread_flow_obj = new sarif_thread_flow (thread);
1143 thread_flows_arr->append (thread_flow_obj);
1144 thread_id_map.put (thread_id, thread_flow_obj);
1147 /* Add event to thread's threadFlow object. */
1148 json::object *thread_flow_loc_obj
1149 = make_thread_flow_location_object (event, i);
1150 thread_flow_obj->add_location (thread_flow_loc_obj);
1152 code_flow_obj->set ("threadFlows", thread_flows_arr);
1154 return code_flow_obj;
1157 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1159 json::object *
1160 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1161 int path_event_idx)
1163 sarif_object *thread_flow_loc_obj = new sarif_object ();
1165 /* Give diagnostic_event subclasses a chance to add custom properties
1166 via a property bag. */
1167 ev.maybe_add_sarif_properties (*thread_flow_loc_obj);
1169 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1170 json::object *location_obj = make_location_object (ev);
1171 thread_flow_loc_obj->set ("location", location_obj);
1173 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1174 diagnostic_event::meaning m = ev.get_meaning ();
1175 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1176 thread_flow_loc_obj->set ("kinds", kinds_arr);
1178 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1179 thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
1181 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1182 Offset by 1 to match the human-readable values emitted by %@. */
1183 thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
1185 /* It might be nice to eventually implement the following for -fanalyzer:
1186 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1187 - the "state" property (SARIF v2.1.0 section 3.38.9)
1188 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1190 return thread_flow_loc_obj;
1193 /* If M has any known meaning, make a json array suitable for the "kinds"
1194 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1196 Otherwise, return NULL. */
1198 json::array *
1199 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1201 if (m.m_verb == diagnostic_event::VERB_unknown
1202 && m.m_noun == diagnostic_event::NOUN_unknown
1203 && m.m_property == diagnostic_event::PROPERTY_unknown)
1204 return NULL;
1206 json::array *kinds_arr = new json::array ();
1207 if (const char *verb_str
1208 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1209 kinds_arr->append (new json::string (verb_str));
1210 if (const char *noun_str
1211 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1212 kinds_arr->append (new json::string (noun_str));
1213 if (const char *property_str
1214 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1215 kinds_arr->append (new json::string (property_str));
1216 return kinds_arr;
1219 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1221 json::object *
1222 sarif_builder::make_message_object (const char *msg) const
1224 json::object *message_obj = new json::object ();
1226 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1227 message_obj->set_string ("text", msg);
1229 return message_obj;
1232 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1233 We emit the diagram as a code block within the Markdown part
1234 of the message. */
1236 json::object *
1237 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1238 const diagnostic_diagram &diagram)
1240 json::object *message_obj = new json::object ();
1242 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1243 message_obj->set_string ("text", diagram.get_alt_text ());
1245 char *saved_prefix = pp_take_prefix (context->printer);
1246 pp_set_prefix (context->printer, NULL);
1248 /* "To produce a code block in Markdown, simply indent every line of
1249 the block by at least 4 spaces or 1 tab."
1250 Here we use 4 spaces. */
1251 diagram.get_canvas ().print_to_pp (context->printer, " ");
1252 pp_set_prefix (context->printer, saved_prefix);
1254 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1255 message_obj->set_string ("markdown", pp_formatted_text (context->printer));
1257 pp_clear_output_area (context->printer);
1259 return message_obj;
1262 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1263 for MSG. */
1265 json::object *
1266 sarif_builder::make_multiformat_message_string (const char *msg) const
1268 json::object *message_obj = new json::object ();
1270 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1271 message_obj->set_string ("text", msg);
1273 return message_obj;
1276 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1277 #define SARIF_VERSION "2.1.0"
1279 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1280 Take ownership of INVOCATION_OBJ and RESULTS. */
1282 json::object *
1283 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1284 json::array *results)
1286 json::object *log_obj = new json::object ();
1288 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1289 log_obj->set_string ("$schema", SARIF_SCHEMA);
1291 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1292 log_obj->set_string ("version", SARIF_VERSION);
1294 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1295 json::array *run_arr = new json::array ();
1296 json::object *run_obj = make_run_object (invocation_obj, results);
1297 run_arr->append (run_obj);
1298 log_obj->set ("runs", run_arr);
1300 return log_obj;
1303 /* Make a run object (SARIF v2.1.0 section 3.14).
1304 Take ownership of INVOCATION_OBJ and RESULTS. */
1306 json::object *
1307 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1308 json::array *results)
1310 json::object *run_obj = new json::object ();
1312 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1313 json::object *tool_obj = make_tool_object ();
1314 run_obj->set ("tool", tool_obj);
1316 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1317 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1318 run_obj->set ("taxonomies", taxonomies_arr);
1320 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1322 json::array *invocations_arr = new json::array ();
1323 invocations_arr->append (invocation_obj);
1324 run_obj->set ("invocations", invocations_arr);
1327 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1328 if (m_seen_any_relative_paths)
1330 json::object *orig_uri_base_ids = new json::object ();
1331 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1332 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1333 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1336 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1337 json::array *artifacts_arr = new json::array ();
1338 for (auto iter : m_filenames)
1340 json::object *artifact_obj = make_artifact_object (iter);
1341 artifacts_arr->append (artifact_obj);
1343 run_obj->set ("artifacts", artifacts_arr);
1345 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1346 run_obj->set ("results", results);
1348 return run_obj;
1351 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1353 json::object *
1354 sarif_builder::make_tool_object () const
1356 json::object *tool_obj = new json::object ();
1358 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1359 json::object *driver_obj = make_driver_tool_component_object ();
1360 tool_obj->set ("driver", driver_obj);
1362 /* Report plugins via the "extensions" property
1363 (SARIF v2.1.0 section 3.18.3). */
1364 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1365 if (const client_version_info *vinfo
1366 = client_data_hooks->get_any_version_info ())
1368 class my_plugin_visitor : public client_version_info :: plugin_visitor
1370 public:
1371 void on_plugin (const diagnostic_client_plugin_info &p) final override
1373 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1374 for the plugin. */
1375 json::object *plugin_obj = new json::object ();
1376 m_plugin_objs.safe_push (plugin_obj);
1378 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1379 if (const char *short_name = p.get_short_name ())
1380 plugin_obj->set_string ("name", short_name);
1382 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1383 if (const char *full_name = p.get_full_name ())
1384 plugin_obj->set_string ("fullName", full_name);
1386 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1387 if (const char *version = p.get_version ())
1388 plugin_obj->set_string ("version", version);
1390 auto_vec <json::object *> m_plugin_objs;
1392 my_plugin_visitor v;
1393 vinfo->for_each_plugin (v);
1394 if (v.m_plugin_objs.length () > 0)
1396 json::array *extensions_arr = new json::array ();
1397 tool_obj->set ("extensions", extensions_arr);
1398 for (auto iter : v.m_plugin_objs)
1399 extensions_arr->append (iter);
1403 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1404 "extensions" (see toplev.cc: print_version). */
1406 return tool_obj;
1409 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1410 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1412 json::object *
1413 sarif_builder::make_driver_tool_component_object () const
1415 json::object *driver_obj = new json::object ();
1417 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1418 if (const client_version_info *vinfo
1419 = client_data_hooks->get_any_version_info ())
1421 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1422 if (const char *name = vinfo->get_tool_name ())
1423 driver_obj->set_string ("name", name);
1425 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1426 if (char *full_name = vinfo->maybe_make_full_name ())
1428 driver_obj->set_string ("fullName", full_name);
1429 free (full_name);
1432 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1433 if (const char *version = vinfo->get_version_string ())
1434 driver_obj->set_string ("version", version);
1436 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1437 if (char *version_url = vinfo->maybe_make_version_url ())
1439 driver_obj->set_string ("informationUri", version_url);
1440 free (version_url);
1444 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1445 driver_obj->set ("rules", m_rules_arr);
1447 return driver_obj;
1450 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1451 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1452 toolComponent (3.19) as per 3.19.3, representing the CWE.
1454 Otherwise return NULL. */
1456 json::array *
1457 sarif_builder::maybe_make_taxonomies_array () const
1459 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1460 if (!cwe_obj)
1461 return NULL;
1463 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1464 json::array *taxonomies_arr = new json::array ();
1465 taxonomies_arr->append (cwe_obj);
1466 return taxonomies_arr;
1469 /* If we've seen any CWE IDs, make a toolComponent object
1470 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1471 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1473 Otherwise return NULL. */
1475 json::object *
1476 sarif_builder::maybe_make_cwe_taxonomy_object () const
1478 if (m_cwe_id_set.is_empty ())
1479 return NULL;
1481 json::object *taxonomy_obj = new json::object ();
1483 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1484 taxonomy_obj->set_string ("name", "CWE");
1486 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1487 taxonomy_obj->set_string ("version", "4.7");
1489 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1490 taxonomy_obj->set_string ("organization", "MITRE");
1492 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1493 json::object *short_desc
1494 = make_multiformat_message_string ("The MITRE"
1495 " Common Weakness Enumeration");
1496 taxonomy_obj->set ("shortDescription", short_desc);
1498 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1499 json::array *taxa_arr = new json::array ();
1500 for (auto cwe_id : m_cwe_id_set)
1502 json::object *cwe_taxon
1503 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1504 taxa_arr->append (cwe_taxon);
1506 taxonomy_obj->set ("taxa", taxa_arr);
1508 return taxonomy_obj;
1511 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1513 json::object *
1514 sarif_builder::make_artifact_object (const char *filename)
1516 json::object *artifact_obj = new json::object ();
1518 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1519 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1520 artifact_obj->set ("location", artifact_loc_obj);
1522 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1523 if (json::object *artifact_content_obj
1524 = maybe_make_artifact_content_object (filename))
1525 artifact_obj->set ("contents", artifact_content_obj);
1527 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1528 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1529 if (const char *source_lang
1530 = client_data_hooks->maybe_get_sarif_source_language (filename))
1531 artifact_obj->set_string ("sourceLanguage", source_lang);
1533 return artifact_obj;
1536 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1537 full contents of FILENAME. */
1539 json::object *
1540 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1542 /* Let input.cc handle any charset conversion. */
1543 char_span utf8_content
1544 = m_context->get_file_cache ().get_source_file_content (filename);
1545 if (!utf8_content)
1546 return NULL;
1548 /* Don't add it if it's not valid UTF-8. */
1549 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1550 return NULL;
1552 json::object *artifact_content_obj = new json::object ();
1553 artifact_content_obj->set ("text",
1554 new json::string (utf8_content.get_buffer (),
1555 utf8_content.length ()));
1556 return artifact_content_obj;
1559 /* Attempt to read the given range of lines from FILENAME; return
1560 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1562 char *
1563 sarif_builder::get_source_lines (const char *filename,
1564 int start_line,
1565 int end_line) const
1567 auto_vec<char> result;
1569 for (int line = start_line; line <= end_line; line++)
1571 char_span line_content
1572 = m_context->get_file_cache ().get_source_line (filename, line);
1573 if (!line_content.get_buffer ())
1574 return NULL;
1575 result.reserve (line_content.length () + 1);
1576 for (size_t i = 0; i < line_content.length (); i++)
1577 result.quick_push (line_content[i]);
1578 result.quick_push ('\n');
1580 result.safe_push ('\0');
1582 return xstrdup (result.address ());
1585 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1586 run of lines within FILENAME (including the endpoints). */
1588 json::object *
1589 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1590 int start_line,
1591 int end_line) const
1593 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1595 if (!text_utf8)
1596 return NULL;
1598 /* Don't add it if it's not valid UTF-8. */
1599 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1601 free (text_utf8);
1602 return NULL;
1605 json::object *artifact_content_obj = new json::object ();
1606 artifact_content_obj->set_string ("text", text_utf8);
1607 free (text_utf8);
1609 return artifact_content_obj;
1612 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1614 json::object *
1615 sarif_builder::make_fix_object (const rich_location &richloc)
1617 json::object *fix_obj = new json::object ();
1619 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1620 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1621 json::array *artifact_change_arr = new json::array ();
1622 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1623 artifact_change_arr->append (artifact_change_obj);
1624 fix_obj->set ("artifactChanges", artifact_change_arr);
1626 return fix_obj;
1629 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1631 json::object *
1632 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1634 json::object *artifact_change_obj = new json::object ();
1636 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1637 json::object *artifact_location_obj
1638 = make_artifact_location_object (richloc.get_loc ());
1639 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1641 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1642 json::array *replacement_arr = new json::array ();
1643 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1645 const fixit_hint *hint = richloc.get_fixit_hint (i);
1646 json::object *replacement_obj = make_replacement_object (*hint);
1647 replacement_arr->append (replacement_obj);
1649 artifact_change_obj->set ("replacements", replacement_arr);
1651 return artifact_change_obj;
1654 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1656 json::object *
1657 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1659 json::object *replacement_obj = new json::object ();
1661 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1662 json::object *region_obj = make_region_object_for_hint (hint);
1663 replacement_obj->set ("deletedRegion", region_obj);
1665 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1666 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1667 replacement_obj->set ("insertedContent", content_obj);
1669 return replacement_obj;
1672 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1674 json::object *
1675 sarif_builder::make_artifact_content_object (const char *text) const
1677 json::object *content_obj = new json::object ();
1679 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1680 content_obj->set_string ("text", text);
1682 return content_obj;
1685 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1686 occurs. */
1688 static void
1689 sarif_ice_handler (diagnostic_context *context)
1691 /* Attempt to ensure that a .sarif file is written out. */
1692 diagnostic_finish (context);
1694 /* Print a header for the remaining output to stderr, and
1695 return, attempting to print the usual ICE messages to
1696 stderr. Hopefully this will be helpful to the user in
1697 indicating what's gone wrong (also for DejaGnu, for pruning
1698 those messages). */
1699 fnotice (stderr, "Internal compiler error:\n");
1702 class sarif_output_format : public diagnostic_output_format
1704 public:
1705 void on_begin_group () final override
1707 /* No-op, */
1709 void on_end_group () final override
1711 m_builder.end_group ();
1713 void
1714 on_begin_diagnostic (const diagnostic_info &) final override
1716 /* No-op, */
1718 void
1719 on_end_diagnostic (const diagnostic_info &diagnostic,
1720 diagnostic_t orig_diag_kind) final override
1722 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1724 void on_diagram (const diagnostic_diagram &diagram) final override
1726 m_builder.emit_diagram (&m_context, diagram);
1729 protected:
1730 sarif_output_format (diagnostic_context &context,
1731 bool formatted)
1732 : diagnostic_output_format (context),
1733 m_builder (&context, formatted)
1736 sarif_builder m_builder;
1739 class sarif_stream_output_format : public sarif_output_format
1741 public:
1742 sarif_stream_output_format (diagnostic_context &context,
1743 bool formatted,
1744 FILE *stream)
1745 : sarif_output_format (context, formatted),
1746 m_stream (stream)
1749 ~sarif_stream_output_format ()
1751 m_builder.flush_to_file (m_stream);
1753 private:
1754 FILE *m_stream;
1757 class sarif_file_output_format : public sarif_output_format
1759 public:
1760 sarif_file_output_format (diagnostic_context &context,
1761 bool formatted,
1762 const char *base_file_name)
1763 : sarif_output_format (context, formatted),
1764 m_base_file_name (xstrdup (base_file_name))
1767 ~sarif_file_output_format ()
1769 char *filename = concat (m_base_file_name, ".sarif", NULL);
1770 free (m_base_file_name);
1771 m_base_file_name = nullptr;
1772 FILE *outf = fopen (filename, "w");
1773 if (!outf)
1775 const char *errstr = xstrerror (errno);
1776 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1777 filename, errstr);
1778 free (filename);
1779 return;
1781 m_builder.flush_to_file (outf);
1782 fclose (outf);
1783 free (filename);
1786 private:
1787 char *m_base_file_name;
1790 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1791 to a file). */
1793 static void
1794 diagnostic_output_format_init_sarif (diagnostic_context *context)
1796 /* Override callbacks. */
1797 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1798 context->set_ice_handler_callback (sarif_ice_handler);
1800 /* The metadata is handled in SARIF format, rather than as text. */
1801 context->set_show_cwe (false);
1802 context->set_show_rules (false);
1804 /* The option is handled in SARIF format, rather than as text. */
1805 context->set_show_option_requested (false);
1807 /* Don't colorize the text. */
1808 pp_show_color (context->printer) = false;
1811 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1813 void
1814 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context,
1815 bool formatted)
1817 diagnostic_output_format_init_sarif (context);
1818 context->set_output_format (new sarif_stream_output_format (*context,
1819 formatted,
1820 stderr));
1823 /* Populate CONTEXT in preparation for SARIF output to a file named
1824 BASE_FILE_NAME.sarif. */
1826 void
1827 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1828 bool formatted,
1829 const char *base_file_name)
1831 diagnostic_output_format_init_sarif (context);
1832 context->set_output_format (new sarif_file_output_format (*context,
1833 formatted,
1834 base_file_name));
1837 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1839 void
1840 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1841 bool formatted,
1842 FILE *stream)
1844 diagnostic_output_format_init_sarif (context);
1845 context->set_output_format (new sarif_stream_output_format (*context,
1846 formatted,
1847 stream));