hppa: Fix pr110279-1.c on hppa
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blob05b2c6df2e27c3cacd4e80f860b044165b7d04a8
1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2023 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 *
188 make_logical_location_object (const logical_location &logical_loc) const;
189 json::object *make_code_flow_object (const diagnostic_path &path);
190 json::object *
191 make_thread_flow_location_object (const diagnostic_event &event,
192 int path_event_idx);
193 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
194 json::object *maybe_make_physical_location_object (location_t loc);
195 json::object *make_artifact_location_object (location_t loc);
196 json::object *make_artifact_location_object (const char *filename);
197 json::object *make_artifact_location_object_for_pwd () const;
198 json::object *maybe_make_region_object (location_t loc) const;
199 json::object *maybe_make_region_object_for_context (location_t loc) const;
200 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
201 json::object *make_multiformat_message_string (const char *msg) const;
202 json::object *make_top_level_object (sarif_invocation *invocation_obj,
203 json::array *results);
204 json::object *make_run_object (sarif_invocation *invocation_obj,
205 json::array *results);
206 json::object *make_tool_object () const;
207 json::object *make_driver_tool_component_object () const;
208 json::array *maybe_make_taxonomies_array () const;
209 json::object *maybe_make_cwe_taxonomy_object () const;
210 json::object *make_tool_component_reference_object_for_cwe () const;
211 json::object *
212 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
213 const diagnostic_info &diagnostic,
214 diagnostic_t orig_diag_kind,
215 const char *option_text);
216 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
217 json::object *
218 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
219 json::object *make_artifact_object (const char *filename);
220 char *get_source_lines (const char *filename,
221 int start_line,
222 int end_line) const;
223 json::object *maybe_make_artifact_content_object (const char *filename) const;
224 json::object *maybe_make_artifact_content_object (const char *filename,
225 int start_line,
226 int end_line) const;
227 json::object *make_fix_object (const rich_location &rich_loc);
228 json::object *make_artifact_change_object (const rich_location &richloc);
229 json::object *make_replacement_object (const fixit_hint &hint) const;
230 json::object *make_artifact_content_object (const char *text) const;
231 int get_sarif_column (expanded_location exploc) const;
233 diagnostic_context *m_context;
235 /* The JSON object for the invocation object. */
236 sarif_invocation *m_invocation_obj;
238 /* The JSON array of pending diagnostics. */
239 json::array *m_results_array;
241 /* The JSON object for the result object (if any) in the current
242 diagnostic group. */
243 sarif_result *m_cur_group_result;
245 hash_set <const char *> m_filenames;
246 bool m_seen_any_relative_paths;
247 hash_set <free_string_hash> m_rule_id_set;
248 json::array *m_rules_arr;
250 /* The set of all CWE IDs we've seen, if any. */
251 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
253 int m_tabstop;
255 bool m_formatted;
258 /* class sarif_object : public json::object. */
260 sarif_property_bag &
261 sarif_object::get_or_create_properties ()
263 json::value *properties_val = get ("properties");
264 if (properties_val)
266 if (properties_val->get_kind () == json::JSON_OBJECT)
267 return *static_cast <sarif_property_bag *> (properties_val);
270 sarif_property_bag *bag = new sarif_property_bag ();
271 set ("properties", bag);
272 return *bag;
275 /* class sarif_invocation : public sarif_object. */
277 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
278 Add an object representing the ICE to the notifications array. */
280 void
281 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
282 const diagnostic_info &diagnostic,
283 sarif_builder *builder)
285 m_success = false;
287 sarif_ice_notification *notification_obj
288 = new sarif_ice_notification (context, diagnostic, builder);
289 m_notifications_arr->append (notification_obj);
292 void
293 sarif_invocation::prepare_to_flush (diagnostic_context *context)
295 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
296 set_bool ("executionSuccessful", m_success);
298 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
299 set ("toolExecutionNotifications", m_notifications_arr);
301 /* Call client hook, allowing it to create a custom property bag for
302 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
303 if (auto client_data_hooks = context->get_client_data_hooks ())
304 client_data_hooks->add_sarif_invocation_properties (*this);
307 /* class sarif_result : public sarif_object. */
309 /* Handle secondary diagnostics that occur within a diagnostic group.
310 The closest SARIF seems to have to nested diagnostics is the
311 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
312 so we lazily set this property and populate the array if and when
313 secondary diagnostics occur (such as notes to a warning). */
315 void
316 sarif_result::on_nested_diagnostic (diagnostic_context *context,
317 const diagnostic_info &diagnostic,
318 diagnostic_t /*orig_diag_kind*/,
319 sarif_builder *builder)
321 /* We don't yet generate meaningful logical locations for notes;
322 sometimes these will related to current_function_decl, but
323 often they won't. */
324 json::object *location_obj
325 = builder->make_location_object (*diagnostic.richloc, NULL);
326 json::object *message_obj
327 = builder->make_message_object (pp_formatted_text (context->printer));
328 pp_clear_output_area (context->printer);
329 location_obj->set ("message", message_obj);
331 add_related_location (location_obj);
334 /* Handle diagrams that occur within a diagnostic group.
335 The closest thing in SARIF seems to be to add a location to the
336 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
337 and to put the diagram into the "message" property of that location
338 (SARIF v2.1.0 section 3.28.5). */
340 void
341 sarif_result::on_diagram (diagnostic_context *context,
342 const diagnostic_diagram &diagram,
343 sarif_builder *builder)
345 json::object *location_obj = new json::object ();
346 json::object *message_obj
347 = builder->make_message_object_for_diagram (context, diagram);
348 location_obj->set ("message", message_obj);
350 add_related_location (location_obj);
353 /* Add LOCATION_OBJ to this result's "relatedLocations" array,
354 creating it if it doesn't yet exist. */
356 void
357 sarif_result::add_related_location (json::object *location_obj)
359 if (!m_related_locations_arr)
361 m_related_locations_arr = new json::array ();
362 set ("relatedLocations", m_related_locations_arr);
364 m_related_locations_arr->append (location_obj);
367 /* class sarif_ice_notification : public sarif_object. */
369 /* sarif_ice_notification's ctor.
370 DIAGNOSTIC is an internal compiler error. */
372 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
373 const diagnostic_info &diagnostic,
374 sarif_builder *builder)
376 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
377 json::array *locations_arr = builder->make_locations_arr (diagnostic);
378 set ("locations", locations_arr);
380 /* "message" property (SARIF v2.1.0 section 3.85.5). */
381 json::object *message_obj
382 = builder->make_message_object (pp_formatted_text (context->printer));
383 pp_clear_output_area (context->printer);
384 set ("message", message_obj);
386 /* "level" property (SARIF v2.1.0 section 3.58.6). */
387 set_string ("level", "error");
390 /* class sarif_thread_flow : public sarif_object. */
392 sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
394 /* "id" property (SARIF v2.1.0 section 3.37.2). */
395 label_text name (thread.get_name (false));
396 set_string ("id", name.get ());
398 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
399 m_locations_arr = new json::array ();
400 set ("locations", m_locations_arr);
403 /* class sarif_builder. */
405 /* sarif_builder's ctor. */
407 sarif_builder::sarif_builder (diagnostic_context *context,
408 bool formatted)
409 : m_context (context),
410 m_invocation_obj (new sarif_invocation ()),
411 m_results_array (new json::array ()),
412 m_cur_group_result (NULL),
413 m_seen_any_relative_paths (false),
414 m_rule_id_set (),
415 m_rules_arr (new json::array ()),
416 m_tabstop (context->m_tabstop),
417 m_formatted (formatted)
421 /* Implementation of "end_diagnostic" for SARIF output. */
423 void
424 sarif_builder::end_diagnostic (diagnostic_context *context,
425 const diagnostic_info &diagnostic,
426 diagnostic_t orig_diag_kind)
428 if (diagnostic.kind == DK_ICE || diagnostic.kind == DK_ICE_NOBT)
430 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
431 return;
434 if (m_cur_group_result)
435 /* Nested diagnostic. */
436 m_cur_group_result->on_nested_diagnostic (context,
437 diagnostic,
438 orig_diag_kind,
439 this);
440 else
442 /* Top-level diagnostic. */
443 sarif_result *result_obj
444 = make_result_object (context, diagnostic, orig_diag_kind);
445 m_results_array->append (result_obj);
446 m_cur_group_result = result_obj;
450 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
451 for SARIF output. */
453 void
454 sarif_builder::emit_diagram (diagnostic_context *context,
455 const diagnostic_diagram &diagram)
457 /* We must be within the emission of a top-level diagnostic. */
458 gcc_assert (m_cur_group_result);
459 m_cur_group_result->on_diagram (context, diagram, this);
462 /* Implementation of "end_group_cb" for SARIF output. */
464 void
465 sarif_builder::end_group ()
467 m_cur_group_result = NULL;
470 /* Create a top-level object, and add it to all the results
471 (and other entities) we've seen so far.
473 Flush it all to OUTF. */
475 void
476 sarif_builder::flush_to_file (FILE *outf)
478 m_invocation_obj->prepare_to_flush (m_context);
479 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
480 top->dump (outf, m_formatted);
481 m_invocation_obj = NULL;
482 m_results_array = NULL;
483 fprintf (outf, "\n");
484 delete top;
487 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
488 property (SARIF v2.1.0 section 3.27.10).
490 Return NULL if there isn't one. */
492 static const char *
493 maybe_get_sarif_level (diagnostic_t diag_kind)
495 switch (diag_kind)
497 case DK_WARNING:
498 return "warning";
499 case DK_ERROR:
500 return "error";
501 case DK_NOTE:
502 case DK_ANACHRONISM:
503 return "note";
504 default:
505 return NULL;
509 /* Make a string for DIAG_KIND suitable for use a ruleId
510 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
511 have anything better to use. */
513 static char *
514 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
516 static const char *const diagnostic_kind_text[] = {
517 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
518 #include "diagnostic.def"
519 #undef DEFINE_DIAGNOSTIC_KIND
520 "must-not-happen"
522 /* Lose the trailing ": ". */
523 const char *kind_text = diagnostic_kind_text[diag_kind];
524 size_t len = strlen (kind_text);
525 gcc_assert (len > 2);
526 gcc_assert (kind_text[len - 2] == ':');
527 gcc_assert (kind_text[len - 1] == ' ');
528 char *rstrip = xstrdup (kind_text);
529 rstrip[len - 2] = '\0';
530 return rstrip;
533 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
535 sarif_result *
536 sarif_builder::make_result_object (diagnostic_context *context,
537 const diagnostic_info &diagnostic,
538 diagnostic_t orig_diag_kind)
540 sarif_result *result_obj = new sarif_result ();
542 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
543 /* Ideally we'd have an option_name for these. */
544 if (char *option_text
545 = context->make_option_name (diagnostic.option_index,
546 orig_diag_kind, diagnostic.kind))
548 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
549 Set ruleId referencing them. */
550 result_obj->set_string ("ruleId", option_text);
551 if (m_rule_id_set.contains (option_text))
552 free (option_text);
553 else
555 /* This is the first time we've seen this ruleId. */
556 /* Add to set, taking ownership. */
557 m_rule_id_set.add (option_text);
559 json::object *reporting_desc_obj
560 = make_reporting_descriptor_object_for_warning (context,
561 diagnostic,
562 orig_diag_kind,
563 option_text);
564 m_rules_arr->append (reporting_desc_obj);
567 else
569 /* Otherwise, we have an "error" or a stray "note"; use the
570 diagnostic kind as the ruleId, so that the result object at least
571 has a ruleId.
572 We don't bother creating reportingDescriptor objects for these. */
573 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
574 result_obj->set_string ("ruleId", rule_id);
575 free (rule_id);
578 if (diagnostic.metadata)
580 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
581 if (int cwe_id = diagnostic.metadata->get_cwe ())
583 json::array *taxa_arr = new json::array ();
584 json::object *cwe_id_obj
585 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
586 taxa_arr->append (cwe_id_obj);
587 result_obj->set ("taxa", taxa_arr);
590 diagnostic.metadata->maybe_add_sarif_properties (*result_obj);
593 /* "level" property (SARIF v2.1.0 section 3.27.10). */
594 if (const char *sarif_level = maybe_get_sarif_level (diagnostic.kind))
595 result_obj->set_string ("level", sarif_level);
597 /* "message" property (SARIF v2.1.0 section 3.27.11). */
598 json::object *message_obj
599 = make_message_object (pp_formatted_text (context->printer));
600 pp_clear_output_area (context->printer);
601 result_obj->set ("message", message_obj);
603 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
604 json::array *locations_arr = make_locations_arr (diagnostic);
605 result_obj->set ("locations", locations_arr);
607 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
608 if (const diagnostic_path *path = diagnostic.richloc->get_path ())
610 json::array *code_flows_arr = new json::array ();
611 json::object *code_flow_obj = make_code_flow_object (*path);
612 code_flows_arr->append (code_flow_obj);
613 result_obj->set ("codeFlows", code_flows_arr);
616 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
617 set up later, if any nested diagnostics occur within this diagnostic
618 group. */
620 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
621 const rich_location *richloc = diagnostic.richloc;
622 if (richloc->get_num_fixit_hints ())
624 json::array *fix_arr = new json::array ();
625 json::object *fix_obj = make_fix_object (*richloc);
626 fix_arr->append (fix_obj);
627 result_obj->set ("fixes", fix_arr);
630 return result_obj;
633 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
634 for a GCC warning. */
636 json::object *
637 sarif_builder::
638 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
639 const diagnostic_info &diagnostic,
640 diagnostic_t /*orig_diag_kind*/,
641 const char *option_text)
643 json::object *reporting_desc = new json::object ();
645 /* "id" property (SARIF v2.1.0 section 3.49.3). */
646 reporting_desc->set_string ("id", option_text);
648 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
649 it seems redundant compared to "id". */
651 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
652 if (char *option_url = context->make_option_url (diagnostic.option_index))
654 reporting_desc->set_string ("helpUri", option_url);
655 free (option_url);
658 return reporting_desc;
661 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
662 for CWE_ID, for use within the CWE taxa array. */
664 json::object *
665 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
667 json::object *reporting_desc = new json::object ();
669 /* "id" property (SARIF v2.1.0 section 3.49.3). */
671 pretty_printer pp;
672 pp_printf (&pp, "%i", cwe_id);
673 reporting_desc->set_string ("id", pp_formatted_text (&pp));
676 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
678 char *url = get_cwe_url (cwe_id);
679 reporting_desc->set_string ("helpUri", url);
680 free (url);
683 return reporting_desc;
686 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
687 referencing CWE_ID, for use within a result object.
688 Also, add CWE_ID to m_cwe_id_set. */
690 json::object *
691 sarif_builder::
692 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
694 json::object *desc_ref_obj = new json::object ();
696 /* "id" property (SARIF v2.1.0 section 3.52.4). */
698 pretty_printer pp;
699 pp_printf (&pp, "%i", cwe_id);
700 desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
703 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
704 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
705 desc_ref_obj->set ("toolComponent", comp_ref_obj);
707 /* Add CWE_ID to our set. */
708 gcc_assert (cwe_id > 0);
709 m_cwe_id_set.add (cwe_id);
711 return desc_ref_obj;
714 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
715 references the CWE taxonomy. */
717 json::object *
718 sarif_builder::
719 make_tool_component_reference_object_for_cwe () const
721 json::object *comp_ref_obj = new json::object ();
723 /* "name" property (SARIF v2.1.0 section 3.54.3). */
724 comp_ref_obj->set_string ("name", "cwe");
726 return comp_ref_obj;
729 /* Make an array suitable for use as the "locations" property of:
730 - a "result" object (SARIF v2.1.0 section 3.27.12), or
731 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
733 json::array *
734 sarif_builder::make_locations_arr (const diagnostic_info &diagnostic)
736 json::array *locations_arr = new json::array ();
737 const logical_location *logical_loc = NULL;
738 if (auto client_data_hooks = m_context->get_client_data_hooks ())
739 logical_loc = client_data_hooks->get_current_logical_location ();
741 json::object *location_obj
742 = make_location_object (*diagnostic.richloc, logical_loc);
743 locations_arr->append (location_obj);
744 return locations_arr;
747 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
748 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
750 void
751 sarif_builder::
752 set_any_logical_locs_arr (json::object *location_obj,
753 const logical_location *logical_loc)
755 if (!logical_loc)
756 return;
757 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
758 json::array *location_locs_arr = new json::array ();
759 location_locs_arr->append (logical_loc_obj);
760 location_obj->set ("logicalLocations", location_locs_arr);
763 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
764 and LOGICAL_LOC. */
766 json::object *
767 sarif_builder::make_location_object (const rich_location &rich_loc,
768 const logical_location *logical_loc)
770 json::object *location_obj = new json::object ();
772 /* Get primary loc from RICH_LOC. */
773 location_t loc = rich_loc.get_loc ();
775 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
776 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
777 location_obj->set ("physicalLocation", phs_loc_obj);
779 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
780 set_any_logical_locs_arr (location_obj, logical_loc);
782 return location_obj;
785 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
786 within a diagnostic_path. */
788 json::object *
789 sarif_builder::make_location_object (const diagnostic_event &event)
791 json::object *location_obj = new json::object ();
793 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
794 location_t loc = event.get_location ();
795 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
796 location_obj->set ("physicalLocation", phs_loc_obj);
798 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
799 const logical_location *logical_loc = event.get_logical_location ();
800 set_any_logical_locs_arr (location_obj, logical_loc);
802 /* "message" property (SARIF v2.1.0 section 3.28.5). */
803 label_text ev_desc = event.get_desc (false);
804 json::object *message_obj = make_message_object (ev_desc.get ());
805 location_obj->set ("message", message_obj);
807 return location_obj;
810 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
811 or return NULL;
812 Add any filename to the m_artifacts. */
814 json::object *
815 sarif_builder::maybe_make_physical_location_object (location_t loc)
817 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
818 return NULL;
820 json::object *phys_loc_obj = new json::object ();
822 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
823 json::object *artifact_loc_obj = make_artifact_location_object (loc);
824 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
825 m_filenames.add (LOCATION_FILE (loc));
827 /* "region" property (SARIF v2.1.0 section 3.29.4). */
828 if (json::object *region_obj = maybe_make_region_object (loc))
829 phys_loc_obj->set ("region", region_obj);
831 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
832 if (json::object *context_region_obj
833 = maybe_make_region_object_for_context (loc))
834 phys_loc_obj->set ("contextRegion", context_region_obj);
836 /* Instead, we add artifacts to the run as a whole,
837 with artifact.contents.
838 Could do both, though. */
840 return phys_loc_obj;
843 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
844 or return NULL. */
846 json::object *
847 sarif_builder::make_artifact_location_object (location_t loc)
849 return make_artifact_location_object (LOCATION_FILE (loc));
852 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
853 for when we need to express paths relative to PWD. */
855 #define PWD_PROPERTY_NAME ("PWD")
857 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
858 or return NULL. */
860 json::object *
861 sarif_builder::make_artifact_location_object (const char *filename)
863 json::object *artifact_loc_obj = new json::object ();
865 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
866 artifact_loc_obj->set_string ("uri", filename);
868 if (filename[0] != '/')
870 /* If we have a relative path, set the "uriBaseId" property
871 (SARIF v2.1.0 section 3.4.4). */
872 artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
873 m_seen_any_relative_paths = true;
876 return artifact_loc_obj;
879 /* Get the PWD, or NULL, as an absolute file-based URI,
880 adding a trailing forward slash (as required by SARIF v2.1.0
881 section 3.14.14). */
883 static char *
884 make_pwd_uri_str ()
886 /* The prefix of a file-based URI, up to, but not including the path. */
887 #define FILE_PREFIX ("file://")
889 const char *pwd = getpwd ();
890 if (!pwd)
891 return NULL;
892 size_t len = strlen (pwd);
893 if (len == 0 || pwd[len - 1] != '/')
894 return concat (FILE_PREFIX, pwd, "/", NULL);
895 else
897 gcc_assert (pwd[len - 1] == '/');
898 return concat (FILE_PREFIX, pwd, NULL);
902 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
903 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
904 section 3.14.14) when we have any relative paths. */
906 json::object *
907 sarif_builder::make_artifact_location_object_for_pwd () const
909 json::object *artifact_loc_obj = new json::object ();
911 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
912 if (char *pwd = make_pwd_uri_str ())
914 gcc_assert (strlen (pwd) > 0);
915 gcc_assert (pwd[strlen (pwd) - 1] == '/');
916 artifact_loc_obj->set_string ("uri", pwd);
917 free (pwd);
920 return artifact_loc_obj;
923 /* Get the column number within EXPLOC. */
926 sarif_builder::get_sarif_column (expanded_location exploc) const
928 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
929 return location_compute_display_column (m_context->get_file_cache (),
930 exploc, policy);
933 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
934 or return NULL. */
936 json::object *
937 sarif_builder::maybe_make_region_object (location_t loc) const
939 location_t caret_loc = get_pure_location (loc);
941 if (caret_loc <= BUILTINS_LOCATION)
942 return NULL;
944 location_t start_loc = get_start (loc);
945 location_t finish_loc = get_finish (loc);
947 expanded_location exploc_caret = expand_location (caret_loc);
948 expanded_location exploc_start = expand_location (start_loc);
949 expanded_location exploc_finish = expand_location (finish_loc);
951 if (exploc_start.file !=exploc_caret.file)
952 return NULL;
953 if (exploc_finish.file !=exploc_caret.file)
954 return NULL;
956 json::object *region_obj = new json::object ();
958 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
959 region_obj->set_integer ("startLine", exploc_start.line);
961 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
962 region_obj->set_integer ("startColumn", get_sarif_column (exploc_start));
964 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
965 if (exploc_finish.line != exploc_start.line)
966 region_obj->set_integer ("endLine", exploc_finish.line);
968 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
969 This expresses the column immediately beyond the range. */
971 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
972 region_obj->set_integer ("endColumn", next_column);
975 return region_obj;
978 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
979 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
981 This is similar to maybe_make_region_object, but ignores column numbers,
982 covering the line(s) as a whole, and including a "snippet" property
983 embedding those source lines, making it easier for consumers to show
984 the pertinent source. */
986 json::object *
987 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
989 location_t caret_loc = get_pure_location (loc);
991 if (caret_loc <= BUILTINS_LOCATION)
992 return NULL;
994 location_t start_loc = get_start (loc);
995 location_t finish_loc = get_finish (loc);
997 expanded_location exploc_caret = expand_location (caret_loc);
998 expanded_location exploc_start = expand_location (start_loc);
999 expanded_location exploc_finish = expand_location (finish_loc);
1001 if (exploc_start.file !=exploc_caret.file)
1002 return NULL;
1003 if (exploc_finish.file !=exploc_caret.file)
1004 return NULL;
1006 json::object *region_obj = new json::object ();
1008 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1009 region_obj->set_integer ("startLine", exploc_start.line);
1011 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1012 if (exploc_finish.line != exploc_start.line)
1013 region_obj->set_integer ("endLine", exploc_finish.line);
1015 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1016 if (json::object *artifact_content_obj
1017 = maybe_make_artifact_content_object (exploc_start.file,
1018 exploc_start.line,
1019 exploc_finish.line))
1020 region_obj->set ("snippet", artifact_content_obj);
1022 return region_obj;
1025 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1026 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1028 json::object *
1029 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1031 location_t start_loc = hint.get_start_loc ();
1032 location_t next_loc = hint.get_next_loc ();
1034 expanded_location exploc_start = expand_location (start_loc);
1035 expanded_location exploc_next = expand_location (next_loc);
1037 json::object *region_obj = new json::object ();
1039 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1040 region_obj->set_integer ("startLine", exploc_start.line);
1042 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1043 int start_col = get_sarif_column (exploc_start);
1044 region_obj->set_integer ("startColumn", start_col);
1046 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1047 if (exploc_next.line != exploc_start.line)
1048 region_obj->set_integer ("endLine", exploc_next.line);
1050 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1051 This expresses the column immediately beyond the range. */
1052 int next_col = get_sarif_column (exploc_next);
1053 region_obj->set_integer ("endColumn", next_col);
1055 return region_obj;
1058 /* Attempt to get a string for a logicalLocation's "kind" property
1059 (SARIF v2.1.0 section 3.33.7).
1060 Return NULL if unknown. */
1062 static const char *
1063 maybe_get_sarif_kind (enum logical_location_kind kind)
1065 switch (kind)
1067 default:
1068 gcc_unreachable ();
1069 case LOGICAL_LOCATION_KIND_UNKNOWN:
1070 return NULL;
1072 case LOGICAL_LOCATION_KIND_FUNCTION:
1073 return "function";
1074 case LOGICAL_LOCATION_KIND_MEMBER:
1075 return "member";
1076 case LOGICAL_LOCATION_KIND_MODULE:
1077 return "module";
1078 case LOGICAL_LOCATION_KIND_NAMESPACE:
1079 return "namespace";
1080 case LOGICAL_LOCATION_KIND_TYPE:
1081 return "type";
1082 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1083 return "returnType";
1084 case LOGICAL_LOCATION_KIND_PARAMETER:
1085 return "parameter";
1086 case LOGICAL_LOCATION_KIND_VARIABLE:
1087 return "variable";
1091 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1092 or return NULL. */
1094 json::object *
1095 sarif_builder::
1096 make_logical_location_object (const logical_location &logical_loc) const
1098 json::object *logical_loc_obj = new json::object ();
1100 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1101 if (const char *short_name = logical_loc.get_short_name ())
1102 logical_loc_obj->set_string ("name", short_name);
1104 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1105 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1106 logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope);
1108 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1109 if (const char *internal_name = logical_loc.get_internal_name ())
1110 logical_loc_obj->set_string ("decoratedName", internal_name);
1112 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1113 enum logical_location_kind kind = logical_loc.get_kind ();
1114 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1115 logical_loc_obj->set_string ("kind", sarif_kind_str);
1117 return logical_loc_obj;
1120 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1122 json::object *
1123 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1125 json::object *code_flow_obj = new json::object ();
1127 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1128 json::array *thread_flows_arr = new json::array ();
1130 /* Walk the events, consolidating into per-thread threadFlow objects,
1131 using the index with PATH as the overall executionOrder. */
1132 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1133 sarif_thread_flow *> thread_id_map;
1134 for (unsigned i = 0; i < path.num_events (); i++)
1136 const diagnostic_event &event = path.get_event (i);
1137 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1138 sarif_thread_flow *thread_flow_obj;
1140 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1141 thread_flow_obj = *slot;
1142 else
1144 const diagnostic_thread &thread = path.get_thread (thread_id);
1145 thread_flow_obj = new sarif_thread_flow (thread);
1146 thread_flows_arr->append (thread_flow_obj);
1147 thread_id_map.put (thread_id, thread_flow_obj);
1150 /* Add event to thread's threadFlow object. */
1151 json::object *thread_flow_loc_obj
1152 = make_thread_flow_location_object (event, i);
1153 thread_flow_obj->add_location (thread_flow_loc_obj);
1155 code_flow_obj->set ("threadFlows", thread_flows_arr);
1157 return code_flow_obj;
1160 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1162 json::object *
1163 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1164 int path_event_idx)
1166 json::object *thread_flow_loc_obj = new json::object ();
1168 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1169 json::object *location_obj = make_location_object (ev);
1170 thread_flow_loc_obj->set ("location", location_obj);
1172 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1173 diagnostic_event::meaning m = ev.get_meaning ();
1174 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1175 thread_flow_loc_obj->set ("kinds", kinds_arr);
1177 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1178 thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
1180 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1181 Offset by 1 to match the human-readable values emitted by %@. */
1182 thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
1184 /* It might be nice to eventually implement the following for -fanalyzer:
1185 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1186 - the "state" property (SARIF v2.1.0 section 3.38.9)
1187 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1189 return thread_flow_loc_obj;
1192 /* If M has any known meaning, make a json array suitable for the "kinds"
1193 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1195 Otherwise, return NULL. */
1197 json::array *
1198 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1200 if (m.m_verb == diagnostic_event::VERB_unknown
1201 && m.m_noun == diagnostic_event::NOUN_unknown
1202 && m.m_property == diagnostic_event::PROPERTY_unknown)
1203 return NULL;
1205 json::array *kinds_arr = new json::array ();
1206 if (const char *verb_str
1207 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1208 kinds_arr->append (new json::string (verb_str));
1209 if (const char *noun_str
1210 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1211 kinds_arr->append (new json::string (noun_str));
1212 if (const char *property_str
1213 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1214 kinds_arr->append (new json::string (property_str));
1215 return kinds_arr;
1218 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1220 json::object *
1221 sarif_builder::make_message_object (const char *msg) const
1223 json::object *message_obj = new json::object ();
1225 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1226 message_obj->set_string ("text", msg);
1228 return message_obj;
1231 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1232 We emit the diagram as a code block within the Markdown part
1233 of the message. */
1235 json::object *
1236 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1237 const diagnostic_diagram &diagram)
1239 json::object *message_obj = new json::object ();
1241 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1242 message_obj->set_string ("text", diagram.get_alt_text ());
1244 char *saved_prefix = pp_take_prefix (context->printer);
1245 pp_set_prefix (context->printer, NULL);
1247 /* "To produce a code block in Markdown, simply indent every line of
1248 the block by at least 4 spaces or 1 tab."
1249 Here we use 4 spaces. */
1250 diagram.get_canvas ().print_to_pp (context->printer, " ");
1251 pp_set_prefix (context->printer, saved_prefix);
1253 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1254 message_obj->set_string ("markdown", pp_formatted_text (context->printer));
1256 pp_clear_output_area (context->printer);
1258 return message_obj;
1261 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1262 for MSG. */
1264 json::object *
1265 sarif_builder::make_multiformat_message_string (const char *msg) const
1267 json::object *message_obj = new json::object ();
1269 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1270 message_obj->set_string ("text", msg);
1272 return message_obj;
1275 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1276 #define SARIF_VERSION "2.1.0"
1278 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1279 Take ownership of INVOCATION_OBJ and RESULTS. */
1281 json::object *
1282 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1283 json::array *results)
1285 json::object *log_obj = new json::object ();
1287 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1288 log_obj->set_string ("$schema", SARIF_SCHEMA);
1290 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1291 log_obj->set_string ("version", SARIF_VERSION);
1293 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1294 json::array *run_arr = new json::array ();
1295 json::object *run_obj = make_run_object (invocation_obj, results);
1296 run_arr->append (run_obj);
1297 log_obj->set ("runs", run_arr);
1299 return log_obj;
1302 /* Make a run object (SARIF v2.1.0 section 3.14).
1303 Take ownership of INVOCATION_OBJ and RESULTS. */
1305 json::object *
1306 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1307 json::array *results)
1309 json::object *run_obj = new json::object ();
1311 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1312 json::object *tool_obj = make_tool_object ();
1313 run_obj->set ("tool", tool_obj);
1315 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1316 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1317 run_obj->set ("taxonomies", taxonomies_arr);
1319 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1321 json::array *invocations_arr = new json::array ();
1322 invocations_arr->append (invocation_obj);
1323 run_obj->set ("invocations", invocations_arr);
1326 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1327 if (m_seen_any_relative_paths)
1329 json::object *orig_uri_base_ids = new json::object ();
1330 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1331 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1332 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1335 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1336 json::array *artifacts_arr = new json::array ();
1337 for (auto iter : m_filenames)
1339 json::object *artifact_obj = make_artifact_object (iter);
1340 artifacts_arr->append (artifact_obj);
1342 run_obj->set ("artifacts", artifacts_arr);
1344 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1345 run_obj->set ("results", results);
1347 return run_obj;
1350 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1352 json::object *
1353 sarif_builder::make_tool_object () const
1355 json::object *tool_obj = new json::object ();
1357 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1358 json::object *driver_obj = make_driver_tool_component_object ();
1359 tool_obj->set ("driver", driver_obj);
1361 /* Report plugins via the "extensions" property
1362 (SARIF v2.1.0 section 3.18.3). */
1363 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1364 if (const client_version_info *vinfo
1365 = client_data_hooks->get_any_version_info ())
1367 class my_plugin_visitor : public client_version_info :: plugin_visitor
1369 public:
1370 void on_plugin (const diagnostic_client_plugin_info &p) final override
1372 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1373 for the plugin. */
1374 json::object *plugin_obj = new json::object ();
1375 m_plugin_objs.safe_push (plugin_obj);
1377 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1378 if (const char *short_name = p.get_short_name ())
1379 plugin_obj->set_string ("name", short_name);
1381 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1382 if (const char *full_name = p.get_full_name ())
1383 plugin_obj->set_string ("fullName", full_name);
1385 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1386 if (const char *version = p.get_version ())
1387 plugin_obj->set_string ("version", version);
1389 auto_vec <json::object *> m_plugin_objs;
1391 my_plugin_visitor v;
1392 vinfo->for_each_plugin (v);
1393 if (v.m_plugin_objs.length () > 0)
1395 json::array *extensions_arr = new json::array ();
1396 tool_obj->set ("extensions", extensions_arr);
1397 for (auto iter : v.m_plugin_objs)
1398 extensions_arr->append (iter);
1402 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1403 "extensions" (see toplev.cc: print_version). */
1405 return tool_obj;
1408 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1409 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1411 json::object *
1412 sarif_builder::make_driver_tool_component_object () const
1414 json::object *driver_obj = new json::object ();
1416 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1417 if (const client_version_info *vinfo
1418 = client_data_hooks->get_any_version_info ())
1420 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1421 if (const char *name = vinfo->get_tool_name ())
1422 driver_obj->set_string ("name", name);
1424 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1425 if (char *full_name = vinfo->maybe_make_full_name ())
1427 driver_obj->set_string ("fullName", full_name);
1428 free (full_name);
1431 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1432 if (const char *version = vinfo->get_version_string ())
1433 driver_obj->set_string ("version", version);
1435 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1436 if (char *version_url = vinfo->maybe_make_version_url ())
1438 driver_obj->set_string ("informationUri", version_url);
1439 free (version_url);
1443 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1444 driver_obj->set ("rules", m_rules_arr);
1446 return driver_obj;
1449 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1450 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1451 toolComponent (3.19) as per 3.19.3, representing the CWE.
1453 Otherwise return NULL. */
1455 json::array *
1456 sarif_builder::maybe_make_taxonomies_array () const
1458 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1459 if (!cwe_obj)
1460 return NULL;
1462 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1463 json::array *taxonomies_arr = new json::array ();
1464 taxonomies_arr->append (cwe_obj);
1465 return taxonomies_arr;
1468 /* If we've seen any CWE IDs, make a toolComponent object
1469 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1470 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1472 Otherwise return NULL. */
1474 json::object *
1475 sarif_builder::maybe_make_cwe_taxonomy_object () const
1477 if (m_cwe_id_set.is_empty ())
1478 return NULL;
1480 json::object *taxonomy_obj = new json::object ();
1482 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1483 taxonomy_obj->set_string ("name", "CWE");
1485 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1486 taxonomy_obj->set_string ("version", "4.7");
1488 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1489 taxonomy_obj->set_string ("organization", "MITRE");
1491 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1492 json::object *short_desc
1493 = make_multiformat_message_string ("The MITRE"
1494 " Common Weakness Enumeration");
1495 taxonomy_obj->set ("shortDescription", short_desc);
1497 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1498 json::array *taxa_arr = new json::array ();
1499 for (auto cwe_id : m_cwe_id_set)
1501 json::object *cwe_taxon
1502 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1503 taxa_arr->append (cwe_taxon);
1505 taxonomy_obj->set ("taxa", taxa_arr);
1507 return taxonomy_obj;
1510 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1512 json::object *
1513 sarif_builder::make_artifact_object (const char *filename)
1515 json::object *artifact_obj = new json::object ();
1517 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1518 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1519 artifact_obj->set ("location", artifact_loc_obj);
1521 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1522 if (json::object *artifact_content_obj
1523 = maybe_make_artifact_content_object (filename))
1524 artifact_obj->set ("contents", artifact_content_obj);
1526 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1527 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1528 if (const char *source_lang
1529 = client_data_hooks->maybe_get_sarif_source_language (filename))
1530 artifact_obj->set_string ("sourceLanguage", source_lang);
1532 return artifact_obj;
1535 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1536 full contents of FILENAME. */
1538 json::object *
1539 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1541 /* Let input.cc handle any charset conversion. */
1542 char_span utf8_content
1543 = m_context->get_file_cache ().get_source_file_content (filename);
1544 if (!utf8_content)
1545 return NULL;
1547 /* Don't add it if it's not valid UTF-8. */
1548 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1549 return NULL;
1551 json::object *artifact_content_obj = new json::object ();
1552 artifact_content_obj->set ("text",
1553 new json::string (utf8_content.get_buffer (),
1554 utf8_content.length ()));
1555 return artifact_content_obj;
1558 /* Attempt to read the given range of lines from FILENAME; return
1559 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1561 char *
1562 sarif_builder::get_source_lines (const char *filename,
1563 int start_line,
1564 int end_line) const
1566 auto_vec<char> result;
1568 for (int line = start_line; line <= end_line; line++)
1570 char_span line_content
1571 = m_context->get_file_cache ().get_source_line (filename, line);
1572 if (!line_content.get_buffer ())
1573 return NULL;
1574 result.reserve (line_content.length () + 1);
1575 for (size_t i = 0; i < line_content.length (); i++)
1576 result.quick_push (line_content[i]);
1577 result.quick_push ('\n');
1579 result.safe_push ('\0');
1581 return xstrdup (result.address ());
1584 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1585 run of lines within FILENAME (including the endpoints). */
1587 json::object *
1588 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1589 int start_line,
1590 int end_line) const
1592 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1594 if (!text_utf8)
1595 return NULL;
1597 /* Don't add it if it's not valid UTF-8. */
1598 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1600 free (text_utf8);
1601 return NULL;
1604 json::object *artifact_content_obj = new json::object ();
1605 artifact_content_obj->set_string ("text", text_utf8);
1606 free (text_utf8);
1608 return artifact_content_obj;
1611 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1613 json::object *
1614 sarif_builder::make_fix_object (const rich_location &richloc)
1616 json::object *fix_obj = new json::object ();
1618 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1619 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1620 json::array *artifact_change_arr = new json::array ();
1621 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1622 artifact_change_arr->append (artifact_change_obj);
1623 fix_obj->set ("artifactChanges", artifact_change_arr);
1625 return fix_obj;
1628 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1630 json::object *
1631 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1633 json::object *artifact_change_obj = new json::object ();
1635 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1636 json::object *artifact_location_obj
1637 = make_artifact_location_object (richloc.get_loc ());
1638 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1640 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1641 json::array *replacement_arr = new json::array ();
1642 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1644 const fixit_hint *hint = richloc.get_fixit_hint (i);
1645 json::object *replacement_obj = make_replacement_object (*hint);
1646 replacement_arr->append (replacement_obj);
1648 artifact_change_obj->set ("replacements", replacement_arr);
1650 return artifact_change_obj;
1653 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1655 json::object *
1656 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1658 json::object *replacement_obj = new json::object ();
1660 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1661 json::object *region_obj = make_region_object_for_hint (hint);
1662 replacement_obj->set ("deletedRegion", region_obj);
1664 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1665 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1666 replacement_obj->set ("insertedContent", content_obj);
1668 return replacement_obj;
1671 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1673 json::object *
1674 sarif_builder::make_artifact_content_object (const char *text) const
1676 json::object *content_obj = new json::object ();
1678 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1679 content_obj->set_string ("text", text);
1681 return content_obj;
1684 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1685 occurs. */
1687 static void
1688 sarif_ice_handler (diagnostic_context *context)
1690 /* Attempt to ensure that a .sarif file is written out. */
1691 diagnostic_finish (context);
1693 /* Print a header for the remaining output to stderr, and
1694 return, attempting to print the usual ICE messages to
1695 stderr. Hopefully this will be helpful to the user in
1696 indicating what's gone wrong (also for DejaGnu, for pruning
1697 those messages). */
1698 fnotice (stderr, "Internal compiler error:\n");
1701 class sarif_output_format : public diagnostic_output_format
1703 public:
1704 void on_begin_group () final override
1706 /* No-op, */
1708 void on_end_group () final override
1710 m_builder.end_group ();
1712 void
1713 on_begin_diagnostic (const diagnostic_info &) final override
1715 /* No-op, */
1717 void
1718 on_end_diagnostic (const diagnostic_info &diagnostic,
1719 diagnostic_t orig_diag_kind) final override
1721 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1723 void on_diagram (const diagnostic_diagram &diagram) final override
1725 m_builder.emit_diagram (&m_context, diagram);
1728 protected:
1729 sarif_output_format (diagnostic_context &context,
1730 bool formatted)
1731 : diagnostic_output_format (context),
1732 m_builder (&context, formatted)
1735 sarif_builder m_builder;
1738 class sarif_stream_output_format : public sarif_output_format
1740 public:
1741 sarif_stream_output_format (diagnostic_context &context,
1742 bool formatted,
1743 FILE *stream)
1744 : sarif_output_format (context, formatted),
1745 m_stream (stream)
1748 ~sarif_stream_output_format ()
1750 m_builder.flush_to_file (m_stream);
1752 private:
1753 FILE *m_stream;
1756 class sarif_file_output_format : public sarif_output_format
1758 public:
1759 sarif_file_output_format (diagnostic_context &context,
1760 bool formatted,
1761 const char *base_file_name)
1762 : sarif_output_format (context, formatted),
1763 m_base_file_name (xstrdup (base_file_name))
1766 ~sarif_file_output_format ()
1768 char *filename = concat (m_base_file_name, ".sarif", NULL);
1769 free (m_base_file_name);
1770 m_base_file_name = nullptr;
1771 FILE *outf = fopen (filename, "w");
1772 if (!outf)
1774 const char *errstr = xstrerror (errno);
1775 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1776 filename, errstr);
1777 free (filename);
1778 return;
1780 m_builder.flush_to_file (outf);
1781 fclose (outf);
1782 free (filename);
1785 private:
1786 char *m_base_file_name;
1789 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1790 to a file). */
1792 static void
1793 diagnostic_output_format_init_sarif (diagnostic_context *context)
1795 /* Override callbacks. */
1796 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1797 context->set_ice_handler_callback (sarif_ice_handler);
1799 /* The metadata is handled in SARIF format, rather than as text. */
1800 context->set_show_cwe (false);
1801 context->set_show_rules (false);
1803 /* The option is handled in SARIF format, rather than as text. */
1804 context->set_show_option_requested (false);
1806 /* Don't colorize the text. */
1807 pp_show_color (context->printer) = false;
1810 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1812 void
1813 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context,
1814 bool formatted)
1816 diagnostic_output_format_init_sarif (context);
1817 context->set_output_format (new sarif_stream_output_format (*context,
1818 formatted,
1819 stderr));
1822 /* Populate CONTEXT in preparation for SARIF output to a file named
1823 BASE_FILE_NAME.sarif. */
1825 void
1826 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1827 bool formatted,
1828 const char *base_file_name)
1830 diagnostic_output_format_init_sarif (context);
1831 context->set_output_format (new sarif_file_output_format (*context,
1832 formatted,
1833 base_file_name));
1836 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1838 void
1839 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1840 bool formatted,
1841 FILE *stream)
1843 diagnostic_output_format_init_sarif (context);
1844 context->set_output_format (new sarif_stream_output_format (*context,
1845 formatted,
1846 stream));