MATCH: Improve `A CMP 0 ? A : -A` set of patterns to use bitwise_equal_p.
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blobd8cca21d834e473835f4ebd54337e3ea7b4ca5fe
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 ("executionSuccessful", new json::literal (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 (context->m_client_data_hooks)
300 context->m_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 ("level", new json::string ("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 ("id", new json::string (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->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->option_name (context, 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 ("ruleId", new json::string (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 ("ruleId", new json::string (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 ("level", new json::string (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 ("id", new json::string (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 (context->get_option_url)
644 char *option_url
645 = context->get_option_url (context, diagnostic->option_index);
646 if (option_url)
648 reporting_desc->set ("helpUri", new json::string (option_url));
649 free (option_url);
653 return reporting_desc;
656 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
657 for CWE_ID, for use within the CWE taxa array. */
659 json::object *
660 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
662 json::object *reporting_desc = new json::object ();
664 /* "id" property (SARIF v2.1.0 section 3.49.3). */
666 pretty_printer pp;
667 pp_printf (&pp, "%i", cwe_id);
668 reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
671 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
673 char *url = get_cwe_url (cwe_id);
674 reporting_desc->set ("helpUri", new json::string (url));
675 free (url);
678 return reporting_desc;
681 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
682 referencing CWE_ID, for use within a result object.
683 Also, add CWE_ID to m_cwe_id_set. */
685 json::object *
686 sarif_builder::
687 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
689 json::object *desc_ref_obj = new json::object ();
691 /* "id" property (SARIF v2.1.0 section 3.52.4). */
693 pretty_printer pp;
694 pp_printf (&pp, "%i", cwe_id);
695 desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
698 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
699 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
700 desc_ref_obj->set ("toolComponent", comp_ref_obj);
702 /* Add CWE_ID to our set. */
703 gcc_assert (cwe_id > 0);
704 m_cwe_id_set.add (cwe_id);
706 return desc_ref_obj;
709 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
710 references the CWE taxonomy. */
712 json::object *
713 sarif_builder::
714 make_tool_component_reference_object_for_cwe () const
716 json::object *comp_ref_obj = new json::object ();
718 /* "name" property (SARIF v2.1.0 section 3.54.3). */
719 comp_ref_obj->set ("name", new json::string ("cwe"));
721 return comp_ref_obj;
724 /* Make an array suitable for use as the "locations" property of:
725 - a "result" object (SARIF v2.1.0 section 3.27.12), or
726 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
728 json::array *
729 sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
731 json::array *locations_arr = new json::array ();
732 const logical_location *logical_loc = NULL;
733 if (m_context->m_client_data_hooks)
734 logical_loc
735 = m_context->m_client_data_hooks->get_current_logical_location ();
737 json::object *location_obj
738 = make_location_object (*diagnostic->richloc, logical_loc);
739 locations_arr->append (location_obj);
740 return locations_arr;
743 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
744 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
746 void
747 sarif_builder::
748 set_any_logical_locs_arr (json::object *location_obj,
749 const logical_location *logical_loc)
751 if (!logical_loc)
752 return;
753 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
754 json::array *location_locs_arr = new json::array ();
755 location_locs_arr->append (logical_loc_obj);
756 location_obj->set ("logicalLocations", location_locs_arr);
759 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
760 and LOGICAL_LOC. */
762 json::object *
763 sarif_builder::make_location_object (const rich_location &rich_loc,
764 const logical_location *logical_loc)
766 json::object *location_obj = new json::object ();
768 /* Get primary loc from RICH_LOC. */
769 location_t loc = rich_loc.get_loc ();
771 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
772 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
773 location_obj->set ("physicalLocation", phs_loc_obj);
775 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
776 set_any_logical_locs_arr (location_obj, logical_loc);
778 return location_obj;
781 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
782 within a diagnostic_path. */
784 json::object *
785 sarif_builder::make_location_object (const diagnostic_event &event)
787 json::object *location_obj = new json::object ();
789 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
790 location_t loc = event.get_location ();
791 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
792 location_obj->set ("physicalLocation", phs_loc_obj);
794 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
795 const logical_location *logical_loc = event.get_logical_location ();
796 set_any_logical_locs_arr (location_obj, logical_loc);
798 /* "message" property (SARIF v2.1.0 section 3.28.5). */
799 label_text ev_desc = event.get_desc (false);
800 json::object *message_obj = make_message_object (ev_desc.get ());
801 location_obj->set ("message", message_obj);
803 return location_obj;
806 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
807 or return NULL;
808 Add any filename to the m_artifacts. */
810 json::object *
811 sarif_builder::maybe_make_physical_location_object (location_t loc)
813 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
814 return NULL;
816 json::object *phys_loc_obj = new json::object ();
818 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
819 json::object *artifact_loc_obj = make_artifact_location_object (loc);
820 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
821 m_filenames.add (LOCATION_FILE (loc));
823 /* "region" property (SARIF v2.1.0 section 3.29.4). */
824 if (json::object *region_obj = maybe_make_region_object (loc))
825 phys_loc_obj->set ("region", region_obj);
827 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
828 if (json::object *context_region_obj
829 = maybe_make_region_object_for_context (loc))
830 phys_loc_obj->set ("contextRegion", context_region_obj);
832 /* Instead, we add artifacts to the run as a whole,
833 with artifact.contents.
834 Could do both, though. */
836 return phys_loc_obj;
839 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
840 or return NULL. */
842 json::object *
843 sarif_builder::make_artifact_location_object (location_t loc)
845 return make_artifact_location_object (LOCATION_FILE (loc));
848 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
849 for when we need to express paths relative to PWD. */
851 #define PWD_PROPERTY_NAME ("PWD")
853 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
854 or return NULL. */
856 json::object *
857 sarif_builder::make_artifact_location_object (const char *filename)
859 json::object *artifact_loc_obj = new json::object ();
861 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
862 artifact_loc_obj->set ("uri", new json::string (filename));
864 if (filename[0] != '/')
866 /* If we have a relative path, set the "uriBaseId" property
867 (SARIF v2.1.0 section 3.4.4). */
868 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
869 m_seen_any_relative_paths = true;
872 return artifact_loc_obj;
875 /* Get the PWD, or NULL, as an absolute file-based URI,
876 adding a trailing forward slash (as required by SARIF v2.1.0
877 section 3.14.14). */
879 static char *
880 make_pwd_uri_str ()
882 /* The prefix of a file-based URI, up to, but not including the path. */
883 #define FILE_PREFIX ("file://")
885 const char *pwd = getpwd ();
886 if (!pwd)
887 return NULL;
888 size_t len = strlen (pwd);
889 if (len == 0 || pwd[len - 1] != '/')
890 return concat (FILE_PREFIX, pwd, "/", NULL);
891 else
893 gcc_assert (pwd[len - 1] == '/');
894 return concat (FILE_PREFIX, pwd, NULL);
898 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
899 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
900 section 3.14.14) when we have any relative paths. */
902 json::object *
903 sarif_builder::make_artifact_location_object_for_pwd () const
905 json::object *artifact_loc_obj = new json::object ();
907 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
908 if (char *pwd = make_pwd_uri_str ())
910 gcc_assert (strlen (pwd) > 0);
911 gcc_assert (pwd[strlen (pwd) - 1] == '/');
912 artifact_loc_obj->set ("uri", new json::string (pwd));
913 free (pwd);
916 return artifact_loc_obj;
919 /* Get the column number within EXPLOC. */
922 sarif_builder::get_sarif_column (expanded_location exploc) const
924 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
925 return location_compute_display_column (exploc, policy);
928 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
929 or return NULL. */
931 json::object *
932 sarif_builder::maybe_make_region_object (location_t loc) const
934 location_t caret_loc = get_pure_location (loc);
936 if (caret_loc <= BUILTINS_LOCATION)
937 return NULL;
939 location_t start_loc = get_start (loc);
940 location_t finish_loc = get_finish (loc);
942 expanded_location exploc_caret = expand_location (caret_loc);
943 expanded_location exploc_start = expand_location (start_loc);
944 expanded_location exploc_finish = expand_location (finish_loc);
946 if (exploc_start.file !=exploc_caret.file)
947 return NULL;
948 if (exploc_finish.file !=exploc_caret.file)
949 return NULL;
951 json::object *region_obj = new json::object ();
953 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
954 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
956 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
957 region_obj->set ("startColumn",
958 new json::integer_number (get_sarif_column (exploc_start)));
960 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
961 if (exploc_finish.line != exploc_start.line)
962 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
964 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
965 This expresses the column immediately beyond the range. */
967 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
968 region_obj->set ("endColumn", new json::integer_number (next_column));
971 return region_obj;
974 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
975 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
977 This is similar to maybe_make_region_object, but ignores column numbers,
978 covering the line(s) as a whole, and including a "snippet" property
979 embedding those source lines, making it easier for consumers to show
980 the pertinent source. */
982 json::object *
983 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
985 location_t caret_loc = get_pure_location (loc);
987 if (caret_loc <= BUILTINS_LOCATION)
988 return NULL;
990 location_t start_loc = get_start (loc);
991 location_t finish_loc = get_finish (loc);
993 expanded_location exploc_caret = expand_location (caret_loc);
994 expanded_location exploc_start = expand_location (start_loc);
995 expanded_location exploc_finish = expand_location (finish_loc);
997 if (exploc_start.file !=exploc_caret.file)
998 return NULL;
999 if (exploc_finish.file !=exploc_caret.file)
1000 return NULL;
1002 json::object *region_obj = new json::object ();
1004 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1005 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
1007 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1008 if (exploc_finish.line != exploc_start.line)
1009 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
1011 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1012 if (json::object *artifact_content_obj
1013 = maybe_make_artifact_content_object (exploc_start.file,
1014 exploc_start.line,
1015 exploc_finish.line))
1016 region_obj->set ("snippet", artifact_content_obj);
1018 return region_obj;
1021 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1022 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1024 json::object *
1025 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1027 location_t start_loc = hint.get_start_loc ();
1028 location_t next_loc = hint.get_next_loc ();
1030 expanded_location exploc_start = expand_location (start_loc);
1031 expanded_location exploc_next = expand_location (next_loc);
1033 json::object *region_obj = new json::object ();
1035 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1036 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
1038 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1039 int start_col = get_sarif_column (exploc_start);
1040 region_obj->set ("startColumn",
1041 new json::integer_number (start_col));
1043 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1044 if (exploc_next.line != exploc_start.line)
1045 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
1047 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1048 This expresses the column immediately beyond the range. */
1049 int next_col = get_sarif_column (exploc_next);
1050 region_obj->set ("endColumn", new json::integer_number (next_col));
1052 return region_obj;
1055 /* Attempt to get a string for a logicalLocation's "kind" property
1056 (SARIF v2.1.0 section 3.33.7).
1057 Return NULL if unknown. */
1059 static const char *
1060 maybe_get_sarif_kind (enum logical_location_kind kind)
1062 switch (kind)
1064 default:
1065 gcc_unreachable ();
1066 case LOGICAL_LOCATION_KIND_UNKNOWN:
1067 return NULL;
1069 case LOGICAL_LOCATION_KIND_FUNCTION:
1070 return "function";
1071 case LOGICAL_LOCATION_KIND_MEMBER:
1072 return "member";
1073 case LOGICAL_LOCATION_KIND_MODULE:
1074 return "module";
1075 case LOGICAL_LOCATION_KIND_NAMESPACE:
1076 return "namespace";
1077 case LOGICAL_LOCATION_KIND_TYPE:
1078 return "type";
1079 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1080 return "returnType";
1081 case LOGICAL_LOCATION_KIND_PARAMETER:
1082 return "parameter";
1083 case LOGICAL_LOCATION_KIND_VARIABLE:
1084 return "variable";
1088 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1089 or return NULL. */
1091 json::object *
1092 sarif_builder::
1093 make_logical_location_object (const logical_location &logical_loc) const
1095 json::object *logical_loc_obj = new json::object ();
1097 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1098 if (const char *short_name = logical_loc.get_short_name ())
1099 logical_loc_obj->set ("name", new json::string (short_name));
1101 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1102 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1103 logical_loc_obj->set ("fullyQualifiedName",
1104 new json::string (name_with_scope));
1106 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1107 if (const char *internal_name = logical_loc.get_internal_name ())
1108 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
1110 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1111 enum logical_location_kind kind = logical_loc.get_kind ();
1112 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1113 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
1115 return logical_loc_obj;
1118 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1120 json::object *
1121 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1123 json::object *code_flow_obj = new json::object ();
1125 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1126 json::array *thread_flows_arr = new json::array ();
1128 /* Walk the events, consolidating into per-thread threadFlow objects,
1129 using the index with PATH as the overall executionOrder. */
1130 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1131 sarif_thread_flow *> thread_id_map;
1132 for (unsigned i = 0; i < path.num_events (); i++)
1134 const diagnostic_event &event = path.get_event (i);
1135 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1136 sarif_thread_flow *thread_flow_obj;
1138 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1139 thread_flow_obj = *slot;
1140 else
1142 const diagnostic_thread &thread = path.get_thread (thread_id);
1143 thread_flow_obj = new sarif_thread_flow (thread);
1144 thread_flows_arr->append (thread_flow_obj);
1145 thread_id_map.put (thread_id, thread_flow_obj);
1148 /* Add event to thread's threadFlow object. */
1149 json::object *thread_flow_loc_obj
1150 = make_thread_flow_location_object (event, i);
1151 thread_flow_obj->add_location (thread_flow_loc_obj);
1153 code_flow_obj->set ("threadFlows", thread_flows_arr);
1155 return code_flow_obj;
1158 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1160 json::object *
1161 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1162 int path_event_idx)
1164 json::object *thread_flow_loc_obj = new json::object ();
1166 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1167 json::object *location_obj = make_location_object (ev);
1168 thread_flow_loc_obj->set ("location", location_obj);
1170 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1171 diagnostic_event::meaning m = ev.get_meaning ();
1172 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1173 thread_flow_loc_obj->set ("kinds", kinds_arr);
1175 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1176 thread_flow_loc_obj->set ("nestingLevel",
1177 new json::integer_number (ev.get_stack_depth ()));
1179 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1180 Offset by 1 to match the human-readable values emitted by %@. */
1181 thread_flow_loc_obj->set ("executionOrder",
1182 new json::integer_number (path_event_idx + 1));
1184 /* It might be nice to eventually implement the following for -fanalyzer:
1185 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1186 - the "state" property (SARIF v2.1.0 section 3.38.9)
1187 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1189 return thread_flow_loc_obj;
1192 /* If M has any known meaning, make a json array suitable for the "kinds"
1193 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1195 Otherwise, return NULL. */
1197 json::array *
1198 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1200 if (m.m_verb == diagnostic_event::VERB_unknown
1201 && m.m_noun == diagnostic_event::NOUN_unknown
1202 && m.m_property == diagnostic_event::PROPERTY_unknown)
1203 return NULL;
1205 json::array *kinds_arr = new json::array ();
1206 if (const char *verb_str
1207 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1208 kinds_arr->append (new json::string (verb_str));
1209 if (const char *noun_str
1210 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1211 kinds_arr->append (new json::string (noun_str));
1212 if (const char *property_str
1213 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1214 kinds_arr->append (new json::string (property_str));
1215 return kinds_arr;
1218 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1220 json::object *
1221 sarif_builder::make_message_object (const char *msg) const
1223 json::object *message_obj = new json::object ();
1225 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1226 message_obj->set ("text", new json::string (msg));
1228 return message_obj;
1231 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1232 We emit the diagram as a code block within the Markdown part
1233 of the message. */
1235 json::object *
1236 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1237 const diagnostic_diagram &diagram)
1239 json::object *message_obj = new json::object ();
1241 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1242 message_obj->set ("text", new json::string (diagram.get_alt_text ()));
1244 char *saved_prefix = pp_take_prefix (context->printer);
1245 pp_set_prefix (context->printer, NULL);
1247 /* "To produce a code block in Markdown, simply indent every line of
1248 the block by at least 4 spaces or 1 tab."
1249 Here we use 4 spaces. */
1250 diagram.get_canvas ().print_to_pp (context->printer, " ");
1251 pp_set_prefix (context->printer, saved_prefix);
1253 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1254 message_obj->set ("markdown",
1255 new json::string (pp_formatted_text (context->printer)));
1257 pp_clear_output_area (context->printer);
1259 return message_obj;
1262 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1263 for MSG. */
1265 json::object *
1266 sarif_builder::make_multiformat_message_string (const char *msg) const
1268 json::object *message_obj = new json::object ();
1270 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1271 message_obj->set ("text", new json::string (msg));
1273 return message_obj;
1276 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1277 #define SARIF_VERSION "2.1.0"
1279 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1280 Take ownership of INVOCATION_OBJ and RESULTS. */
1282 json::object *
1283 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1284 json::array *results)
1286 json::object *log_obj = new json::object ();
1288 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1289 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1291 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1292 log_obj->set ("version", new json::string (SARIF_VERSION));
1294 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1295 json::array *run_arr = new json::array ();
1296 json::object *run_obj = make_run_object (invocation_obj, results);
1297 run_arr->append (run_obj);
1298 log_obj->set ("runs", run_arr);
1300 return log_obj;
1303 /* Make a run object (SARIF v2.1.0 section 3.14).
1304 Take ownership of INVOCATION_OBJ and RESULTS. */
1306 json::object *
1307 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1308 json::array *results)
1310 json::object *run_obj = new json::object ();
1312 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1313 json::object *tool_obj = make_tool_object ();
1314 run_obj->set ("tool", tool_obj);
1316 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1317 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1318 run_obj->set ("taxonomies", taxonomies_arr);
1320 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1322 json::array *invocations_arr = new json::array ();
1323 invocations_arr->append (invocation_obj);
1324 run_obj->set ("invocations", invocations_arr);
1327 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1328 if (m_seen_any_relative_paths)
1330 json::object *orig_uri_base_ids = new json::object ();
1331 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1332 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1333 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1336 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1337 json::array *artifacts_arr = new json::array ();
1338 for (auto iter : m_filenames)
1340 json::object *artifact_obj = make_artifact_object (iter);
1341 artifacts_arr->append (artifact_obj);
1343 run_obj->set ("artifacts", artifacts_arr);
1345 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1346 run_obj->set ("results", results);
1348 return run_obj;
1351 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1353 json::object *
1354 sarif_builder::make_tool_object () const
1356 json::object *tool_obj = new json::object ();
1358 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1359 json::object *driver_obj = make_driver_tool_component_object ();
1360 tool_obj->set ("driver", driver_obj);
1362 /* Report plugins via the "extensions" property
1363 (SARIF v2.1.0 section 3.18.3). */
1364 if (m_context->m_client_data_hooks)
1365 if (const client_version_info *vinfo
1366 = m_context->m_client_data_hooks->get_any_version_info ())
1368 class my_plugin_visitor : public client_version_info :: plugin_visitor
1370 public:
1371 void on_plugin (const diagnostic_client_plugin_info &p) final override
1373 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1374 for the plugin. */
1375 json::object *plugin_obj = new json::object ();
1376 m_plugin_objs.safe_push (plugin_obj);
1378 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1379 if (const char *short_name = p.get_short_name ())
1380 plugin_obj->set ("name", new json::string (short_name));
1382 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1383 if (const char *full_name = p.get_full_name ())
1384 plugin_obj->set ("fullName", new json::string (full_name));
1386 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1387 if (const char *version = p.get_version ())
1388 plugin_obj->set ("version", new json::string (version));
1390 auto_vec <json::object *> m_plugin_objs;
1392 my_plugin_visitor v;
1393 vinfo->for_each_plugin (v);
1394 if (v.m_plugin_objs.length () > 0)
1396 json::array *extensions_arr = new json::array ();
1397 tool_obj->set ("extensions", extensions_arr);
1398 for (auto iter : v.m_plugin_objs)
1399 extensions_arr->append (iter);
1403 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1404 "extensions" (see toplev.cc: print_version). */
1406 return tool_obj;
1409 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1410 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1412 json::object *
1413 sarif_builder::make_driver_tool_component_object () const
1415 json::object *driver_obj = new json::object ();
1417 if (m_context->m_client_data_hooks)
1418 if (const client_version_info *vinfo
1419 = m_context->m_client_data_hooks->get_any_version_info ())
1421 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1422 if (const char *name = vinfo->get_tool_name ())
1423 driver_obj->set ("name", new json::string (name));
1425 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1426 if (char *full_name = vinfo->maybe_make_full_name ())
1428 driver_obj->set ("fullName", new json::string (full_name));
1429 free (full_name);
1432 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1433 if (const char *version = vinfo->get_version_string ())
1434 driver_obj->set ("version", new json::string (version));
1436 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1437 if (char *version_url = vinfo->maybe_make_version_url ())
1439 driver_obj->set ("informationUri", new json::string (version_url));
1440 free (version_url);
1444 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1445 driver_obj->set ("rules", m_rules_arr);
1447 return driver_obj;
1450 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1451 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1452 toolComponent (3.19) as per 3.19.3, representing the CWE.
1454 Otherwise return NULL. */
1456 json::array *
1457 sarif_builder::maybe_make_taxonomies_array () const
1459 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1460 if (!cwe_obj)
1461 return NULL;
1463 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1464 json::array *taxonomies_arr = new json::array ();
1465 taxonomies_arr->append (cwe_obj);
1466 return taxonomies_arr;
1469 /* If we've seen any CWE IDs, make a toolComponent object
1470 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1471 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1473 Otherwise return NULL. */
1475 json::object *
1476 sarif_builder::maybe_make_cwe_taxonomy_object () const
1478 if (m_cwe_id_set.is_empty ())
1479 return NULL;
1481 json::object *taxonomy_obj = new json::object ();
1483 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1484 taxonomy_obj->set ("name", new json::string ("CWE"));
1486 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1487 taxonomy_obj->set ("version", new json::string ("4.7"));
1489 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1490 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1492 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1493 json::object *short_desc
1494 = make_multiformat_message_string ("The MITRE"
1495 " Common Weakness Enumeration");
1496 taxonomy_obj->set ("shortDescription", short_desc);
1498 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1499 json::array *taxa_arr = new json::array ();
1500 for (auto cwe_id : m_cwe_id_set)
1502 json::object *cwe_taxon
1503 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1504 taxa_arr->append (cwe_taxon);
1506 taxonomy_obj->set ("taxa", taxa_arr);
1508 return taxonomy_obj;
1511 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1513 json::object *
1514 sarif_builder::make_artifact_object (const char *filename)
1516 json::object *artifact_obj = new json::object ();
1518 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1519 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1520 artifact_obj->set ("location", artifact_loc_obj);
1522 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1523 if (json::object *artifact_content_obj
1524 = maybe_make_artifact_content_object (filename))
1525 artifact_obj->set ("contents", artifact_content_obj);
1527 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1528 if (m_context->m_client_data_hooks)
1529 if (const char *source_lang
1530 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1531 (filename))
1532 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1534 return artifact_obj;
1537 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1538 full contents of FILENAME. */
1540 json::object *
1541 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1543 /* Let input.cc handle any charset conversion. */
1544 char_span utf8_content
1545 = m_context->m_file_cache->get_source_file_content (filename);
1546 if (!utf8_content)
1547 return NULL;
1549 /* Don't add it if it's not valid UTF-8. */
1550 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1551 return NULL;
1553 json::object *artifact_content_obj = new json::object ();
1554 artifact_content_obj->set ("text",
1555 new json::string (utf8_content.get_buffer (),
1556 utf8_content.length ()));
1557 return artifact_content_obj;
1560 /* Attempt to read the given range of lines from FILENAME; return
1561 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1563 char *
1564 sarif_builder::get_source_lines (const char *filename,
1565 int start_line,
1566 int end_line) const
1568 auto_vec<char> result;
1570 for (int line = start_line; line <= end_line; line++)
1572 char_span line_content
1573 = m_context->m_file_cache->get_source_line (filename, line);
1574 if (!line_content.get_buffer ())
1575 return NULL;
1576 result.reserve (line_content.length () + 1);
1577 for (size_t i = 0; i < line_content.length (); i++)
1578 result.quick_push (line_content[i]);
1579 result.quick_push ('\n');
1581 result.safe_push ('\0');
1583 return xstrdup (result.address ());
1586 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1587 run of lines within FILENAME (including the endpoints). */
1589 json::object *
1590 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1591 int start_line,
1592 int end_line) const
1594 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1596 if (!text_utf8)
1597 return NULL;
1599 /* Don't add it if it's not valid UTF-8. */
1600 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1602 free (text_utf8);
1603 return NULL;
1606 json::object *artifact_content_obj = new json::object ();
1607 artifact_content_obj->set ("text", new json::string (text_utf8));
1608 free (text_utf8);
1610 return artifact_content_obj;
1613 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1615 json::object *
1616 sarif_builder::make_fix_object (const rich_location &richloc)
1618 json::object *fix_obj = new json::object ();
1620 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1621 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1622 json::array *artifact_change_arr = new json::array ();
1623 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1624 artifact_change_arr->append (artifact_change_obj);
1625 fix_obj->set ("artifactChanges", artifact_change_arr);
1627 return fix_obj;
1630 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1632 json::object *
1633 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1635 json::object *artifact_change_obj = new json::object ();
1637 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1638 json::object *artifact_location_obj
1639 = make_artifact_location_object (richloc.get_loc ());
1640 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1642 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1643 json::array *replacement_arr = new json::array ();
1644 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1646 const fixit_hint *hint = richloc.get_fixit_hint (i);
1647 json::object *replacement_obj = make_replacement_object (*hint);
1648 replacement_arr->append (replacement_obj);
1650 artifact_change_obj->set ("replacements", replacement_arr);
1652 return artifact_change_obj;
1655 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1657 json::object *
1658 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1660 json::object *replacement_obj = new json::object ();
1662 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1663 json::object *region_obj = make_region_object_for_hint (hint);
1664 replacement_obj->set ("deletedRegion", region_obj);
1666 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1667 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1668 replacement_obj->set ("insertedContent", content_obj);
1670 return replacement_obj;
1673 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1675 json::object *
1676 sarif_builder::make_artifact_content_object (const char *text) const
1678 json::object *content_obj = new json::object ();
1680 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1681 content_obj->set ("text", new json::string (text));
1683 return content_obj;
1686 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1687 occurs. */
1689 static void
1690 sarif_ice_handler (diagnostic_context *context)
1692 /* Attempt to ensure that a .sarif file is written out. */
1693 diagnostic_finish (context);
1695 /* Print a header for the remaining output to stderr, and
1696 return, attempting to print the usual ICE messages to
1697 stderr. Hopefully this will be helpful to the user in
1698 indicating what's gone wrong (also for DejaGnu, for pruning
1699 those messages). */
1700 fnotice (stderr, "Internal compiler error:\n");
1703 class sarif_output_format : public diagnostic_output_format
1705 public:
1706 void on_begin_group () final override
1708 /* No-op, */
1710 void on_end_group () final override
1712 m_builder.end_group ();
1714 void
1715 on_begin_diagnostic (diagnostic_info *) final override
1717 /* No-op, */
1719 void
1720 on_end_diagnostic (diagnostic_info *diagnostic,
1721 diagnostic_t orig_diag_kind) final override
1723 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1725 void on_diagram (const diagnostic_diagram &diagram) final override
1727 m_builder.emit_diagram (&m_context, diagram);
1730 protected:
1731 sarif_output_format (diagnostic_context &context)
1732 : diagnostic_output_format (context),
1733 m_builder (&context)
1736 sarif_builder m_builder;
1739 class sarif_stream_output_format : public sarif_output_format
1741 public:
1742 sarif_stream_output_format (diagnostic_context &context, FILE *stream)
1743 : sarif_output_format (context),
1744 m_stream (stream)
1747 ~sarif_stream_output_format ()
1749 m_builder.flush_to_file (m_stream);
1751 private:
1752 FILE *m_stream;
1755 class sarif_file_output_format : public sarif_output_format
1757 public:
1758 sarif_file_output_format (diagnostic_context &context,
1759 const char *base_file_name)
1760 : sarif_output_format (context),
1761 m_base_file_name (xstrdup (base_file_name))
1764 ~sarif_file_output_format ()
1766 char *filename = concat (m_base_file_name, ".sarif", NULL);
1767 free (m_base_file_name);
1768 m_base_file_name = nullptr;
1769 FILE *outf = fopen (filename, "w");
1770 if (!outf)
1772 const char *errstr = xstrerror (errno);
1773 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1774 filename, errstr);
1775 free (filename);
1776 return;
1778 m_builder.flush_to_file (outf);
1779 fclose (outf);
1780 free (filename);
1783 private:
1784 char *m_base_file_name;
1787 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1788 to a file). */
1790 static void
1791 diagnostic_output_format_init_sarif (diagnostic_context *context)
1793 /* Override callbacks. */
1794 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
1795 context->ice_handler_cb = sarif_ice_handler;
1797 /* The metadata is handled in SARIF format, rather than as text. */
1798 context->show_cwe = false;
1799 context->show_rules = false;
1801 /* The option is handled in SARIF format, rather than as text. */
1802 context->show_option_requested = false;
1804 /* Don't colorize the text. */
1805 pp_show_color (context->printer) = false;
1808 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1810 void
1811 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1813 diagnostic_output_format_init_sarif (context);
1814 delete context->m_output_format;
1815 context->m_output_format = new sarif_stream_output_format (*context, stderr);
1818 /* Populate CONTEXT in preparation for SARIF output to a file named
1819 BASE_FILE_NAME.sarif. */
1821 void
1822 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1823 const char *base_file_name)
1825 diagnostic_output_format_init_sarif (context);
1826 delete context->m_output_format;
1827 context->m_output_format = new sarif_file_output_format (*context,
1828 base_file_name);
1831 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1833 void
1834 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1835 FILE *stream)
1837 diagnostic_output_format_init_sarif (context);
1838 delete context->m_output_format;
1839 context->m_output_format = new sarif_stream_output_format (*context,
1840 stream);