tree-optimization/111792 - new testcase
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blob25f6239272400be2cded5cb3dcbc2286ed152a31
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 (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 ("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->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->m_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->m_get_option_url)
644 char *option_url
645 = context->m_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 (auto client_data_hooks = m_context->get_client_data_hooks ())
734 logical_loc = client_data_hooks->get_current_logical_location ();
736 json::object *location_obj
737 = make_location_object (*diagnostic->richloc, logical_loc);
738 locations_arr->append (location_obj);
739 return locations_arr;
742 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
743 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
745 void
746 sarif_builder::
747 set_any_logical_locs_arr (json::object *location_obj,
748 const logical_location *logical_loc)
750 if (!logical_loc)
751 return;
752 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
753 json::array *location_locs_arr = new json::array ();
754 location_locs_arr->append (logical_loc_obj);
755 location_obj->set ("logicalLocations", location_locs_arr);
758 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
759 and LOGICAL_LOC. */
761 json::object *
762 sarif_builder::make_location_object (const rich_location &rich_loc,
763 const logical_location *logical_loc)
765 json::object *location_obj = new json::object ();
767 /* Get primary loc from RICH_LOC. */
768 location_t loc = rich_loc.get_loc ();
770 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
771 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
772 location_obj->set ("physicalLocation", phs_loc_obj);
774 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
775 set_any_logical_locs_arr (location_obj, logical_loc);
777 return location_obj;
780 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
781 within a diagnostic_path. */
783 json::object *
784 sarif_builder::make_location_object (const diagnostic_event &event)
786 json::object *location_obj = new json::object ();
788 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
789 location_t loc = event.get_location ();
790 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
791 location_obj->set ("physicalLocation", phs_loc_obj);
793 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
794 const logical_location *logical_loc = event.get_logical_location ();
795 set_any_logical_locs_arr (location_obj, logical_loc);
797 /* "message" property (SARIF v2.1.0 section 3.28.5). */
798 label_text ev_desc = event.get_desc (false);
799 json::object *message_obj = make_message_object (ev_desc.get ());
800 location_obj->set ("message", message_obj);
802 return location_obj;
805 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
806 or return NULL;
807 Add any filename to the m_artifacts. */
809 json::object *
810 sarif_builder::maybe_make_physical_location_object (location_t loc)
812 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
813 return NULL;
815 json::object *phys_loc_obj = new json::object ();
817 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
818 json::object *artifact_loc_obj = make_artifact_location_object (loc);
819 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
820 m_filenames.add (LOCATION_FILE (loc));
822 /* "region" property (SARIF v2.1.0 section 3.29.4). */
823 if (json::object *region_obj = maybe_make_region_object (loc))
824 phys_loc_obj->set ("region", region_obj);
826 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
827 if (json::object *context_region_obj
828 = maybe_make_region_object_for_context (loc))
829 phys_loc_obj->set ("contextRegion", context_region_obj);
831 /* Instead, we add artifacts to the run as a whole,
832 with artifact.contents.
833 Could do both, though. */
835 return phys_loc_obj;
838 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
839 or return NULL. */
841 json::object *
842 sarif_builder::make_artifact_location_object (location_t loc)
844 return make_artifact_location_object (LOCATION_FILE (loc));
847 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
848 for when we need to express paths relative to PWD. */
850 #define PWD_PROPERTY_NAME ("PWD")
852 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
853 or return NULL. */
855 json::object *
856 sarif_builder::make_artifact_location_object (const char *filename)
858 json::object *artifact_loc_obj = new json::object ();
860 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
861 artifact_loc_obj->set ("uri", new json::string (filename));
863 if (filename[0] != '/')
865 /* If we have a relative path, set the "uriBaseId" property
866 (SARIF v2.1.0 section 3.4.4). */
867 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
868 m_seen_any_relative_paths = true;
871 return artifact_loc_obj;
874 /* Get the PWD, or NULL, as an absolute file-based URI,
875 adding a trailing forward slash (as required by SARIF v2.1.0
876 section 3.14.14). */
878 static char *
879 make_pwd_uri_str ()
881 /* The prefix of a file-based URI, up to, but not including the path. */
882 #define FILE_PREFIX ("file://")
884 const char *pwd = getpwd ();
885 if (!pwd)
886 return NULL;
887 size_t len = strlen (pwd);
888 if (len == 0 || pwd[len - 1] != '/')
889 return concat (FILE_PREFIX, pwd, "/", NULL);
890 else
892 gcc_assert (pwd[len - 1] == '/');
893 return concat (FILE_PREFIX, pwd, NULL);
897 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
898 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
899 section 3.14.14) when we have any relative paths. */
901 json::object *
902 sarif_builder::make_artifact_location_object_for_pwd () const
904 json::object *artifact_loc_obj = new json::object ();
906 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
907 if (char *pwd = make_pwd_uri_str ())
909 gcc_assert (strlen (pwd) > 0);
910 gcc_assert (pwd[strlen (pwd) - 1] == '/');
911 artifact_loc_obj->set ("uri", new json::string (pwd));
912 free (pwd);
915 return artifact_loc_obj;
918 /* Get the column number within EXPLOC. */
921 sarif_builder::get_sarif_column (expanded_location exploc) const
923 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
924 return location_compute_display_column (exploc, policy);
927 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
928 or return NULL. */
930 json::object *
931 sarif_builder::maybe_make_region_object (location_t loc) const
933 location_t caret_loc = get_pure_location (loc);
935 if (caret_loc <= BUILTINS_LOCATION)
936 return NULL;
938 location_t start_loc = get_start (loc);
939 location_t finish_loc = get_finish (loc);
941 expanded_location exploc_caret = expand_location (caret_loc);
942 expanded_location exploc_start = expand_location (start_loc);
943 expanded_location exploc_finish = expand_location (finish_loc);
945 if (exploc_start.file !=exploc_caret.file)
946 return NULL;
947 if (exploc_finish.file !=exploc_caret.file)
948 return NULL;
950 json::object *region_obj = new json::object ();
952 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
953 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
955 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
956 region_obj->set ("startColumn",
957 new json::integer_number (get_sarif_column (exploc_start)));
959 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
960 if (exploc_finish.line != exploc_start.line)
961 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
963 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
964 This expresses the column immediately beyond the range. */
966 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
967 region_obj->set ("endColumn", new json::integer_number (next_column));
970 return region_obj;
973 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
974 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
976 This is similar to maybe_make_region_object, but ignores column numbers,
977 covering the line(s) as a whole, and including a "snippet" property
978 embedding those source lines, making it easier for consumers to show
979 the pertinent source. */
981 json::object *
982 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
984 location_t caret_loc = get_pure_location (loc);
986 if (caret_loc <= BUILTINS_LOCATION)
987 return NULL;
989 location_t start_loc = get_start (loc);
990 location_t finish_loc = get_finish (loc);
992 expanded_location exploc_caret = expand_location (caret_loc);
993 expanded_location exploc_start = expand_location (start_loc);
994 expanded_location exploc_finish = expand_location (finish_loc);
996 if (exploc_start.file !=exploc_caret.file)
997 return NULL;
998 if (exploc_finish.file !=exploc_caret.file)
999 return NULL;
1001 json::object *region_obj = new json::object ();
1003 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1004 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
1006 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1007 if (exploc_finish.line != exploc_start.line)
1008 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
1010 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1011 if (json::object *artifact_content_obj
1012 = maybe_make_artifact_content_object (exploc_start.file,
1013 exploc_start.line,
1014 exploc_finish.line))
1015 region_obj->set ("snippet", artifact_content_obj);
1017 return region_obj;
1020 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1021 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1023 json::object *
1024 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1026 location_t start_loc = hint.get_start_loc ();
1027 location_t next_loc = hint.get_next_loc ();
1029 expanded_location exploc_start = expand_location (start_loc);
1030 expanded_location exploc_next = expand_location (next_loc);
1032 json::object *region_obj = new json::object ();
1034 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1035 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
1037 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1038 int start_col = get_sarif_column (exploc_start);
1039 region_obj->set ("startColumn",
1040 new json::integer_number (start_col));
1042 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1043 if (exploc_next.line != exploc_start.line)
1044 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
1046 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1047 This expresses the column immediately beyond the range. */
1048 int next_col = get_sarif_column (exploc_next);
1049 region_obj->set ("endColumn", new json::integer_number (next_col));
1051 return region_obj;
1054 /* Attempt to get a string for a logicalLocation's "kind" property
1055 (SARIF v2.1.0 section 3.33.7).
1056 Return NULL if unknown. */
1058 static const char *
1059 maybe_get_sarif_kind (enum logical_location_kind kind)
1061 switch (kind)
1063 default:
1064 gcc_unreachable ();
1065 case LOGICAL_LOCATION_KIND_UNKNOWN:
1066 return NULL;
1068 case LOGICAL_LOCATION_KIND_FUNCTION:
1069 return "function";
1070 case LOGICAL_LOCATION_KIND_MEMBER:
1071 return "member";
1072 case LOGICAL_LOCATION_KIND_MODULE:
1073 return "module";
1074 case LOGICAL_LOCATION_KIND_NAMESPACE:
1075 return "namespace";
1076 case LOGICAL_LOCATION_KIND_TYPE:
1077 return "type";
1078 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1079 return "returnType";
1080 case LOGICAL_LOCATION_KIND_PARAMETER:
1081 return "parameter";
1082 case LOGICAL_LOCATION_KIND_VARIABLE:
1083 return "variable";
1087 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1088 or return NULL. */
1090 json::object *
1091 sarif_builder::
1092 make_logical_location_object (const logical_location &logical_loc) const
1094 json::object *logical_loc_obj = new json::object ();
1096 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1097 if (const char *short_name = logical_loc.get_short_name ())
1098 logical_loc_obj->set ("name", new json::string (short_name));
1100 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1101 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1102 logical_loc_obj->set ("fullyQualifiedName",
1103 new json::string (name_with_scope));
1105 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1106 if (const char *internal_name = logical_loc.get_internal_name ())
1107 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
1109 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1110 enum logical_location_kind kind = logical_loc.get_kind ();
1111 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1112 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
1114 return logical_loc_obj;
1117 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1119 json::object *
1120 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1122 json::object *code_flow_obj = new json::object ();
1124 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1125 json::array *thread_flows_arr = new json::array ();
1127 /* Walk the events, consolidating into per-thread threadFlow objects,
1128 using the index with PATH as the overall executionOrder. */
1129 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1130 sarif_thread_flow *> thread_id_map;
1131 for (unsigned i = 0; i < path.num_events (); i++)
1133 const diagnostic_event &event = path.get_event (i);
1134 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1135 sarif_thread_flow *thread_flow_obj;
1137 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1138 thread_flow_obj = *slot;
1139 else
1141 const diagnostic_thread &thread = path.get_thread (thread_id);
1142 thread_flow_obj = new sarif_thread_flow (thread);
1143 thread_flows_arr->append (thread_flow_obj);
1144 thread_id_map.put (thread_id, thread_flow_obj);
1147 /* Add event to thread's threadFlow object. */
1148 json::object *thread_flow_loc_obj
1149 = make_thread_flow_location_object (event, i);
1150 thread_flow_obj->add_location (thread_flow_loc_obj);
1152 code_flow_obj->set ("threadFlows", thread_flows_arr);
1154 return code_flow_obj;
1157 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1159 json::object *
1160 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1161 int path_event_idx)
1163 json::object *thread_flow_loc_obj = new json::object ();
1165 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1166 json::object *location_obj = make_location_object (ev);
1167 thread_flow_loc_obj->set ("location", location_obj);
1169 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1170 diagnostic_event::meaning m = ev.get_meaning ();
1171 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1172 thread_flow_loc_obj->set ("kinds", kinds_arr);
1174 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1175 thread_flow_loc_obj->set ("nestingLevel",
1176 new json::integer_number (ev.get_stack_depth ()));
1178 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1179 Offset by 1 to match the human-readable values emitted by %@. */
1180 thread_flow_loc_obj->set ("executionOrder",
1181 new json::integer_number (path_event_idx + 1));
1183 /* It might be nice to eventually implement the following for -fanalyzer:
1184 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1185 - the "state" property (SARIF v2.1.0 section 3.38.9)
1186 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1188 return thread_flow_loc_obj;
1191 /* If M has any known meaning, make a json array suitable for the "kinds"
1192 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1194 Otherwise, return NULL. */
1196 json::array *
1197 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1199 if (m.m_verb == diagnostic_event::VERB_unknown
1200 && m.m_noun == diagnostic_event::NOUN_unknown
1201 && m.m_property == diagnostic_event::PROPERTY_unknown)
1202 return NULL;
1204 json::array *kinds_arr = new json::array ();
1205 if (const char *verb_str
1206 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1207 kinds_arr->append (new json::string (verb_str));
1208 if (const char *noun_str
1209 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1210 kinds_arr->append (new json::string (noun_str));
1211 if (const char *property_str
1212 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1213 kinds_arr->append (new json::string (property_str));
1214 return kinds_arr;
1217 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1219 json::object *
1220 sarif_builder::make_message_object (const char *msg) const
1222 json::object *message_obj = new json::object ();
1224 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1225 message_obj->set ("text", new json::string (msg));
1227 return message_obj;
1230 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1231 We emit the diagram as a code block within the Markdown part
1232 of the message. */
1234 json::object *
1235 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1236 const diagnostic_diagram &diagram)
1238 json::object *message_obj = new json::object ();
1240 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1241 message_obj->set ("text", new json::string (diagram.get_alt_text ()));
1243 char *saved_prefix = pp_take_prefix (context->printer);
1244 pp_set_prefix (context->printer, NULL);
1246 /* "To produce a code block in Markdown, simply indent every line of
1247 the block by at least 4 spaces or 1 tab."
1248 Here we use 4 spaces. */
1249 diagram.get_canvas ().print_to_pp (context->printer, " ");
1250 pp_set_prefix (context->printer, saved_prefix);
1252 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1253 message_obj->set ("markdown",
1254 new json::string (pp_formatted_text (context->printer)));
1256 pp_clear_output_area (context->printer);
1258 return message_obj;
1261 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1262 for MSG. */
1264 json::object *
1265 sarif_builder::make_multiformat_message_string (const char *msg) const
1267 json::object *message_obj = new json::object ();
1269 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1270 message_obj->set ("text", new json::string (msg));
1272 return message_obj;
1275 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1276 #define SARIF_VERSION "2.1.0"
1278 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1279 Take ownership of INVOCATION_OBJ and RESULTS. */
1281 json::object *
1282 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1283 json::array *results)
1285 json::object *log_obj = new json::object ();
1287 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1288 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1290 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1291 log_obj->set ("version", new json::string (SARIF_VERSION));
1293 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1294 json::array *run_arr = new json::array ();
1295 json::object *run_obj = make_run_object (invocation_obj, results);
1296 run_arr->append (run_obj);
1297 log_obj->set ("runs", run_arr);
1299 return log_obj;
1302 /* Make a run object (SARIF v2.1.0 section 3.14).
1303 Take ownership of INVOCATION_OBJ and RESULTS. */
1305 json::object *
1306 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1307 json::array *results)
1309 json::object *run_obj = new json::object ();
1311 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1312 json::object *tool_obj = make_tool_object ();
1313 run_obj->set ("tool", tool_obj);
1315 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1316 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1317 run_obj->set ("taxonomies", taxonomies_arr);
1319 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1321 json::array *invocations_arr = new json::array ();
1322 invocations_arr->append (invocation_obj);
1323 run_obj->set ("invocations", invocations_arr);
1326 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1327 if (m_seen_any_relative_paths)
1329 json::object *orig_uri_base_ids = new json::object ();
1330 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1331 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1332 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1335 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1336 json::array *artifacts_arr = new json::array ();
1337 for (auto iter : m_filenames)
1339 json::object *artifact_obj = make_artifact_object (iter);
1340 artifacts_arr->append (artifact_obj);
1342 run_obj->set ("artifacts", artifacts_arr);
1344 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1345 run_obj->set ("results", results);
1347 return run_obj;
1350 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1352 json::object *
1353 sarif_builder::make_tool_object () const
1355 json::object *tool_obj = new json::object ();
1357 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1358 json::object *driver_obj = make_driver_tool_component_object ();
1359 tool_obj->set ("driver", driver_obj);
1361 /* Report plugins via the "extensions" property
1362 (SARIF v2.1.0 section 3.18.3). */
1363 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1364 if (const client_version_info *vinfo
1365 = client_data_hooks->get_any_version_info ())
1367 class my_plugin_visitor : public client_version_info :: plugin_visitor
1369 public:
1370 void on_plugin (const diagnostic_client_plugin_info &p) final override
1372 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1373 for the plugin. */
1374 json::object *plugin_obj = new json::object ();
1375 m_plugin_objs.safe_push (plugin_obj);
1377 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1378 if (const char *short_name = p.get_short_name ())
1379 plugin_obj->set ("name", new json::string (short_name));
1381 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1382 if (const char *full_name = p.get_full_name ())
1383 plugin_obj->set ("fullName", new json::string (full_name));
1385 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1386 if (const char *version = p.get_version ())
1387 plugin_obj->set ("version", new json::string (version));
1389 auto_vec <json::object *> m_plugin_objs;
1391 my_plugin_visitor v;
1392 vinfo->for_each_plugin (v);
1393 if (v.m_plugin_objs.length () > 0)
1395 json::array *extensions_arr = new json::array ();
1396 tool_obj->set ("extensions", extensions_arr);
1397 for (auto iter : v.m_plugin_objs)
1398 extensions_arr->append (iter);
1402 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1403 "extensions" (see toplev.cc: print_version). */
1405 return tool_obj;
1408 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1409 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1411 json::object *
1412 sarif_builder::make_driver_tool_component_object () const
1414 json::object *driver_obj = new json::object ();
1416 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1417 if (const client_version_info *vinfo
1418 = client_data_hooks->get_any_version_info ())
1420 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1421 if (const char *name = vinfo->get_tool_name ())
1422 driver_obj->set ("name", new json::string (name));
1424 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1425 if (char *full_name = vinfo->maybe_make_full_name ())
1427 driver_obj->set ("fullName", new json::string (full_name));
1428 free (full_name);
1431 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1432 if (const char *version = vinfo->get_version_string ())
1433 driver_obj->set ("version", new json::string (version));
1435 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1436 if (char *version_url = vinfo->maybe_make_version_url ())
1438 driver_obj->set ("informationUri", new json::string (version_url));
1439 free (version_url);
1443 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1444 driver_obj->set ("rules", m_rules_arr);
1446 return driver_obj;
1449 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1450 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1451 toolComponent (3.19) as per 3.19.3, representing the CWE.
1453 Otherwise return NULL. */
1455 json::array *
1456 sarif_builder::maybe_make_taxonomies_array () const
1458 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1459 if (!cwe_obj)
1460 return NULL;
1462 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1463 json::array *taxonomies_arr = new json::array ();
1464 taxonomies_arr->append (cwe_obj);
1465 return taxonomies_arr;
1468 /* If we've seen any CWE IDs, make a toolComponent object
1469 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1470 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1472 Otherwise return NULL. */
1474 json::object *
1475 sarif_builder::maybe_make_cwe_taxonomy_object () const
1477 if (m_cwe_id_set.is_empty ())
1478 return NULL;
1480 json::object *taxonomy_obj = new json::object ();
1482 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1483 taxonomy_obj->set ("name", new json::string ("CWE"));
1485 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1486 taxonomy_obj->set ("version", new json::string ("4.7"));
1488 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1489 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1491 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1492 json::object *short_desc
1493 = make_multiformat_message_string ("The MITRE"
1494 " Common Weakness Enumeration");
1495 taxonomy_obj->set ("shortDescription", short_desc);
1497 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1498 json::array *taxa_arr = new json::array ();
1499 for (auto cwe_id : m_cwe_id_set)
1501 json::object *cwe_taxon
1502 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1503 taxa_arr->append (cwe_taxon);
1505 taxonomy_obj->set ("taxa", taxa_arr);
1507 return taxonomy_obj;
1510 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1512 json::object *
1513 sarif_builder::make_artifact_object (const char *filename)
1515 json::object *artifact_obj = new json::object ();
1517 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1518 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1519 artifact_obj->set ("location", artifact_loc_obj);
1521 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1522 if (json::object *artifact_content_obj
1523 = maybe_make_artifact_content_object (filename))
1524 artifact_obj->set ("contents", artifact_content_obj);
1526 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1527 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1528 if (const char *source_lang
1529 = client_data_hooks->maybe_get_sarif_source_language (filename))
1530 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1532 return artifact_obj;
1535 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1536 full contents of FILENAME. */
1538 json::object *
1539 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1541 /* Let input.cc handle any charset conversion. */
1542 char_span utf8_content
1543 = m_context->get_file_cache ()->get_source_file_content (filename);
1544 if (!utf8_content)
1545 return NULL;
1547 /* Don't add it if it's not valid UTF-8. */
1548 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1549 return NULL;
1551 json::object *artifact_content_obj = new json::object ();
1552 artifact_content_obj->set ("text",
1553 new json::string (utf8_content.get_buffer (),
1554 utf8_content.length ()));
1555 return artifact_content_obj;
1558 /* Attempt to read the given range of lines from FILENAME; return
1559 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1561 char *
1562 sarif_builder::get_source_lines (const char *filename,
1563 int start_line,
1564 int end_line) const
1566 auto_vec<char> result;
1568 for (int line = start_line; line <= end_line; line++)
1570 char_span line_content
1571 = m_context->get_file_cache ()->get_source_line (filename, line);
1572 if (!line_content.get_buffer ())
1573 return NULL;
1574 result.reserve (line_content.length () + 1);
1575 for (size_t i = 0; i < line_content.length (); i++)
1576 result.quick_push (line_content[i]);
1577 result.quick_push ('\n');
1579 result.safe_push ('\0');
1581 return xstrdup (result.address ());
1584 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1585 run of lines within FILENAME (including the endpoints). */
1587 json::object *
1588 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1589 int start_line,
1590 int end_line) const
1592 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1594 if (!text_utf8)
1595 return NULL;
1597 /* Don't add it if it's not valid UTF-8. */
1598 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1600 free (text_utf8);
1601 return NULL;
1604 json::object *artifact_content_obj = new json::object ();
1605 artifact_content_obj->set ("text", new json::string (text_utf8));
1606 free (text_utf8);
1608 return artifact_content_obj;
1611 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1613 json::object *
1614 sarif_builder::make_fix_object (const rich_location &richloc)
1616 json::object *fix_obj = new json::object ();
1618 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1619 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1620 json::array *artifact_change_arr = new json::array ();
1621 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1622 artifact_change_arr->append (artifact_change_obj);
1623 fix_obj->set ("artifactChanges", artifact_change_arr);
1625 return fix_obj;
1628 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1630 json::object *
1631 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1633 json::object *artifact_change_obj = new json::object ();
1635 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1636 json::object *artifact_location_obj
1637 = make_artifact_location_object (richloc.get_loc ());
1638 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1640 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1641 json::array *replacement_arr = new json::array ();
1642 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1644 const fixit_hint *hint = richloc.get_fixit_hint (i);
1645 json::object *replacement_obj = make_replacement_object (*hint);
1646 replacement_arr->append (replacement_obj);
1648 artifact_change_obj->set ("replacements", replacement_arr);
1650 return artifact_change_obj;
1653 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1655 json::object *
1656 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1658 json::object *replacement_obj = new json::object ();
1660 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1661 json::object *region_obj = make_region_object_for_hint (hint);
1662 replacement_obj->set ("deletedRegion", region_obj);
1664 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1665 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1666 replacement_obj->set ("insertedContent", content_obj);
1668 return replacement_obj;
1671 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1673 json::object *
1674 sarif_builder::make_artifact_content_object (const char *text) const
1676 json::object *content_obj = new json::object ();
1678 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1679 content_obj->set ("text", new json::string (text));
1681 return content_obj;
1684 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1685 occurs. */
1687 static void
1688 sarif_ice_handler (diagnostic_context *context)
1690 /* Attempt to ensure that a .sarif file is written out. */
1691 diagnostic_finish (context);
1693 /* Print a header for the remaining output to stderr, and
1694 return, attempting to print the usual ICE messages to
1695 stderr. Hopefully this will be helpful to the user in
1696 indicating what's gone wrong (also for DejaGnu, for pruning
1697 those messages). */
1698 fnotice (stderr, "Internal compiler error:\n");
1701 class sarif_output_format : public diagnostic_output_format
1703 public:
1704 void on_begin_group () final override
1706 /* No-op, */
1708 void on_end_group () final override
1710 m_builder.end_group ();
1712 void
1713 on_begin_diagnostic (diagnostic_info *) final override
1715 /* No-op, */
1717 void
1718 on_end_diagnostic (diagnostic_info *diagnostic,
1719 diagnostic_t orig_diag_kind) final override
1721 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1723 void on_diagram (const diagnostic_diagram &diagram) final override
1725 m_builder.emit_diagram (&m_context, diagram);
1728 protected:
1729 sarif_output_format (diagnostic_context &context)
1730 : diagnostic_output_format (context),
1731 m_builder (&context)
1734 sarif_builder m_builder;
1737 class sarif_stream_output_format : public sarif_output_format
1739 public:
1740 sarif_stream_output_format (diagnostic_context &context, FILE *stream)
1741 : sarif_output_format (context),
1742 m_stream (stream)
1745 ~sarif_stream_output_format ()
1747 m_builder.flush_to_file (m_stream);
1749 private:
1750 FILE *m_stream;
1753 class sarif_file_output_format : public sarif_output_format
1755 public:
1756 sarif_file_output_format (diagnostic_context &context,
1757 const char *base_file_name)
1758 : sarif_output_format (context),
1759 m_base_file_name (xstrdup (base_file_name))
1762 ~sarif_file_output_format ()
1764 char *filename = concat (m_base_file_name, ".sarif", NULL);
1765 free (m_base_file_name);
1766 m_base_file_name = nullptr;
1767 FILE *outf = fopen (filename, "w");
1768 if (!outf)
1770 const char *errstr = xstrerror (errno);
1771 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1772 filename, errstr);
1773 free (filename);
1774 return;
1776 m_builder.flush_to_file (outf);
1777 fclose (outf);
1778 free (filename);
1781 private:
1782 char *m_base_file_name;
1785 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1786 to a file). */
1788 static void
1789 diagnostic_output_format_init_sarif (diagnostic_context *context)
1791 /* Override callbacks. */
1792 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1793 context->set_ice_handler_callback (sarif_ice_handler);
1795 /* The metadata is handled in SARIF format, rather than as text. */
1796 context->set_show_cwe (false);
1797 context->set_show_rules (false);
1799 /* The option is handled in SARIF format, rather than as text. */
1800 context->set_show_option_requested (false);
1802 /* Don't colorize the text. */
1803 pp_show_color (context->printer) = false;
1806 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1808 void
1809 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1811 diagnostic_output_format_init_sarif (context);
1812 context->set_output_format (new sarif_stream_output_format (*context,
1813 stderr));
1816 /* Populate CONTEXT in preparation for SARIF output to a file named
1817 BASE_FILE_NAME.sarif. */
1819 void
1820 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1821 const char *base_file_name)
1823 diagnostic_output_format_init_sarif (context);
1824 context->set_output_format (new sarif_file_output_format (*context,
1825 base_file_name));
1828 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1830 void
1831 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1832 FILE *stream)
1834 diagnostic_output_format_init_sarif (context);
1835 context->set_output_format (new sarif_stream_output_format (*context,
1836 stream));