ada: Fix wrong finalization for call to BIP function in conditional expression
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blobfd29ac2ca3b4ad9e6f3438ac5c9a8ec189a31c47
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 #include "system.h"
24 #include "coretypes.h"
25 #include "diagnostic.h"
26 #include "diagnostic-metadata.h"
27 #include "diagnostic-path.h"
28 #include "json.h"
29 #include "cpplib.h"
30 #include "logical-location.h"
31 #include "diagnostic-client-data-hooks.h"
33 class sarif_builder;
35 /* Subclass of json::object for SARIF invocation objects
36 (SARIF v2.1.0 section 3.20). */
38 class sarif_invocation : public json::object
40 public:
41 sarif_invocation ()
42 : m_notifications_arr (new json::array ()),
43 m_success (true)
46 void add_notification_for_ice (diagnostic_context *context,
47 diagnostic_info *diagnostic,
48 sarif_builder *builder);
49 void prepare_to_flush ();
51 private:
52 json::array *m_notifications_arr;
53 bool m_success;
56 /* Subclass of json::object for SARIF result objects
57 (SARIF v2.1.0 section 3.27). */
59 class sarif_result : public json::object
61 public:
62 sarif_result () : m_related_locations_arr (NULL) {}
64 void
65 on_nested_diagnostic (diagnostic_context *context,
66 diagnostic_info *diagnostic,
67 diagnostic_t orig_diag_kind,
68 sarif_builder *builder);
70 private:
71 json::array *m_related_locations_arr;
74 /* Subclass of json::object for SARIF notification objects
75 (SARIF v2.1.0 section 3.58).
77 This subclass is specifically for notifying when an
78 internal compiler error occurs. */
80 class sarif_ice_notification : public json::object
82 public:
83 sarif_ice_notification (diagnostic_context *context,
84 diagnostic_info *diagnostic,
85 sarif_builder *builder);
88 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
89 and -fdiagnostics-format=sarif-file).
91 As diagnostics occur, we build "result" JSON objects, and
92 accumulate state:
93 - which source files are referenced
94 - which warnings are emitted
95 - which CWEs are used
97 At the end of the compile, we use the above to build the full SARIF
98 object tree, adding the result objects to the correct place, and
99 creating objects for the various source files, warnings and CWEs
100 referenced.
102 Implemented:
103 - fix-it hints
104 - CWE metadata
105 - diagnostic groups (see limitations below)
106 - logical locations (e.g. cfun)
108 Known limitations:
109 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
110 but we only capture location and message information from such nested
111 diagnostics (e.g. we ignore fix-it hints on them)
112 - doesn't yet capture command-line arguments: would be run.invocations
113 property (SARIF v2.1.0 section 3.14.11), as invocation objects
114 (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
115 toplev::main, and the response files.
116 - doesn't capture escape_on_output_p
117 - doesn't capture secondary locations within a rich_location
118 (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
119 section 3.27.22)
120 - doesn't capture "artifact.encoding" property
121 (SARIF v2.1.0 section 3.24.9).
122 - doesn't capture hashes of the source files
123 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
124 - doesn't capture the "analysisTarget" property
125 (SARIF v2.1.0 section 3.27.13).
126 - doesn't capture labelled ranges
127 - doesn't capture -Werror cleanly
128 - doesn't capture inlining information (can SARIF handle this?)
129 - doesn't capture macro expansion information (can SARIF handle this?). */
131 class sarif_builder
133 public:
134 sarif_builder (diagnostic_context *context);
136 void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
137 diagnostic_t orig_diag_kind);
139 void end_group ();
141 void flush_to_file (FILE *outf);
143 json::array *make_locations_arr (diagnostic_info *diagnostic);
144 json::object *make_location_object (const rich_location &rich_loc,
145 const logical_location *logical_loc);
146 json::object *make_message_object (const char *msg) const;
148 private:
149 sarif_result *make_result_object (diagnostic_context *context,
150 diagnostic_info *diagnostic,
151 diagnostic_t orig_diag_kind);
152 void set_any_logical_locs_arr (json::object *location_obj,
153 const logical_location *logical_loc);
154 json::object *make_location_object (const diagnostic_event &event);
155 json::object *
156 make_logical_location_object (const logical_location &logical_loc) const;
157 json::object *make_code_flow_object (const diagnostic_path &path);
158 json::object *make_thread_flow_object (const diagnostic_path &path);
159 json::object *
160 make_thread_flow_location_object (const diagnostic_event &event);
161 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
162 json::object *maybe_make_physical_location_object (location_t loc);
163 json::object *make_artifact_location_object (location_t loc);
164 json::object *make_artifact_location_object (const char *filename);
165 json::object *make_artifact_location_object_for_pwd () const;
166 json::object *maybe_make_region_object (location_t loc) const;
167 json::object *maybe_make_region_object_for_context (location_t loc) const;
168 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
169 json::object *make_multiformat_message_string (const char *msg) const;
170 json::object *make_top_level_object (sarif_invocation *invocation_obj,
171 json::array *results);
172 json::object *make_run_object (sarif_invocation *invocation_obj,
173 json::array *results);
174 json::object *make_tool_object () const;
175 json::object *make_driver_tool_component_object () const;
176 json::array *maybe_make_taxonomies_array () const;
177 json::object *maybe_make_cwe_taxonomy_object () const;
178 json::object *make_tool_component_reference_object_for_cwe () const;
179 json::object *
180 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
181 diagnostic_info *diagnostic,
182 diagnostic_t orig_diag_kind,
183 const char *option_text);
184 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
185 json::object *
186 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
187 json::object *make_artifact_object (const char *filename);
188 json::object *maybe_make_artifact_content_object (const char *filename) const;
189 json::object *maybe_make_artifact_content_object (const char *filename,
190 int start_line,
191 int end_line) const;
192 json::object *make_fix_object (const rich_location &rich_loc);
193 json::object *make_artifact_change_object (const rich_location &richloc);
194 json::object *make_replacement_object (const fixit_hint &hint) const;
195 json::object *make_artifact_content_object (const char *text) const;
196 int get_sarif_column (expanded_location exploc) const;
198 diagnostic_context *m_context;
200 /* The JSON object for the invocation object. */
201 sarif_invocation *m_invocation_obj;
203 /* The JSON array of pending diagnostics. */
204 json::array *m_results_array;
206 /* The JSON object for the result object (if any) in the current
207 diagnostic group. */
208 sarif_result *m_cur_group_result;
210 hash_set <const char *> m_filenames;
211 bool m_seen_any_relative_paths;
212 hash_set <free_string_hash> m_rule_id_set;
213 json::array *m_rules_arr;
215 /* The set of all CWE IDs we've seen, if any. */
216 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
218 int m_tabstop;
221 static sarif_builder *the_builder;
223 /* class sarif_invocation : public json::object. */
225 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
226 Add an object representing the ICE to the notifications array. */
228 void
229 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
230 diagnostic_info *diagnostic,
231 sarif_builder *builder)
233 m_success = false;
235 sarif_ice_notification *notification_obj
236 = new sarif_ice_notification (context, diagnostic, builder);
237 m_notifications_arr->append (notification_obj);
240 void
241 sarif_invocation::prepare_to_flush ()
243 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
244 set ("executionSuccessful", new json::literal (m_success));
246 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
247 set ("toolExecutionNotifications", m_notifications_arr);
250 /* class sarif_result : public json::object. */
252 /* Handle secondary diagnostics that occur within a diagnostic group.
253 The closest SARIF seems to have to nested diagnostics is the
254 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
255 so we lazily set this property and populate the array if and when
256 secondary diagnostics occur (such as notes to a warning). */
258 void
259 sarif_result::on_nested_diagnostic (diagnostic_context *context,
260 diagnostic_info *diagnostic,
261 diagnostic_t /*orig_diag_kind*/,
262 sarif_builder *builder)
264 if (!m_related_locations_arr)
266 m_related_locations_arr = new json::array ();
267 set ("relatedLocations", m_related_locations_arr);
270 /* We don't yet generate meaningful logical locations for notes;
271 sometimes these will related to current_function_decl, but
272 often they won't. */
273 json::object *location_obj
274 = builder->make_location_object (*diagnostic->richloc, NULL);
275 json::object *message_obj
276 = builder->make_message_object (pp_formatted_text (context->printer));
277 pp_clear_output_area (context->printer);
278 location_obj->set ("message", message_obj);
280 m_related_locations_arr->append (location_obj);
283 /* class sarif_ice_notification : public json::object. */
285 /* sarif_ice_notification's ctor.
286 DIAGNOSTIC is an internal compiler error. */
288 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
289 diagnostic_info *diagnostic,
290 sarif_builder *builder)
292 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
293 json::array *locations_arr = builder->make_locations_arr (diagnostic);
294 set ("locations", locations_arr);
296 /* "message" property (SARIF v2.1.0 section 3.85.5). */
297 json::object *message_obj
298 = builder->make_message_object (pp_formatted_text (context->printer));
299 pp_clear_output_area (context->printer);
300 set ("message", message_obj);
302 /* "level" property (SARIF v2.1.0 section 3.58.6). */
303 set ("level", new json::string ("error"));
306 /* class sarif_builder. */
308 /* sarif_builder's ctor. */
310 sarif_builder::sarif_builder (diagnostic_context *context)
311 : m_context (context),
312 m_invocation_obj (new sarif_invocation ()),
313 m_results_array (new json::array ()),
314 m_cur_group_result (NULL),
315 m_seen_any_relative_paths (false),
316 m_rule_id_set (),
317 m_rules_arr (new json::array ()),
318 m_tabstop (context->tabstop)
322 /* Implementation of "end_diagnostic" for SARIF output. */
324 void
325 sarif_builder::end_diagnostic (diagnostic_context *context,
326 diagnostic_info *diagnostic,
327 diagnostic_t orig_diag_kind)
329 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
331 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
332 return;
335 if (m_cur_group_result)
336 /* Nested diagnostic. */
337 m_cur_group_result->on_nested_diagnostic (context,
338 diagnostic,
339 orig_diag_kind,
340 this);
341 else
343 /* Top-level diagnostic. */
344 sarif_result *result_obj
345 = make_result_object (context, diagnostic, orig_diag_kind);
346 m_results_array->append (result_obj);
347 m_cur_group_result = result_obj;
351 /* Implementation of "end_group_cb" for SARIF output. */
353 void
354 sarif_builder::end_group ()
356 m_cur_group_result = NULL;
359 /* Create a top-level object, and add it to all the results
360 (and other entities) we've seen so far.
362 Flush it all to OUTF. */
364 void
365 sarif_builder::flush_to_file (FILE *outf)
367 m_invocation_obj->prepare_to_flush ();
368 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
369 top->dump (outf);
370 m_invocation_obj = NULL;
371 m_results_array = NULL;
372 fprintf (outf, "\n");
373 delete top;
376 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
377 property (SARIF v2.1.0 section 3.27.10).
379 Return NULL if there isn't one. */
381 static const char *
382 maybe_get_sarif_level (diagnostic_t diag_kind)
384 switch (diag_kind)
386 case DK_WARNING:
387 return "warning";
388 case DK_ERROR:
389 return "error";
390 case DK_NOTE:
391 case DK_ANACHRONISM:
392 return "note";
393 default:
394 return NULL;
398 /* Make a string for DIAG_KIND suitable for use a ruleId
399 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
400 have anything better to use. */
402 static char *
403 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
405 static const char *const diagnostic_kind_text[] = {
406 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
407 #include "diagnostic.def"
408 #undef DEFINE_DIAGNOSTIC_KIND
409 "must-not-happen"
411 /* Lose the trailing ": ". */
412 const char *kind_text = diagnostic_kind_text[diag_kind];
413 size_t len = strlen (kind_text);
414 gcc_assert (len > 2);
415 gcc_assert (kind_text[len - 2] == ':');
416 gcc_assert (kind_text[len - 1] == ' ');
417 char *rstrip = xstrdup (kind_text);
418 rstrip[len - 2] = '\0';
419 return rstrip;
422 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
424 sarif_result *
425 sarif_builder::make_result_object (diagnostic_context *context,
426 diagnostic_info *diagnostic,
427 diagnostic_t orig_diag_kind)
429 sarif_result *result_obj = new sarif_result ();
431 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
432 /* Ideally we'd have an option_name for these. */
433 if (char *option_text
434 = context->option_name (context, diagnostic->option_index,
435 orig_diag_kind, diagnostic->kind))
437 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
438 Set ruleId referencing them. */
439 result_obj->set ("ruleId", new json::string (option_text));
440 if (m_rule_id_set.contains (option_text))
441 free (option_text);
442 else
444 /* This is the first time we've seen this ruleId. */
445 /* Add to set, taking ownership. */
446 m_rule_id_set.add (option_text);
448 json::object *reporting_desc_obj
449 = make_reporting_descriptor_object_for_warning (context,
450 diagnostic,
451 orig_diag_kind,
452 option_text);
453 m_rules_arr->append (reporting_desc_obj);
456 else
458 /* Otherwise, we have an "error" or a stray "note"; use the
459 diagnostic kind as the ruleId, so that the result object at least
460 has a ruleId.
461 We don't bother creating reportingDescriptor objects for these. */
462 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
463 result_obj->set ("ruleId", new json::string (rule_id));
464 free (rule_id);
467 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
468 if (diagnostic->metadata)
469 if (int cwe_id = diagnostic->metadata->get_cwe ())
471 json::array *taxa_arr = new json::array ();
472 json::object *cwe_id_obj
473 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
474 taxa_arr->append (cwe_id_obj);
475 result_obj->set ("taxa", taxa_arr);
478 /* "level" property (SARIF v2.1.0 section 3.27.10). */
479 if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
480 result_obj->set ("level", new json::string (sarif_level));
482 /* "message" property (SARIF v2.1.0 section 3.27.11). */
483 json::object *message_obj
484 = make_message_object (pp_formatted_text (context->printer));
485 pp_clear_output_area (context->printer);
486 result_obj->set ("message", message_obj);
488 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
489 json::array *locations_arr = make_locations_arr (diagnostic);
490 result_obj->set ("locations", locations_arr);
492 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
493 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
495 json::array *code_flows_arr = new json::array ();
496 json::object *code_flow_obj = make_code_flow_object (*path);
497 code_flows_arr->append (code_flow_obj);
498 result_obj->set ("codeFlows", code_flows_arr);
501 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
502 set up later, if any nested diagnostics occur within this diagnostic
503 group. */
505 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
506 const rich_location *richloc = diagnostic->richloc;
507 if (richloc->get_num_fixit_hints ())
509 json::array *fix_arr = new json::array ();
510 json::object *fix_obj = make_fix_object (*richloc);
511 fix_arr->append (fix_obj);
512 result_obj->set ("fixes", fix_arr);
515 return result_obj;
518 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
519 for a GCC warning. */
521 json::object *
522 sarif_builder::
523 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
524 diagnostic_info *diagnostic,
525 diagnostic_t /*orig_diag_kind*/,
526 const char *option_text)
528 json::object *reporting_desc = new json::object ();
530 /* "id" property (SARIF v2.1.0 section 3.49.3). */
531 reporting_desc->set ("id", new json::string (option_text));
533 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
534 it seems redundant compared to "id". */
536 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
537 if (context->get_option_url)
539 char *option_url
540 = context->get_option_url (context, diagnostic->option_index);
541 if (option_url)
543 reporting_desc->set ("helpUri", new json::string (option_url));
544 free (option_url);
548 return reporting_desc;
551 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
552 for CWE_ID, for use within the CWE taxa array. */
554 json::object *
555 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
557 json::object *reporting_desc = new json::object ();
559 /* "id" property (SARIF v2.1.0 section 3.49.3). */
561 pretty_printer pp;
562 pp_printf (&pp, "%i", cwe_id);
563 reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
566 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
568 char *url = get_cwe_url (cwe_id);
569 reporting_desc->set ("helpUri", new json::string (url));
570 free (url);
573 return reporting_desc;
576 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
577 referencing CWE_ID, for use within a result object.
578 Also, add CWE_ID to m_cwe_id_set. */
580 json::object *
581 sarif_builder::
582 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
584 json::object *desc_ref_obj = new json::object ();
586 /* "id" property (SARIF v2.1.0 section 3.52.4). */
588 pretty_printer pp;
589 pp_printf (&pp, "%i", cwe_id);
590 desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
593 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
594 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
595 desc_ref_obj->set ("toolComponent", comp_ref_obj);
597 /* Add CWE_ID to our set. */
598 gcc_assert (cwe_id > 0);
599 m_cwe_id_set.add (cwe_id);
601 return desc_ref_obj;
604 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
605 references the CWE taxonomy. */
607 json::object *
608 sarif_builder::
609 make_tool_component_reference_object_for_cwe () const
611 json::object *comp_ref_obj = new json::object ();
613 /* "name" property (SARIF v2.1.0 section 3.54.3). */
614 comp_ref_obj->set ("name", new json::string ("cwe"));
616 return comp_ref_obj;
619 /* Make an array suitable for use as the "locations" property of:
620 - a "result" object (SARIF v2.1.0 section 3.27.12), or
621 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
623 json::array *
624 sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
626 json::array *locations_arr = new json::array ();
627 const logical_location *logical_loc = NULL;
628 if (m_context->m_client_data_hooks)
629 logical_loc
630 = m_context->m_client_data_hooks->get_current_logical_location ();
632 json::object *location_obj
633 = make_location_object (*diagnostic->richloc, logical_loc);
634 locations_arr->append (location_obj);
635 return locations_arr;
638 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
639 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
641 void
642 sarif_builder::
643 set_any_logical_locs_arr (json::object *location_obj,
644 const logical_location *logical_loc)
646 if (!logical_loc)
647 return;
648 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
649 json::array *location_locs_arr = new json::array ();
650 location_locs_arr->append (logical_loc_obj);
651 location_obj->set ("logicalLocations", location_locs_arr);
654 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
655 and LOGICAL_LOC. */
657 json::object *
658 sarif_builder::make_location_object (const rich_location &rich_loc,
659 const logical_location *logical_loc)
661 json::object *location_obj = new json::object ();
663 /* Get primary loc from RICH_LOC. */
664 location_t loc = rich_loc.get_loc ();
666 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
667 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
668 location_obj->set ("physicalLocation", phs_loc_obj);
670 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
671 set_any_logical_locs_arr (location_obj, logical_loc);
673 return location_obj;
676 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
677 within a diagnostic_path. */
679 json::object *
680 sarif_builder::make_location_object (const diagnostic_event &event)
682 json::object *location_obj = new json::object ();
684 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
685 location_t loc = event.get_location ();
686 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
687 location_obj->set ("physicalLocation", phs_loc_obj);
689 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
690 const logical_location *logical_loc = event.get_logical_location ();
691 set_any_logical_locs_arr (location_obj, logical_loc);
693 /* "message" property (SARIF v2.1.0 section 3.28.5). */
694 label_text ev_desc = event.get_desc (false);
695 json::object *message_obj = make_message_object (ev_desc.get ());
696 location_obj->set ("message", message_obj);
698 return location_obj;
701 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
702 or return NULL;
703 Add any filename to the m_artifacts. */
705 json::object *
706 sarif_builder::maybe_make_physical_location_object (location_t loc)
708 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
709 return NULL;
711 json::object *phys_loc_obj = new json::object ();
713 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
714 json::object *artifact_loc_obj = make_artifact_location_object (loc);
715 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
716 m_filenames.add (LOCATION_FILE (loc));
718 /* "region" property (SARIF v2.1.0 section 3.29.4). */
719 if (json::object *region_obj = maybe_make_region_object (loc))
720 phys_loc_obj->set ("region", region_obj);
722 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
723 if (json::object *context_region_obj
724 = maybe_make_region_object_for_context (loc))
725 phys_loc_obj->set ("contextRegion", context_region_obj);
727 /* Instead, we add artifacts to the run as a whole,
728 with artifact.contents.
729 Could do both, though. */
731 return phys_loc_obj;
734 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
735 or return NULL. */
737 json::object *
738 sarif_builder::make_artifact_location_object (location_t loc)
740 return make_artifact_location_object (LOCATION_FILE (loc));
743 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
744 for when we need to express paths relative to PWD. */
746 #define PWD_PROPERTY_NAME ("PWD")
748 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
749 or return NULL. */
751 json::object *
752 sarif_builder::make_artifact_location_object (const char *filename)
754 json::object *artifact_loc_obj = new json::object ();
756 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
757 artifact_loc_obj->set ("uri", new json::string (filename));
759 if (filename[0] != '/')
761 /* If we have a relative path, set the "uriBaseId" property
762 (SARIF v2.1.0 section 3.4.4). */
763 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
764 m_seen_any_relative_paths = true;
767 return artifact_loc_obj;
770 /* Get the PWD, or NULL, as an absolute file-based URI,
771 adding a trailing forward slash (as required by SARIF v2.1.0
772 section 3.14.14). */
774 static char *
775 make_pwd_uri_str ()
777 /* The prefix of a file-based URI, up to, but not including the path. */
778 #define FILE_PREFIX ("file://")
780 const char *pwd = getpwd ();
781 if (!pwd)
782 return NULL;
783 size_t len = strlen (pwd);
784 if (len == 0 || pwd[len - 1] != '/')
785 return concat (FILE_PREFIX, pwd, "/", NULL);
786 else
788 gcc_assert (pwd[len - 1] == '/');
789 return concat (FILE_PREFIX, pwd, NULL);
793 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
794 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
795 section 3.14.14) when we have any relative paths. */
797 json::object *
798 sarif_builder::make_artifact_location_object_for_pwd () const
800 json::object *artifact_loc_obj = new json::object ();
802 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
803 if (char *pwd = make_pwd_uri_str ())
805 gcc_assert (strlen (pwd) > 0);
806 gcc_assert (pwd[strlen (pwd) - 1] == '/');
807 artifact_loc_obj->set ("uri", new json::string (pwd));
808 free (pwd);
811 return artifact_loc_obj;
814 /* Get the column number within EXPLOC. */
817 sarif_builder::get_sarif_column (expanded_location exploc) const
819 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
820 return location_compute_display_column (exploc, policy);
823 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
824 or return NULL. */
826 json::object *
827 sarif_builder::maybe_make_region_object (location_t loc) const
829 location_t caret_loc = get_pure_location (loc);
831 if (caret_loc <= BUILTINS_LOCATION)
832 return NULL;
834 location_t start_loc = get_start (loc);
835 location_t finish_loc = get_finish (loc);
837 expanded_location exploc_caret = expand_location (caret_loc);
838 expanded_location exploc_start = expand_location (start_loc);
839 expanded_location exploc_finish = expand_location (finish_loc);
841 if (exploc_start.file !=exploc_caret.file)
842 return NULL;
843 if (exploc_finish.file !=exploc_caret.file)
844 return NULL;
846 json::object *region_obj = new json::object ();
848 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
849 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
851 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
852 region_obj->set ("startColumn",
853 new json::integer_number (get_sarif_column (exploc_start)));
855 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
856 if (exploc_finish.line != exploc_start.line)
857 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
859 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
860 This expresses the column immediately beyond the range. */
862 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
863 region_obj->set ("endColumn", new json::integer_number (next_column));
866 return region_obj;
869 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
870 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
872 This is similar to maybe_make_region_object, but ignores column numbers,
873 covering the line(s) as a whole, and including a "snippet" property
874 embedding those source lines, making it easier for consumers to show
875 the pertinent source. */
877 json::object *
878 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
880 location_t caret_loc = get_pure_location (loc);
882 if (caret_loc <= BUILTINS_LOCATION)
883 return NULL;
885 location_t start_loc = get_start (loc);
886 location_t finish_loc = get_finish (loc);
888 expanded_location exploc_caret = expand_location (caret_loc);
889 expanded_location exploc_start = expand_location (start_loc);
890 expanded_location exploc_finish = expand_location (finish_loc);
892 if (exploc_start.file !=exploc_caret.file)
893 return NULL;
894 if (exploc_finish.file !=exploc_caret.file)
895 return NULL;
897 json::object *region_obj = new json::object ();
899 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
900 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
902 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
903 if (exploc_finish.line != exploc_start.line)
904 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
906 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
907 if (json::object *artifact_content_obj
908 = maybe_make_artifact_content_object (exploc_start.file,
909 exploc_start.line,
910 exploc_finish.line))
911 region_obj->set ("snippet", artifact_content_obj);
913 return region_obj;
916 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
917 of HINT (as per SARIF v2.1.0 section 3.57.3). */
919 json::object *
920 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
922 location_t start_loc = hint.get_start_loc ();
923 location_t next_loc = hint.get_next_loc ();
925 expanded_location exploc_start = expand_location (start_loc);
926 expanded_location exploc_next = expand_location (next_loc);
928 json::object *region_obj = new json::object ();
930 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
931 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
933 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
934 int start_col = get_sarif_column (exploc_start);
935 region_obj->set ("startColumn",
936 new json::integer_number (start_col));
938 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
939 if (exploc_next.line != exploc_start.line)
940 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
942 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
943 This expresses the column immediately beyond the range. */
944 int next_col = get_sarif_column (exploc_next);
945 region_obj->set ("endColumn", new json::integer_number (next_col));
947 return region_obj;
950 /* Attempt to get a string for a logicalLocation's "kind" property
951 (SARIF v2.1.0 section 3.33.7).
952 Return NULL if unknown. */
954 static const char *
955 maybe_get_sarif_kind (enum logical_location_kind kind)
957 switch (kind)
959 default:
960 gcc_unreachable ();
961 case LOGICAL_LOCATION_KIND_UNKNOWN:
962 return NULL;
964 case LOGICAL_LOCATION_KIND_FUNCTION:
965 return "function";
966 case LOGICAL_LOCATION_KIND_MEMBER:
967 return "member";
968 case LOGICAL_LOCATION_KIND_MODULE:
969 return "module";
970 case LOGICAL_LOCATION_KIND_NAMESPACE:
971 return "namespace";
972 case LOGICAL_LOCATION_KIND_TYPE:
973 return "type";
974 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
975 return "returnType";
976 case LOGICAL_LOCATION_KIND_PARAMETER:
977 return "parameter";
978 case LOGICAL_LOCATION_KIND_VARIABLE:
979 return "variable";
983 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
984 or return NULL. */
986 json::object *
987 sarif_builder::
988 make_logical_location_object (const logical_location &logical_loc) const
990 json::object *logical_loc_obj = new json::object ();
992 /* "name" property (SARIF v2.1.0 section 3.33.4). */
993 if (const char *short_name = logical_loc.get_short_name ())
994 logical_loc_obj->set ("name", new json::string (short_name));
996 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
997 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
998 logical_loc_obj->set ("fullyQualifiedName",
999 new json::string (name_with_scope));
1001 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1002 if (const char *internal_name = logical_loc.get_internal_name ())
1003 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
1005 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1006 enum logical_location_kind kind = logical_loc.get_kind ();
1007 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1008 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
1010 return logical_loc_obj;
1013 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1015 json::object *
1016 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1018 json::object *code_flow_obj = new json::object ();
1020 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
1021 Currently we only support one thread per result. */
1022 json::array *thread_flows_arr = new json::array ();
1023 json::object *thread_flow_obj = make_thread_flow_object (path);
1024 thread_flows_arr->append (thread_flow_obj);
1025 code_flow_obj->set ("threadFlows", thread_flows_arr);
1027 return code_flow_obj;
1030 /* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
1032 json::object *
1033 sarif_builder::make_thread_flow_object (const diagnostic_path &path)
1035 json::object *thread_flow_obj = new json::object ();
1037 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
1038 json::array *locations_arr = new json::array ();
1039 for (unsigned i = 0; i < path.num_events (); i++)
1041 const diagnostic_event &event = path.get_event (i);
1042 json::object *thread_flow_loc_obj
1043 = make_thread_flow_location_object (event);
1044 locations_arr->append (thread_flow_loc_obj);
1046 thread_flow_obj->set ("locations", locations_arr);
1048 return thread_flow_obj;
1051 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1053 json::object *
1054 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
1056 json::object *thread_flow_loc_obj = new json::object ();
1058 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1059 json::object *location_obj = make_location_object (ev);
1060 thread_flow_loc_obj->set ("location", location_obj);
1062 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1063 diagnostic_event::meaning m = ev.get_meaning ();
1064 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1065 thread_flow_loc_obj->set ("kinds", kinds_arr);
1067 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1068 thread_flow_loc_obj->set ("nestingLevel",
1069 new json::integer_number (ev.get_stack_depth ()));
1071 /* It might be nice to eventually implement the following for -fanalyzer:
1072 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1073 - the "state" property (SARIF v2.1.0 section 3.38.9)
1074 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1076 return thread_flow_loc_obj;
1079 /* If M has any known meaning, make a json array suitable for the "kinds"
1080 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1082 Otherwise, return NULL. */
1084 json::array *
1085 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1087 if (m.m_verb == diagnostic_event::VERB_unknown
1088 && m.m_noun == diagnostic_event::NOUN_unknown
1089 && m.m_property == diagnostic_event::PROPERTY_unknown)
1090 return NULL;
1092 json::array *kinds_arr = new json::array ();
1093 if (const char *verb_str
1094 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1095 kinds_arr->append (new json::string (verb_str));
1096 if (const char *noun_str
1097 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1098 kinds_arr->append (new json::string (noun_str));
1099 if (const char *property_str
1100 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1101 kinds_arr->append (new json::string (property_str));
1102 return kinds_arr;
1105 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1107 json::object *
1108 sarif_builder::make_message_object (const char *msg) const
1110 json::object *message_obj = new json::object ();
1112 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1113 message_obj->set ("text", new json::string (msg));
1115 return message_obj;
1118 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1119 for MSG. */
1121 json::object *
1122 sarif_builder::make_multiformat_message_string (const char *msg) const
1124 json::object *message_obj = new json::object ();
1126 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1127 message_obj->set ("text", new json::string (msg));
1129 return message_obj;
1132 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1133 #define SARIF_VERSION "2.1.0"
1135 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1136 Take ownership of INVOCATION_OBJ and RESULTS. */
1138 json::object *
1139 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1140 json::array *results)
1142 json::object *log_obj = new json::object ();
1144 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1145 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1147 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1148 log_obj->set ("version", new json::string (SARIF_VERSION));
1150 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1151 json::array *run_arr = new json::array ();
1152 json::object *run_obj = make_run_object (invocation_obj, results);
1153 run_arr->append (run_obj);
1154 log_obj->set ("runs", run_arr);
1156 return log_obj;
1159 /* Make a run object (SARIF v2.1.0 section 3.14).
1160 Take ownership of INVOCATION_OBJ and RESULTS. */
1162 json::object *
1163 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1164 json::array *results)
1166 json::object *run_obj = new json::object ();
1168 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1169 json::object *tool_obj = make_tool_object ();
1170 run_obj->set ("tool", tool_obj);
1172 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1173 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1174 run_obj->set ("taxonomies", taxonomies_arr);
1176 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1178 json::array *invocations_arr = new json::array ();
1179 invocations_arr->append (invocation_obj);
1180 run_obj->set ("invocations", invocations_arr);
1183 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1184 if (m_seen_any_relative_paths)
1186 json::object *orig_uri_base_ids = new json::object ();
1187 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1188 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1189 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1192 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1193 json::array *artifacts_arr = new json::array ();
1194 for (auto iter : m_filenames)
1196 json::object *artifact_obj = make_artifact_object (iter);
1197 artifacts_arr->append (artifact_obj);
1199 run_obj->set ("artifacts", artifacts_arr);
1201 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1202 run_obj->set ("results", results);
1204 return run_obj;
1207 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1209 json::object *
1210 sarif_builder::make_tool_object () const
1212 json::object *tool_obj = new json::object ();
1214 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1215 json::object *driver_obj = make_driver_tool_component_object ();
1216 tool_obj->set ("driver", driver_obj);
1218 /* Report plugins via the "extensions" property
1219 (SARIF v2.1.0 section 3.18.3). */
1220 if (m_context->m_client_data_hooks)
1221 if (const client_version_info *vinfo
1222 = m_context->m_client_data_hooks->get_any_version_info ())
1224 class my_plugin_visitor : public client_version_info :: plugin_visitor
1226 public:
1227 void on_plugin (const diagnostic_client_plugin_info &p) final override
1229 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1230 for the plugin. */
1231 json::object *plugin_obj = new json::object ();
1232 m_plugin_objs.safe_push (plugin_obj);
1234 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1235 if (const char *short_name = p.get_short_name ())
1236 plugin_obj->set ("name", new json::string (short_name));
1238 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1239 if (const char *full_name = p.get_full_name ())
1240 plugin_obj->set ("fullName", new json::string (full_name));
1242 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1243 if (const char *version = p.get_version ())
1244 plugin_obj->set ("version", new json::string (version));
1246 auto_vec <json::object *> m_plugin_objs;
1248 my_plugin_visitor v;
1249 vinfo->for_each_plugin (v);
1250 if (v.m_plugin_objs.length () > 0)
1252 json::array *extensions_arr = new json::array ();
1253 tool_obj->set ("extensions", extensions_arr);
1254 for (auto iter : v.m_plugin_objs)
1255 extensions_arr->append (iter);
1259 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1260 "extensions" (see toplev.cc: print_version). */
1262 return tool_obj;
1265 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1266 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1268 json::object *
1269 sarif_builder::make_driver_tool_component_object () const
1271 json::object *driver_obj = new json::object ();
1273 if (m_context->m_client_data_hooks)
1274 if (const client_version_info *vinfo
1275 = m_context->m_client_data_hooks->get_any_version_info ())
1277 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1278 if (const char *name = vinfo->get_tool_name ())
1279 driver_obj->set ("name", new json::string (name));
1281 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1282 if (char *full_name = vinfo->maybe_make_full_name ())
1284 driver_obj->set ("fullName", new json::string (full_name));
1285 free (full_name);
1288 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1289 if (const char *version = vinfo->get_version_string ())
1290 driver_obj->set ("version", new json::string (version));
1292 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1293 if (char *version_url = vinfo->maybe_make_version_url ())
1295 driver_obj->set ("informationUri", new json::string (version_url));
1296 free (version_url);
1300 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1301 driver_obj->set ("rules", m_rules_arr);
1303 return driver_obj;
1306 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1307 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1308 toolComponent (3.19) as per 3.19.3, representing the CWE.
1310 Otherwise return NULL. */
1312 json::array *
1313 sarif_builder::maybe_make_taxonomies_array () const
1315 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1316 if (!cwe_obj)
1317 return NULL;
1319 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1320 json::array *taxonomies_arr = new json::array ();
1321 taxonomies_arr->append (cwe_obj);
1322 return taxonomies_arr;
1325 /* If we've seen any CWE IDs, make a toolComponent object
1326 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1327 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1329 Otherwise return NULL. */
1331 json::object *
1332 sarif_builder::maybe_make_cwe_taxonomy_object () const
1334 if (m_cwe_id_set.is_empty ())
1335 return NULL;
1337 json::object *taxonomy_obj = new json::object ();
1339 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1340 taxonomy_obj->set ("name", new json::string ("CWE"));
1342 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1343 taxonomy_obj->set ("version", new json::string ("4.7"));
1345 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1346 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1348 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1349 json::object *short_desc
1350 = make_multiformat_message_string ("The MITRE"
1351 " Common Weakness Enumeration");
1352 taxonomy_obj->set ("shortDescription", short_desc);
1354 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1355 json::array *taxa_arr = new json::array ();
1356 for (auto cwe_id : m_cwe_id_set)
1358 json::object *cwe_taxon
1359 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1360 taxa_arr->append (cwe_taxon);
1362 taxonomy_obj->set ("taxa", taxa_arr);
1364 return taxonomy_obj;
1367 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1369 json::object *
1370 sarif_builder::make_artifact_object (const char *filename)
1372 json::object *artifact_obj = new json::object ();
1374 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1375 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1376 artifact_obj->set ("location", artifact_loc_obj);
1378 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1379 if (json::object *artifact_content_obj
1380 = maybe_make_artifact_content_object (filename))
1381 artifact_obj->set ("contents", artifact_content_obj);
1383 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1384 if (m_context->m_client_data_hooks)
1385 if (const char *source_lang
1386 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1387 (filename))
1388 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1390 return artifact_obj;
1393 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1394 full contents of FILENAME. */
1396 json::object *
1397 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1399 /* Let input.cc handle any charset conversion. */
1400 char_span utf8_content = get_source_file_content (filename);
1401 if (!utf8_content)
1402 return NULL;
1404 /* Don't add it if it's not valid UTF-8. */
1405 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1406 return NULL;
1408 json::object *artifact_content_obj = new json::object ();
1409 artifact_content_obj->set ("text",
1410 new json::string (utf8_content.get_buffer (),
1411 utf8_content.length ()));
1412 return artifact_content_obj;
1415 /* Attempt to read the given range of lines from FILENAME; return
1416 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1418 static char *
1419 get_source_lines (const char *filename,
1420 int start_line,
1421 int end_line)
1423 auto_vec<char> result;
1425 for (int line = start_line; line <= end_line; line++)
1427 char_span line_content = location_get_source_line (filename, line);
1428 if (!line_content.get_buffer ())
1429 return NULL;
1430 result.reserve (line_content.length () + 1);
1431 for (size_t i = 0; i < line_content.length (); i++)
1432 result.quick_push (line_content[i]);
1433 result.quick_push ('\n');
1435 result.safe_push ('\0');
1437 return xstrdup (result.address ());
1440 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1441 run of lines within FILENAME (including the endpoints). */
1443 json::object *
1444 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1445 int start_line,
1446 int end_line) const
1448 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1450 if (!text_utf8)
1451 return NULL;
1453 /* Don't add it if it's not valid UTF-8. */
1454 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1456 free (text_utf8);
1457 return NULL;
1460 json::object *artifact_content_obj = new json::object ();
1461 artifact_content_obj->set ("text", new json::string (text_utf8));
1462 free (text_utf8);
1464 return artifact_content_obj;
1467 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1469 json::object *
1470 sarif_builder::make_fix_object (const rich_location &richloc)
1472 json::object *fix_obj = new json::object ();
1474 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1475 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1476 json::array *artifact_change_arr = new json::array ();
1477 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1478 artifact_change_arr->append (artifact_change_obj);
1479 fix_obj->set ("artifactChanges", artifact_change_arr);
1481 return fix_obj;
1484 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1486 json::object *
1487 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1489 json::object *artifact_change_obj = new json::object ();
1491 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1492 json::object *artifact_location_obj
1493 = make_artifact_location_object (richloc.get_loc ());
1494 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1496 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1497 json::array *replacement_arr = new json::array ();
1498 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1500 const fixit_hint *hint = richloc.get_fixit_hint (i);
1501 json::object *replacement_obj = make_replacement_object (*hint);
1502 replacement_arr->append (replacement_obj);
1504 artifact_change_obj->set ("replacements", replacement_arr);
1506 return artifact_change_obj;
1509 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1511 json::object *
1512 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1514 json::object *replacement_obj = new json::object ();
1516 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1517 json::object *region_obj = make_region_object_for_hint (hint);
1518 replacement_obj->set ("deletedRegion", region_obj);
1520 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1521 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1522 replacement_obj->set ("insertedContent", content_obj);
1524 return replacement_obj;
1527 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1529 json::object *
1530 sarif_builder::make_artifact_content_object (const char *text) const
1532 json::object *content_obj = new json::object ();
1534 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1535 content_obj->set ("text", new json::string (text));
1537 return content_obj;
1540 /* No-op implementation of "begin_diagnostic" for SARIF output. */
1542 static void
1543 sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
1547 /* Implementation of "end_diagnostic" for SARIF output. */
1549 static void
1550 sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
1551 diagnostic_t orig_diag_kind)
1553 gcc_assert (the_builder);
1554 the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
1557 /* No-op implementation of "begin_group_cb" for SARIF output. */
1559 static void
1560 sarif_begin_group (diagnostic_context *)
1564 /* Implementation of "end_group_cb" for SARIF output. */
1566 static void
1567 sarif_end_group (diagnostic_context *)
1569 gcc_assert (the_builder);
1570 the_builder->end_group ();
1573 /* Flush the top-level array to OUTF. */
1575 static void
1576 sarif_flush_to_file (FILE *outf)
1578 gcc_assert (the_builder);
1579 the_builder->flush_to_file (outf);
1580 delete the_builder;
1581 the_builder = NULL;
1584 /* Callback for final cleanup for SARIF output to stderr. */
1586 static void
1587 sarif_stderr_final_cb (diagnostic_context *)
1589 gcc_assert (the_builder);
1590 sarif_flush_to_file (stderr);
1593 static char *sarif_output_base_file_name;
1595 /* Callback for final cleanup for SARIF output to a file. */
1597 static void
1598 sarif_file_final_cb (diagnostic_context *)
1600 char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
1601 FILE *outf = fopen (filename, "w");
1602 if (!outf)
1604 const char *errstr = xstrerror (errno);
1605 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1606 filename, errstr);
1607 free (filename);
1608 return;
1610 gcc_assert (the_builder);
1611 sarif_flush_to_file (outf);
1612 fclose (outf);
1613 free (filename);
1616 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1617 occurs. */
1619 static void
1620 sarif_ice_handler (diagnostic_context *context)
1622 /* Attempt to ensure that a .sarif file is written out. */
1623 diagnostic_finish (context);
1625 /* Print a header for the remaining output to stderr, and
1626 return, attempting to print the usual ICE messages to
1627 stderr. Hopefully this will be helpful to the user in
1628 indicating what's gone wrong (also for DejaGnu, for pruning
1629 those messages). */
1630 fnotice (stderr, "Internal compiler error:\n");
1633 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1634 to a file). */
1636 static void
1637 diagnostic_output_format_init_sarif (diagnostic_context *context)
1639 the_builder = new sarif_builder (context);
1641 /* Override callbacks. */
1642 context->begin_diagnostic = sarif_begin_diagnostic;
1643 context->end_diagnostic = sarif_end_diagnostic;
1644 context->begin_group_cb = sarif_begin_group;
1645 context->end_group_cb = sarif_end_group;
1646 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
1647 context->ice_handler_cb = sarif_ice_handler;
1649 /* The metadata is handled in SARIF format, rather than as text. */
1650 context->show_cwe = false;
1651 context->show_rules = false;
1653 /* The option is handled in SARIF format, rather than as text. */
1654 context->show_option_requested = false;
1656 /* Don't colorize the text. */
1657 pp_show_color (context->printer) = false;
1660 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1662 void
1663 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1665 diagnostic_output_format_init_sarif (context);
1666 context->final_cb = sarif_stderr_final_cb;
1669 /* Populate CONTEXT in preparation for SARIF output to a file named
1670 BASE_FILE_NAME.sarif. */
1672 void
1673 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1674 const char *base_file_name)
1676 diagnostic_output_format_init_sarif (context);
1677 context->final_cb = sarif_file_final_cb;
1678 sarif_output_base_file_name = xstrdup (base_file_name);