hppa: Export main in pr104869.C on hpux
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blob1bb728654e6cc07690e7917336e9d2e1c28e1cad
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 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 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 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);
162 void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
163 diagnostic_t orig_diag_kind);
164 void emit_diagram (diagnostic_context *context,
165 const diagnostic_diagram &diagram);
166 void end_group ();
168 void flush_to_file (FILE *outf);
170 json::array *make_locations_arr (diagnostic_info *diagnostic);
171 json::object *make_location_object (const rich_location &rich_loc,
172 const logical_location *logical_loc);
173 json::object *make_message_object (const char *msg) const;
174 json::object *
175 make_message_object_for_diagram (diagnostic_context *context,
176 const diagnostic_diagram &diagram);
178 private:
179 sarif_result *make_result_object (diagnostic_context *context,
180 diagnostic_info *diagnostic,
181 diagnostic_t orig_diag_kind);
182 void set_any_logical_locs_arr (json::object *location_obj,
183 const logical_location *logical_loc);
184 json::object *make_location_object (const diagnostic_event &event);
185 json::object *
186 make_logical_location_object (const logical_location &logical_loc) const;
187 json::object *make_code_flow_object (const diagnostic_path &path);
188 json::object *
189 make_thread_flow_location_object (const diagnostic_event &event,
190 int path_event_idx);
191 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
192 json::object *maybe_make_physical_location_object (location_t loc);
193 json::object *make_artifact_location_object (location_t loc);
194 json::object *make_artifact_location_object (const char *filename);
195 json::object *make_artifact_location_object_for_pwd () const;
196 json::object *maybe_make_region_object (location_t loc) const;
197 json::object *maybe_make_region_object_for_context (location_t loc) const;
198 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
199 json::object *make_multiformat_message_string (const char *msg) const;
200 json::object *make_top_level_object (sarif_invocation *invocation_obj,
201 json::array *results);
202 json::object *make_run_object (sarif_invocation *invocation_obj,
203 json::array *results);
204 json::object *make_tool_object () const;
205 json::object *make_driver_tool_component_object () const;
206 json::array *maybe_make_taxonomies_array () const;
207 json::object *maybe_make_cwe_taxonomy_object () const;
208 json::object *make_tool_component_reference_object_for_cwe () const;
209 json::object *
210 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
211 diagnostic_info *diagnostic,
212 diagnostic_t orig_diag_kind,
213 const char *option_text);
214 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
215 json::object *
216 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
217 json::object *make_artifact_object (const char *filename);
218 char *get_source_lines (const char *filename,
219 int start_line,
220 int end_line) const;
221 json::object *maybe_make_artifact_content_object (const char *filename) const;
222 json::object *maybe_make_artifact_content_object (const char *filename,
223 int start_line,
224 int end_line) const;
225 json::object *make_fix_object (const rich_location &rich_loc);
226 json::object *make_artifact_change_object (const rich_location &richloc);
227 json::object *make_replacement_object (const fixit_hint &hint) const;
228 json::object *make_artifact_content_object (const char *text) const;
229 int get_sarif_column (expanded_location exploc) const;
231 diagnostic_context *m_context;
233 /* The JSON object for the invocation object. */
234 sarif_invocation *m_invocation_obj;
236 /* The JSON array of pending diagnostics. */
237 json::array *m_results_array;
239 /* The JSON object for the result object (if any) in the current
240 diagnostic group. */
241 sarif_result *m_cur_group_result;
243 hash_set <const char *> m_filenames;
244 bool m_seen_any_relative_paths;
245 hash_set <free_string_hash> m_rule_id_set;
246 json::array *m_rules_arr;
248 /* The set of all CWE IDs we've seen, if any. */
249 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
251 int m_tabstop;
254 /* class sarif_object : public json::object. */
256 sarif_property_bag &
257 sarif_object::get_or_create_properties ()
259 json::value *properties_val = get ("properties");
260 if (properties_val)
262 if (properties_val->get_kind () == json::JSON_OBJECT)
263 return *static_cast <sarif_property_bag *> (properties_val);
266 sarif_property_bag *bag = new sarif_property_bag ();
267 set ("properties", bag);
268 return *bag;
271 /* class sarif_invocation : public sarif_object. */
273 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
274 Add an object representing the ICE to the notifications array. */
276 void
277 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
278 diagnostic_info *diagnostic,
279 sarif_builder *builder)
281 m_success = false;
283 sarif_ice_notification *notification_obj
284 = new sarif_ice_notification (context, diagnostic, builder);
285 m_notifications_arr->append (notification_obj);
288 void
289 sarif_invocation::prepare_to_flush (diagnostic_context *context)
291 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
292 set_bool ("executionSuccessful", m_success);
294 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
295 set ("toolExecutionNotifications", m_notifications_arr);
297 /* Call client hook, allowing it to create a custom property bag for
298 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
299 if (auto client_data_hooks = context->get_client_data_hooks ())
300 client_data_hooks->add_sarif_invocation_properties (*this);
303 /* class sarif_result : public sarif_object. */
305 /* Handle secondary diagnostics that occur within a diagnostic group.
306 The closest SARIF seems to have to nested diagnostics is the
307 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
308 so we lazily set this property and populate the array if and when
309 secondary diagnostics occur (such as notes to a warning). */
311 void
312 sarif_result::on_nested_diagnostic (diagnostic_context *context,
313 diagnostic_info *diagnostic,
314 diagnostic_t /*orig_diag_kind*/,
315 sarif_builder *builder)
317 /* We don't yet generate meaningful logical locations for notes;
318 sometimes these will related to current_function_decl, but
319 often they won't. */
320 json::object *location_obj
321 = builder->make_location_object (*diagnostic->richloc, NULL);
322 json::object *message_obj
323 = builder->make_message_object (pp_formatted_text (context->printer));
324 pp_clear_output_area (context->printer);
325 location_obj->set ("message", message_obj);
327 add_related_location (location_obj);
330 /* Handle diagrams that occur within a diagnostic group.
331 The closest thing in SARIF seems to be to add a location to the
332 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
333 and to put the diagram into the "message" property of that location
334 (SARIF v2.1.0 section 3.28.5). */
336 void
337 sarif_result::on_diagram (diagnostic_context *context,
338 const diagnostic_diagram &diagram,
339 sarif_builder *builder)
341 json::object *location_obj = new json::object ();
342 json::object *message_obj
343 = builder->make_message_object_for_diagram (context, diagram);
344 location_obj->set ("message", message_obj);
346 add_related_location (location_obj);
349 /* Add LOCATION_OBJ to this result's "relatedLocations" array,
350 creating it if it doesn't yet exist. */
352 void
353 sarif_result::add_related_location (json::object *location_obj)
355 if (!m_related_locations_arr)
357 m_related_locations_arr = new json::array ();
358 set ("relatedLocations", m_related_locations_arr);
360 m_related_locations_arr->append (location_obj);
363 /* class sarif_ice_notification : public sarif_object. */
365 /* sarif_ice_notification's ctor.
366 DIAGNOSTIC is an internal compiler error. */
368 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
369 diagnostic_info *diagnostic,
370 sarif_builder *builder)
372 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
373 json::array *locations_arr = builder->make_locations_arr (diagnostic);
374 set ("locations", locations_arr);
376 /* "message" property (SARIF v2.1.0 section 3.85.5). */
377 json::object *message_obj
378 = builder->make_message_object (pp_formatted_text (context->printer));
379 pp_clear_output_area (context->printer);
380 set ("message", message_obj);
382 /* "level" property (SARIF v2.1.0 section 3.58.6). */
383 set_string ("level", "error");
386 /* class sarif_thread_flow : public sarif_object. */
388 sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
390 /* "id" property (SARIF v2.1.0 section 3.37.2). */
391 label_text name (thread.get_name (false));
392 set_string ("id", name.get ());
394 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
395 m_locations_arr = new json::array ();
396 set ("locations", m_locations_arr);
399 /* class sarif_builder. */
401 /* sarif_builder's ctor. */
403 sarif_builder::sarif_builder (diagnostic_context *context)
404 : m_context (context),
405 m_invocation_obj (new sarif_invocation ()),
406 m_results_array (new json::array ()),
407 m_cur_group_result (NULL),
408 m_seen_any_relative_paths (false),
409 m_rule_id_set (),
410 m_rules_arr (new json::array ()),
411 m_tabstop (context->m_tabstop)
415 /* Implementation of "end_diagnostic" for SARIF output. */
417 void
418 sarif_builder::end_diagnostic (diagnostic_context *context,
419 diagnostic_info *diagnostic,
420 diagnostic_t orig_diag_kind)
422 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
424 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
425 return;
428 if (m_cur_group_result)
429 /* Nested diagnostic. */
430 m_cur_group_result->on_nested_diagnostic (context,
431 diagnostic,
432 orig_diag_kind,
433 this);
434 else
436 /* Top-level diagnostic. */
437 sarif_result *result_obj
438 = make_result_object (context, diagnostic, orig_diag_kind);
439 m_results_array->append (result_obj);
440 m_cur_group_result = result_obj;
444 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
445 for SARIF output. */
447 void
448 sarif_builder::emit_diagram (diagnostic_context *context,
449 const diagnostic_diagram &diagram)
451 /* We must be within the emission of a top-level diagnostic. */
452 gcc_assert (m_cur_group_result);
453 m_cur_group_result->on_diagram (context, diagram, this);
456 /* Implementation of "end_group_cb" for SARIF output. */
458 void
459 sarif_builder::end_group ()
461 m_cur_group_result = NULL;
464 /* Create a top-level object, and add it to all the results
465 (and other entities) we've seen so far.
467 Flush it all to OUTF. */
469 void
470 sarif_builder::flush_to_file (FILE *outf)
472 m_invocation_obj->prepare_to_flush (m_context);
473 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
474 top->dump (outf);
475 m_invocation_obj = NULL;
476 m_results_array = NULL;
477 fprintf (outf, "\n");
478 delete top;
481 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
482 property (SARIF v2.1.0 section 3.27.10).
484 Return NULL if there isn't one. */
486 static const char *
487 maybe_get_sarif_level (diagnostic_t diag_kind)
489 switch (diag_kind)
491 case DK_WARNING:
492 return "warning";
493 case DK_ERROR:
494 return "error";
495 case DK_NOTE:
496 case DK_ANACHRONISM:
497 return "note";
498 default:
499 return NULL;
503 /* Make a string for DIAG_KIND suitable for use a ruleId
504 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
505 have anything better to use. */
507 static char *
508 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
510 static const char *const diagnostic_kind_text[] = {
511 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
512 #include "diagnostic.def"
513 #undef DEFINE_DIAGNOSTIC_KIND
514 "must-not-happen"
516 /* Lose the trailing ": ". */
517 const char *kind_text = diagnostic_kind_text[diag_kind];
518 size_t len = strlen (kind_text);
519 gcc_assert (len > 2);
520 gcc_assert (kind_text[len - 2] == ':');
521 gcc_assert (kind_text[len - 1] == ' ');
522 char *rstrip = xstrdup (kind_text);
523 rstrip[len - 2] = '\0';
524 return rstrip;
527 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
529 sarif_result *
530 sarif_builder::make_result_object (diagnostic_context *context,
531 diagnostic_info *diagnostic,
532 diagnostic_t orig_diag_kind)
534 sarif_result *result_obj = new sarif_result ();
536 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
537 /* Ideally we'd have an option_name for these. */
538 if (char *option_text
539 = context->make_option_name (diagnostic->option_index,
540 orig_diag_kind, diagnostic->kind))
542 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
543 Set ruleId referencing them. */
544 result_obj->set_string ("ruleId", option_text);
545 if (m_rule_id_set.contains (option_text))
546 free (option_text);
547 else
549 /* This is the first time we've seen this ruleId. */
550 /* Add to set, taking ownership. */
551 m_rule_id_set.add (option_text);
553 json::object *reporting_desc_obj
554 = make_reporting_descriptor_object_for_warning (context,
555 diagnostic,
556 orig_diag_kind,
557 option_text);
558 m_rules_arr->append (reporting_desc_obj);
561 else
563 /* Otherwise, we have an "error" or a stray "note"; use the
564 diagnostic kind as the ruleId, so that the result object at least
565 has a ruleId.
566 We don't bother creating reportingDescriptor objects for these. */
567 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
568 result_obj->set_string ("ruleId", rule_id);
569 free (rule_id);
572 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
573 if (diagnostic->metadata)
574 if (int cwe_id = diagnostic->metadata->get_cwe ())
576 json::array *taxa_arr = new json::array ();
577 json::object *cwe_id_obj
578 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
579 taxa_arr->append (cwe_id_obj);
580 result_obj->set ("taxa", taxa_arr);
583 /* "level" property (SARIF v2.1.0 section 3.27.10). */
584 if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
585 result_obj->set_string ("level", sarif_level);
587 /* "message" property (SARIF v2.1.0 section 3.27.11). */
588 json::object *message_obj
589 = make_message_object (pp_formatted_text (context->printer));
590 pp_clear_output_area (context->printer);
591 result_obj->set ("message", message_obj);
593 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
594 json::array *locations_arr = make_locations_arr (diagnostic);
595 result_obj->set ("locations", locations_arr);
597 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
598 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
600 json::array *code_flows_arr = new json::array ();
601 json::object *code_flow_obj = make_code_flow_object (*path);
602 code_flows_arr->append (code_flow_obj);
603 result_obj->set ("codeFlows", code_flows_arr);
606 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
607 set up later, if any nested diagnostics occur within this diagnostic
608 group. */
610 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
611 const rich_location *richloc = diagnostic->richloc;
612 if (richloc->get_num_fixit_hints ())
614 json::array *fix_arr = new json::array ();
615 json::object *fix_obj = make_fix_object (*richloc);
616 fix_arr->append (fix_obj);
617 result_obj->set ("fixes", fix_arr);
620 return result_obj;
623 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
624 for a GCC warning. */
626 json::object *
627 sarif_builder::
628 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
629 diagnostic_info *diagnostic,
630 diagnostic_t /*orig_diag_kind*/,
631 const char *option_text)
633 json::object *reporting_desc = new json::object ();
635 /* "id" property (SARIF v2.1.0 section 3.49.3). */
636 reporting_desc->set_string ("id", option_text);
638 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
639 it seems redundant compared to "id". */
641 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
642 if (char *option_url = context->make_option_url (diagnostic->option_index))
644 reporting_desc->set_string ("helpUri", option_url);
645 free (option_url);
648 return reporting_desc;
651 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
652 for CWE_ID, for use within the CWE taxa array. */
654 json::object *
655 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
657 json::object *reporting_desc = new json::object ();
659 /* "id" property (SARIF v2.1.0 section 3.49.3). */
661 pretty_printer pp;
662 pp_printf (&pp, "%i", cwe_id);
663 reporting_desc->set_string ("id", pp_formatted_text (&pp));
666 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
668 char *url = get_cwe_url (cwe_id);
669 reporting_desc->set_string ("helpUri", url);
670 free (url);
673 return reporting_desc;
676 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
677 referencing CWE_ID, for use within a result object.
678 Also, add CWE_ID to m_cwe_id_set. */
680 json::object *
681 sarif_builder::
682 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
684 json::object *desc_ref_obj = new json::object ();
686 /* "id" property (SARIF v2.1.0 section 3.52.4). */
688 pretty_printer pp;
689 pp_printf (&pp, "%i", cwe_id);
690 desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
693 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
694 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
695 desc_ref_obj->set ("toolComponent", comp_ref_obj);
697 /* Add CWE_ID to our set. */
698 gcc_assert (cwe_id > 0);
699 m_cwe_id_set.add (cwe_id);
701 return desc_ref_obj;
704 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
705 references the CWE taxonomy. */
707 json::object *
708 sarif_builder::
709 make_tool_component_reference_object_for_cwe () const
711 json::object *comp_ref_obj = new json::object ();
713 /* "name" property (SARIF v2.1.0 section 3.54.3). */
714 comp_ref_obj->set_string ("name", "cwe");
716 return comp_ref_obj;
719 /* Make an array suitable for use as the "locations" property of:
720 - a "result" object (SARIF v2.1.0 section 3.27.12), or
721 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
723 json::array *
724 sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
726 json::array *locations_arr = new json::array ();
727 const logical_location *logical_loc = NULL;
728 if (auto client_data_hooks = m_context->get_client_data_hooks ())
729 logical_loc = client_data_hooks->get_current_logical_location ();
731 json::object *location_obj
732 = make_location_object (*diagnostic->richloc, logical_loc);
733 locations_arr->append (location_obj);
734 return locations_arr;
737 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
738 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
740 void
741 sarif_builder::
742 set_any_logical_locs_arr (json::object *location_obj,
743 const logical_location *logical_loc)
745 if (!logical_loc)
746 return;
747 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
748 json::array *location_locs_arr = new json::array ();
749 location_locs_arr->append (logical_loc_obj);
750 location_obj->set ("logicalLocations", location_locs_arr);
753 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
754 and LOGICAL_LOC. */
756 json::object *
757 sarif_builder::make_location_object (const rich_location &rich_loc,
758 const logical_location *logical_loc)
760 json::object *location_obj = new json::object ();
762 /* Get primary loc from RICH_LOC. */
763 location_t loc = rich_loc.get_loc ();
765 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
766 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
767 location_obj->set ("physicalLocation", phs_loc_obj);
769 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
770 set_any_logical_locs_arr (location_obj, logical_loc);
772 return location_obj;
775 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
776 within a diagnostic_path. */
778 json::object *
779 sarif_builder::make_location_object (const diagnostic_event &event)
781 json::object *location_obj = new json::object ();
783 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
784 location_t loc = event.get_location ();
785 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
786 location_obj->set ("physicalLocation", phs_loc_obj);
788 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
789 const logical_location *logical_loc = event.get_logical_location ();
790 set_any_logical_locs_arr (location_obj, logical_loc);
792 /* "message" property (SARIF v2.1.0 section 3.28.5). */
793 label_text ev_desc = event.get_desc (false);
794 json::object *message_obj = make_message_object (ev_desc.get ());
795 location_obj->set ("message", message_obj);
797 return location_obj;
800 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
801 or return NULL;
802 Add any filename to the m_artifacts. */
804 json::object *
805 sarif_builder::maybe_make_physical_location_object (location_t loc)
807 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
808 return NULL;
810 json::object *phys_loc_obj = new json::object ();
812 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
813 json::object *artifact_loc_obj = make_artifact_location_object (loc);
814 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
815 m_filenames.add (LOCATION_FILE (loc));
817 /* "region" property (SARIF v2.1.0 section 3.29.4). */
818 if (json::object *region_obj = maybe_make_region_object (loc))
819 phys_loc_obj->set ("region", region_obj);
821 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
822 if (json::object *context_region_obj
823 = maybe_make_region_object_for_context (loc))
824 phys_loc_obj->set ("contextRegion", context_region_obj);
826 /* Instead, we add artifacts to the run as a whole,
827 with artifact.contents.
828 Could do both, though. */
830 return phys_loc_obj;
833 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
834 or return NULL. */
836 json::object *
837 sarif_builder::make_artifact_location_object (location_t loc)
839 return make_artifact_location_object (LOCATION_FILE (loc));
842 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
843 for when we need to express paths relative to PWD. */
845 #define PWD_PROPERTY_NAME ("PWD")
847 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
848 or return NULL. */
850 json::object *
851 sarif_builder::make_artifact_location_object (const char *filename)
853 json::object *artifact_loc_obj = new json::object ();
855 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
856 artifact_loc_obj->set_string ("uri", filename);
858 if (filename[0] != '/')
860 /* If we have a relative path, set the "uriBaseId" property
861 (SARIF v2.1.0 section 3.4.4). */
862 artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
863 m_seen_any_relative_paths = true;
866 return artifact_loc_obj;
869 /* Get the PWD, or NULL, as an absolute file-based URI,
870 adding a trailing forward slash (as required by SARIF v2.1.0
871 section 3.14.14). */
873 static char *
874 make_pwd_uri_str ()
876 /* The prefix of a file-based URI, up to, but not including the path. */
877 #define FILE_PREFIX ("file://")
879 const char *pwd = getpwd ();
880 if (!pwd)
881 return NULL;
882 size_t len = strlen (pwd);
883 if (len == 0 || pwd[len - 1] != '/')
884 return concat (FILE_PREFIX, pwd, "/", NULL);
885 else
887 gcc_assert (pwd[len - 1] == '/');
888 return concat (FILE_PREFIX, pwd, NULL);
892 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
893 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
894 section 3.14.14) when we have any relative paths. */
896 json::object *
897 sarif_builder::make_artifact_location_object_for_pwd () const
899 json::object *artifact_loc_obj = new json::object ();
901 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
902 if (char *pwd = make_pwd_uri_str ())
904 gcc_assert (strlen (pwd) > 0);
905 gcc_assert (pwd[strlen (pwd) - 1] == '/');
906 artifact_loc_obj->set_string ("uri", pwd);
907 free (pwd);
910 return artifact_loc_obj;
913 /* Get the column number within EXPLOC. */
916 sarif_builder::get_sarif_column (expanded_location exploc) const
918 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
919 return location_compute_display_column (m_context->get_file_cache (),
920 exploc, policy);
923 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
924 or return NULL. */
926 json::object *
927 sarif_builder::maybe_make_region_object (location_t loc) const
929 location_t caret_loc = get_pure_location (loc);
931 if (caret_loc <= BUILTINS_LOCATION)
932 return NULL;
934 location_t start_loc = get_start (loc);
935 location_t finish_loc = get_finish (loc);
937 expanded_location exploc_caret = expand_location (caret_loc);
938 expanded_location exploc_start = expand_location (start_loc);
939 expanded_location exploc_finish = expand_location (finish_loc);
941 if (exploc_start.file !=exploc_caret.file)
942 return NULL;
943 if (exploc_finish.file !=exploc_caret.file)
944 return NULL;
946 json::object *region_obj = new json::object ();
948 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
949 region_obj->set_integer ("startLine", exploc_start.line);
951 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
952 region_obj->set_integer ("startColumn", get_sarif_column (exploc_start));
954 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
955 if (exploc_finish.line != exploc_start.line)
956 region_obj->set_integer ("endLine", exploc_finish.line);
958 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
959 This expresses the column immediately beyond the range. */
961 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
962 region_obj->set_integer ("endColumn", next_column);
965 return region_obj;
968 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
969 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
971 This is similar to maybe_make_region_object, but ignores column numbers,
972 covering the line(s) as a whole, and including a "snippet" property
973 embedding those source lines, making it easier for consumers to show
974 the pertinent source. */
976 json::object *
977 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
979 location_t caret_loc = get_pure_location (loc);
981 if (caret_loc <= BUILTINS_LOCATION)
982 return NULL;
984 location_t start_loc = get_start (loc);
985 location_t finish_loc = get_finish (loc);
987 expanded_location exploc_caret = expand_location (caret_loc);
988 expanded_location exploc_start = expand_location (start_loc);
989 expanded_location exploc_finish = expand_location (finish_loc);
991 if (exploc_start.file !=exploc_caret.file)
992 return NULL;
993 if (exploc_finish.file !=exploc_caret.file)
994 return NULL;
996 json::object *region_obj = new json::object ();
998 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
999 region_obj->set_integer ("startLine", exploc_start.line);
1001 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1002 if (exploc_finish.line != exploc_start.line)
1003 region_obj->set_integer ("endLine", exploc_finish.line);
1005 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1006 if (json::object *artifact_content_obj
1007 = maybe_make_artifact_content_object (exploc_start.file,
1008 exploc_start.line,
1009 exploc_finish.line))
1010 region_obj->set ("snippet", artifact_content_obj);
1012 return region_obj;
1015 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1016 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1018 json::object *
1019 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1021 location_t start_loc = hint.get_start_loc ();
1022 location_t next_loc = hint.get_next_loc ();
1024 expanded_location exploc_start = expand_location (start_loc);
1025 expanded_location exploc_next = expand_location (next_loc);
1027 json::object *region_obj = new json::object ();
1029 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1030 region_obj->set_integer ("startLine", exploc_start.line);
1032 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1033 int start_col = get_sarif_column (exploc_start);
1034 region_obj->set_integer ("startColumn", start_col);
1036 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1037 if (exploc_next.line != exploc_start.line)
1038 region_obj->set_integer ("endLine", exploc_next.line);
1040 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1041 This expresses the column immediately beyond the range. */
1042 int next_col = get_sarif_column (exploc_next);
1043 region_obj->set_integer ("endColumn", next_col);
1045 return region_obj;
1048 /* Attempt to get a string for a logicalLocation's "kind" property
1049 (SARIF v2.1.0 section 3.33.7).
1050 Return NULL if unknown. */
1052 static const char *
1053 maybe_get_sarif_kind (enum logical_location_kind kind)
1055 switch (kind)
1057 default:
1058 gcc_unreachable ();
1059 case LOGICAL_LOCATION_KIND_UNKNOWN:
1060 return NULL;
1062 case LOGICAL_LOCATION_KIND_FUNCTION:
1063 return "function";
1064 case LOGICAL_LOCATION_KIND_MEMBER:
1065 return "member";
1066 case LOGICAL_LOCATION_KIND_MODULE:
1067 return "module";
1068 case LOGICAL_LOCATION_KIND_NAMESPACE:
1069 return "namespace";
1070 case LOGICAL_LOCATION_KIND_TYPE:
1071 return "type";
1072 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1073 return "returnType";
1074 case LOGICAL_LOCATION_KIND_PARAMETER:
1075 return "parameter";
1076 case LOGICAL_LOCATION_KIND_VARIABLE:
1077 return "variable";
1081 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1082 or return NULL. */
1084 json::object *
1085 sarif_builder::
1086 make_logical_location_object (const logical_location &logical_loc) const
1088 json::object *logical_loc_obj = new json::object ();
1090 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1091 if (const char *short_name = logical_loc.get_short_name ())
1092 logical_loc_obj->set_string ("name", short_name);
1094 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1095 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1096 logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope);
1098 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1099 if (const char *internal_name = logical_loc.get_internal_name ())
1100 logical_loc_obj->set_string ("decoratedName", internal_name);
1102 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1103 enum logical_location_kind kind = logical_loc.get_kind ();
1104 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1105 logical_loc_obj->set_string ("kind", sarif_kind_str);
1107 return logical_loc_obj;
1110 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1112 json::object *
1113 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1115 json::object *code_flow_obj = new json::object ();
1117 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1118 json::array *thread_flows_arr = new json::array ();
1120 /* Walk the events, consolidating into per-thread threadFlow objects,
1121 using the index with PATH as the overall executionOrder. */
1122 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1123 sarif_thread_flow *> thread_id_map;
1124 for (unsigned i = 0; i < path.num_events (); i++)
1126 const diagnostic_event &event = path.get_event (i);
1127 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1128 sarif_thread_flow *thread_flow_obj;
1130 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1131 thread_flow_obj = *slot;
1132 else
1134 const diagnostic_thread &thread = path.get_thread (thread_id);
1135 thread_flow_obj = new sarif_thread_flow (thread);
1136 thread_flows_arr->append (thread_flow_obj);
1137 thread_id_map.put (thread_id, thread_flow_obj);
1140 /* Add event to thread's threadFlow object. */
1141 json::object *thread_flow_loc_obj
1142 = make_thread_flow_location_object (event, i);
1143 thread_flow_obj->add_location (thread_flow_loc_obj);
1145 code_flow_obj->set ("threadFlows", thread_flows_arr);
1147 return code_flow_obj;
1150 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1152 json::object *
1153 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1154 int path_event_idx)
1156 json::object *thread_flow_loc_obj = new json::object ();
1158 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1159 json::object *location_obj = make_location_object (ev);
1160 thread_flow_loc_obj->set ("location", location_obj);
1162 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1163 diagnostic_event::meaning m = ev.get_meaning ();
1164 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1165 thread_flow_loc_obj->set ("kinds", kinds_arr);
1167 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1168 thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
1170 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1171 Offset by 1 to match the human-readable values emitted by %@. */
1172 thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
1174 /* It might be nice to eventually implement the following for -fanalyzer:
1175 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1176 - the "state" property (SARIF v2.1.0 section 3.38.9)
1177 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1179 return thread_flow_loc_obj;
1182 /* If M has any known meaning, make a json array suitable for the "kinds"
1183 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1185 Otherwise, return NULL. */
1187 json::array *
1188 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1190 if (m.m_verb == diagnostic_event::VERB_unknown
1191 && m.m_noun == diagnostic_event::NOUN_unknown
1192 && m.m_property == diagnostic_event::PROPERTY_unknown)
1193 return NULL;
1195 json::array *kinds_arr = new json::array ();
1196 if (const char *verb_str
1197 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1198 kinds_arr->append (new json::string (verb_str));
1199 if (const char *noun_str
1200 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1201 kinds_arr->append (new json::string (noun_str));
1202 if (const char *property_str
1203 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1204 kinds_arr->append (new json::string (property_str));
1205 return kinds_arr;
1208 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1210 json::object *
1211 sarif_builder::make_message_object (const char *msg) const
1213 json::object *message_obj = new json::object ();
1215 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1216 message_obj->set_string ("text", msg);
1218 return message_obj;
1221 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1222 We emit the diagram as a code block within the Markdown part
1223 of the message. */
1225 json::object *
1226 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1227 const diagnostic_diagram &diagram)
1229 json::object *message_obj = new json::object ();
1231 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1232 message_obj->set_string ("text", diagram.get_alt_text ());
1234 char *saved_prefix = pp_take_prefix (context->printer);
1235 pp_set_prefix (context->printer, NULL);
1237 /* "To produce a code block in Markdown, simply indent every line of
1238 the block by at least 4 spaces or 1 tab."
1239 Here we use 4 spaces. */
1240 diagram.get_canvas ().print_to_pp (context->printer, " ");
1241 pp_set_prefix (context->printer, saved_prefix);
1243 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1244 message_obj->set_string ("markdown", pp_formatted_text (context->printer));
1246 pp_clear_output_area (context->printer);
1248 return message_obj;
1251 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1252 for MSG. */
1254 json::object *
1255 sarif_builder::make_multiformat_message_string (const char *msg) const
1257 json::object *message_obj = new json::object ();
1259 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1260 message_obj->set_string ("text", msg);
1262 return message_obj;
1265 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1266 #define SARIF_VERSION "2.1.0"
1268 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1269 Take ownership of INVOCATION_OBJ and RESULTS. */
1271 json::object *
1272 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1273 json::array *results)
1275 json::object *log_obj = new json::object ();
1277 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1278 log_obj->set_string ("$schema", SARIF_SCHEMA);
1280 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1281 log_obj->set_string ("version", SARIF_VERSION);
1283 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1284 json::array *run_arr = new json::array ();
1285 json::object *run_obj = make_run_object (invocation_obj, results);
1286 run_arr->append (run_obj);
1287 log_obj->set ("runs", run_arr);
1289 return log_obj;
1292 /* Make a run object (SARIF v2.1.0 section 3.14).
1293 Take ownership of INVOCATION_OBJ and RESULTS. */
1295 json::object *
1296 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1297 json::array *results)
1299 json::object *run_obj = new json::object ();
1301 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1302 json::object *tool_obj = make_tool_object ();
1303 run_obj->set ("tool", tool_obj);
1305 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1306 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1307 run_obj->set ("taxonomies", taxonomies_arr);
1309 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1311 json::array *invocations_arr = new json::array ();
1312 invocations_arr->append (invocation_obj);
1313 run_obj->set ("invocations", invocations_arr);
1316 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1317 if (m_seen_any_relative_paths)
1319 json::object *orig_uri_base_ids = new json::object ();
1320 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1321 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1322 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1325 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1326 json::array *artifacts_arr = new json::array ();
1327 for (auto iter : m_filenames)
1329 json::object *artifact_obj = make_artifact_object (iter);
1330 artifacts_arr->append (artifact_obj);
1332 run_obj->set ("artifacts", artifacts_arr);
1334 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1335 run_obj->set ("results", results);
1337 return run_obj;
1340 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1342 json::object *
1343 sarif_builder::make_tool_object () const
1345 json::object *tool_obj = new json::object ();
1347 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1348 json::object *driver_obj = make_driver_tool_component_object ();
1349 tool_obj->set ("driver", driver_obj);
1351 /* Report plugins via the "extensions" property
1352 (SARIF v2.1.0 section 3.18.3). */
1353 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1354 if (const client_version_info *vinfo
1355 = client_data_hooks->get_any_version_info ())
1357 class my_plugin_visitor : public client_version_info :: plugin_visitor
1359 public:
1360 void on_plugin (const diagnostic_client_plugin_info &p) final override
1362 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1363 for the plugin. */
1364 json::object *plugin_obj = new json::object ();
1365 m_plugin_objs.safe_push (plugin_obj);
1367 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1368 if (const char *short_name = p.get_short_name ())
1369 plugin_obj->set_string ("name", short_name);
1371 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1372 if (const char *full_name = p.get_full_name ())
1373 plugin_obj->set_string ("fullName", full_name);
1375 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1376 if (const char *version = p.get_version ())
1377 plugin_obj->set_string ("version", version);
1379 auto_vec <json::object *> m_plugin_objs;
1381 my_plugin_visitor v;
1382 vinfo->for_each_plugin (v);
1383 if (v.m_plugin_objs.length () > 0)
1385 json::array *extensions_arr = new json::array ();
1386 tool_obj->set ("extensions", extensions_arr);
1387 for (auto iter : v.m_plugin_objs)
1388 extensions_arr->append (iter);
1392 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1393 "extensions" (see toplev.cc: print_version). */
1395 return tool_obj;
1398 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1399 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1401 json::object *
1402 sarif_builder::make_driver_tool_component_object () const
1404 json::object *driver_obj = new json::object ();
1406 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1407 if (const client_version_info *vinfo
1408 = client_data_hooks->get_any_version_info ())
1410 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1411 if (const char *name = vinfo->get_tool_name ())
1412 driver_obj->set_string ("name", name);
1414 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1415 if (char *full_name = vinfo->maybe_make_full_name ())
1417 driver_obj->set_string ("fullName", full_name);
1418 free (full_name);
1421 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1422 if (const char *version = vinfo->get_version_string ())
1423 driver_obj->set_string ("version", version);
1425 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1426 if (char *version_url = vinfo->maybe_make_version_url ())
1428 driver_obj->set_string ("informationUri", version_url);
1429 free (version_url);
1433 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1434 driver_obj->set ("rules", m_rules_arr);
1436 return driver_obj;
1439 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1440 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1441 toolComponent (3.19) as per 3.19.3, representing the CWE.
1443 Otherwise return NULL. */
1445 json::array *
1446 sarif_builder::maybe_make_taxonomies_array () const
1448 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1449 if (!cwe_obj)
1450 return NULL;
1452 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1453 json::array *taxonomies_arr = new json::array ();
1454 taxonomies_arr->append (cwe_obj);
1455 return taxonomies_arr;
1458 /* If we've seen any CWE IDs, make a toolComponent object
1459 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1460 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1462 Otherwise return NULL. */
1464 json::object *
1465 sarif_builder::maybe_make_cwe_taxonomy_object () const
1467 if (m_cwe_id_set.is_empty ())
1468 return NULL;
1470 json::object *taxonomy_obj = new json::object ();
1472 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1473 taxonomy_obj->set_string ("name", "CWE");
1475 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1476 taxonomy_obj->set_string ("version", "4.7");
1478 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1479 taxonomy_obj->set_string ("organization", "MITRE");
1481 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1482 json::object *short_desc
1483 = make_multiformat_message_string ("The MITRE"
1484 " Common Weakness Enumeration");
1485 taxonomy_obj->set ("shortDescription", short_desc);
1487 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1488 json::array *taxa_arr = new json::array ();
1489 for (auto cwe_id : m_cwe_id_set)
1491 json::object *cwe_taxon
1492 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1493 taxa_arr->append (cwe_taxon);
1495 taxonomy_obj->set ("taxa", taxa_arr);
1497 return taxonomy_obj;
1500 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1502 json::object *
1503 sarif_builder::make_artifact_object (const char *filename)
1505 json::object *artifact_obj = new json::object ();
1507 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1508 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1509 artifact_obj->set ("location", artifact_loc_obj);
1511 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1512 if (json::object *artifact_content_obj
1513 = maybe_make_artifact_content_object (filename))
1514 artifact_obj->set ("contents", artifact_content_obj);
1516 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1517 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1518 if (const char *source_lang
1519 = client_data_hooks->maybe_get_sarif_source_language (filename))
1520 artifact_obj->set_string ("sourceLanguage", source_lang);
1522 return artifact_obj;
1525 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1526 full contents of FILENAME. */
1528 json::object *
1529 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1531 /* Let input.cc handle any charset conversion. */
1532 char_span utf8_content
1533 = m_context->get_file_cache ().get_source_file_content (filename);
1534 if (!utf8_content)
1535 return NULL;
1537 /* Don't add it if it's not valid UTF-8. */
1538 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1539 return NULL;
1541 json::object *artifact_content_obj = new json::object ();
1542 artifact_content_obj->set ("text",
1543 new json::string (utf8_content.get_buffer (),
1544 utf8_content.length ()));
1545 return artifact_content_obj;
1548 /* Attempt to read the given range of lines from FILENAME; return
1549 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1551 char *
1552 sarif_builder::get_source_lines (const char *filename,
1553 int start_line,
1554 int end_line) const
1556 auto_vec<char> result;
1558 for (int line = start_line; line <= end_line; line++)
1560 char_span line_content
1561 = m_context->get_file_cache ().get_source_line (filename, line);
1562 if (!line_content.get_buffer ())
1563 return NULL;
1564 result.reserve (line_content.length () + 1);
1565 for (size_t i = 0; i < line_content.length (); i++)
1566 result.quick_push (line_content[i]);
1567 result.quick_push ('\n');
1569 result.safe_push ('\0');
1571 return xstrdup (result.address ());
1574 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1575 run of lines within FILENAME (including the endpoints). */
1577 json::object *
1578 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1579 int start_line,
1580 int end_line) const
1582 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1584 if (!text_utf8)
1585 return NULL;
1587 /* Don't add it if it's not valid UTF-8. */
1588 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1590 free (text_utf8);
1591 return NULL;
1594 json::object *artifact_content_obj = new json::object ();
1595 artifact_content_obj->set_string ("text", text_utf8);
1596 free (text_utf8);
1598 return artifact_content_obj;
1601 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1603 json::object *
1604 sarif_builder::make_fix_object (const rich_location &richloc)
1606 json::object *fix_obj = new json::object ();
1608 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1609 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1610 json::array *artifact_change_arr = new json::array ();
1611 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1612 artifact_change_arr->append (artifact_change_obj);
1613 fix_obj->set ("artifactChanges", artifact_change_arr);
1615 return fix_obj;
1618 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1620 json::object *
1621 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1623 json::object *artifact_change_obj = new json::object ();
1625 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1626 json::object *artifact_location_obj
1627 = make_artifact_location_object (richloc.get_loc ());
1628 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1630 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1631 json::array *replacement_arr = new json::array ();
1632 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1634 const fixit_hint *hint = richloc.get_fixit_hint (i);
1635 json::object *replacement_obj = make_replacement_object (*hint);
1636 replacement_arr->append (replacement_obj);
1638 artifact_change_obj->set ("replacements", replacement_arr);
1640 return artifact_change_obj;
1643 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1645 json::object *
1646 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1648 json::object *replacement_obj = new json::object ();
1650 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1651 json::object *region_obj = make_region_object_for_hint (hint);
1652 replacement_obj->set ("deletedRegion", region_obj);
1654 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1655 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1656 replacement_obj->set ("insertedContent", content_obj);
1658 return replacement_obj;
1661 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1663 json::object *
1664 sarif_builder::make_artifact_content_object (const char *text) const
1666 json::object *content_obj = new json::object ();
1668 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1669 content_obj->set_string ("text", text);
1671 return content_obj;
1674 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1675 occurs. */
1677 static void
1678 sarif_ice_handler (diagnostic_context *context)
1680 /* Attempt to ensure that a .sarif file is written out. */
1681 diagnostic_finish (context);
1683 /* Print a header for the remaining output to stderr, and
1684 return, attempting to print the usual ICE messages to
1685 stderr. Hopefully this will be helpful to the user in
1686 indicating what's gone wrong (also for DejaGnu, for pruning
1687 those messages). */
1688 fnotice (stderr, "Internal compiler error:\n");
1691 class sarif_output_format : public diagnostic_output_format
1693 public:
1694 void on_begin_group () final override
1696 /* No-op, */
1698 void on_end_group () final override
1700 m_builder.end_group ();
1702 void
1703 on_begin_diagnostic (diagnostic_info *) final override
1705 /* No-op, */
1707 void
1708 on_end_diagnostic (diagnostic_info *diagnostic,
1709 diagnostic_t orig_diag_kind) final override
1711 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1713 void on_diagram (const diagnostic_diagram &diagram) final override
1715 m_builder.emit_diagram (&m_context, diagram);
1718 protected:
1719 sarif_output_format (diagnostic_context &context)
1720 : diagnostic_output_format (context),
1721 m_builder (&context)
1724 sarif_builder m_builder;
1727 class sarif_stream_output_format : public sarif_output_format
1729 public:
1730 sarif_stream_output_format (diagnostic_context &context, FILE *stream)
1731 : sarif_output_format (context),
1732 m_stream (stream)
1735 ~sarif_stream_output_format ()
1737 m_builder.flush_to_file (m_stream);
1739 private:
1740 FILE *m_stream;
1743 class sarif_file_output_format : public sarif_output_format
1745 public:
1746 sarif_file_output_format (diagnostic_context &context,
1747 const char *base_file_name)
1748 : sarif_output_format (context),
1749 m_base_file_name (xstrdup (base_file_name))
1752 ~sarif_file_output_format ()
1754 char *filename = concat (m_base_file_name, ".sarif", NULL);
1755 free (m_base_file_name);
1756 m_base_file_name = nullptr;
1757 FILE *outf = fopen (filename, "w");
1758 if (!outf)
1760 const char *errstr = xstrerror (errno);
1761 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1762 filename, errstr);
1763 free (filename);
1764 return;
1766 m_builder.flush_to_file (outf);
1767 fclose (outf);
1768 free (filename);
1771 private:
1772 char *m_base_file_name;
1775 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1776 to a file). */
1778 static void
1779 diagnostic_output_format_init_sarif (diagnostic_context *context)
1781 /* Override callbacks. */
1782 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1783 context->set_ice_handler_callback (sarif_ice_handler);
1785 /* The metadata is handled in SARIF format, rather than as text. */
1786 context->set_show_cwe (false);
1787 context->set_show_rules (false);
1789 /* The option is handled in SARIF format, rather than as text. */
1790 context->set_show_option_requested (false);
1792 /* Don't colorize the text. */
1793 pp_show_color (context->printer) = false;
1796 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1798 void
1799 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1801 diagnostic_output_format_init_sarif (context);
1802 context->set_output_format (new sarif_stream_output_format (*context,
1803 stderr));
1806 /* Populate CONTEXT in preparation for SARIF output to a file named
1807 BASE_FILE_NAME.sarif. */
1809 void
1810 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1811 const char *base_file_name)
1813 diagnostic_output_format_init_sarif (context);
1814 context->set_output_format (new sarif_file_output_format (*context,
1815 base_file_name));
1818 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1820 void
1821 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1822 FILE *stream)
1824 diagnostic_output_format_init_sarif (context);
1825 context->set_output_format (new sarif_stream_output_format (*context,
1826 stream));