PR27116, Spelling errors found by Debian style checker
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blob1eff71962d746ab3680763a5a46d9fb2df881960
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 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
98 and -fdiagnostics-format=sarif-file).
100 As diagnostics occur, we build "result" JSON objects, and
101 accumulate state:
102 - which source files are referenced
103 - which warnings are emitted
104 - which CWEs are used
106 At the end of the compile, we use the above to build the full SARIF
107 object tree, adding the result objects to the correct place, and
108 creating objects for the various source files, warnings and CWEs
109 referenced.
111 Implemented:
112 - fix-it hints
113 - CWE metadata
114 - diagnostic groups (see limitations below)
115 - logical locations (e.g. cfun)
117 Known limitations:
118 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
119 but we only capture location and message information from such nested
120 diagnostics (e.g. we ignore fix-it hints on them)
121 - doesn't yet capture command-line arguments: would be run.invocations
122 property (SARIF v2.1.0 section 3.14.11), as invocation objects
123 (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
124 toplev::main, and the response files.
125 - doesn't capture escape_on_output_p
126 - doesn't capture secondary locations within a rich_location
127 (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
128 section 3.27.22)
129 - doesn't capture "artifact.encoding" property
130 (SARIF v2.1.0 section 3.24.9).
131 - doesn't capture hashes of the source files
132 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
133 - doesn't capture the "analysisTarget" property
134 (SARIF v2.1.0 section 3.27.13).
135 - doesn't capture labelled ranges
136 - doesn't capture -Werror cleanly
137 - doesn't capture inlining information (can SARIF handle this?)
138 - doesn't capture macro expansion information (can SARIF handle this?). */
140 class sarif_builder
142 public:
143 sarif_builder (diagnostic_context *context);
145 void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
146 diagnostic_t orig_diag_kind);
147 void emit_diagram (diagnostic_context *context,
148 const diagnostic_diagram &diagram);
149 void end_group ();
151 void flush_to_file (FILE *outf);
153 json::array *make_locations_arr (diagnostic_info *diagnostic);
154 json::object *make_location_object (const rich_location &rich_loc,
155 const logical_location *logical_loc);
156 json::object *make_message_object (const char *msg) const;
157 json::object *
158 make_message_object_for_diagram (diagnostic_context *context,
159 const diagnostic_diagram &diagram);
161 private:
162 sarif_result *make_result_object (diagnostic_context *context,
163 diagnostic_info *diagnostic,
164 diagnostic_t orig_diag_kind);
165 void set_any_logical_locs_arr (json::object *location_obj,
166 const logical_location *logical_loc);
167 json::object *make_location_object (const diagnostic_event &event);
168 json::object *
169 make_logical_location_object (const logical_location &logical_loc) const;
170 json::object *make_code_flow_object (const diagnostic_path &path);
171 json::object *make_thread_flow_object (const diagnostic_path &path);
172 json::object *
173 make_thread_flow_location_object (const diagnostic_event &event);
174 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
175 json::object *maybe_make_physical_location_object (location_t loc);
176 json::object *make_artifact_location_object (location_t loc);
177 json::object *make_artifact_location_object (const char *filename);
178 json::object *make_artifact_location_object_for_pwd () const;
179 json::object *maybe_make_region_object (location_t loc) const;
180 json::object *maybe_make_region_object_for_context (location_t loc) const;
181 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
182 json::object *make_multiformat_message_string (const char *msg) const;
183 json::object *make_top_level_object (sarif_invocation *invocation_obj,
184 json::array *results);
185 json::object *make_run_object (sarif_invocation *invocation_obj,
186 json::array *results);
187 json::object *make_tool_object () const;
188 json::object *make_driver_tool_component_object () const;
189 json::array *maybe_make_taxonomies_array () const;
190 json::object *maybe_make_cwe_taxonomy_object () const;
191 json::object *make_tool_component_reference_object_for_cwe () const;
192 json::object *
193 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
194 diagnostic_info *diagnostic,
195 diagnostic_t orig_diag_kind,
196 const char *option_text);
197 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
198 json::object *
199 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
200 json::object *make_artifact_object (const char *filename);
201 json::object *maybe_make_artifact_content_object (const char *filename) const;
202 json::object *maybe_make_artifact_content_object (const char *filename,
203 int start_line,
204 int end_line) const;
205 json::object *make_fix_object (const rich_location &rich_loc);
206 json::object *make_artifact_change_object (const rich_location &richloc);
207 json::object *make_replacement_object (const fixit_hint &hint) const;
208 json::object *make_artifact_content_object (const char *text) const;
209 int get_sarif_column (expanded_location exploc) const;
211 diagnostic_context *m_context;
213 /* The JSON object for the invocation object. */
214 sarif_invocation *m_invocation_obj;
216 /* The JSON array of pending diagnostics. */
217 json::array *m_results_array;
219 /* The JSON object for the result object (if any) in the current
220 diagnostic group. */
221 sarif_result *m_cur_group_result;
223 hash_set <const char *> m_filenames;
224 bool m_seen_any_relative_paths;
225 hash_set <free_string_hash> m_rule_id_set;
226 json::array *m_rules_arr;
228 /* The set of all CWE IDs we've seen, if any. */
229 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
231 int m_tabstop;
234 static sarif_builder *the_builder;
236 /* class sarif_object : public json::object. */
238 sarif_property_bag &
239 sarif_object::get_or_create_properties ()
241 json::value *properties_val = get ("properties");
242 if (properties_val)
244 if (properties_val->get_kind () == json::JSON_OBJECT)
245 return *static_cast <sarif_property_bag *> (properties_val);
248 sarif_property_bag *bag = new sarif_property_bag ();
249 set ("properties", bag);
250 return *bag;
253 /* class sarif_invocation : public sarif_object. */
255 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
256 Add an object representing the ICE to the notifications array. */
258 void
259 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
260 diagnostic_info *diagnostic,
261 sarif_builder *builder)
263 m_success = false;
265 sarif_ice_notification *notification_obj
266 = new sarif_ice_notification (context, diagnostic, builder);
267 m_notifications_arr->append (notification_obj);
270 void
271 sarif_invocation::prepare_to_flush (diagnostic_context *context)
273 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
274 set ("executionSuccessful", new json::literal (m_success));
276 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
277 set ("toolExecutionNotifications", m_notifications_arr);
279 /* Call client hook, allowing it to create a custom property bag for
280 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
281 if (context->m_client_data_hooks)
282 context->m_client_data_hooks->add_sarif_invocation_properties (*this);
285 /* class sarif_result : public sarif_object. */
287 /* Handle secondary diagnostics that occur within a diagnostic group.
288 The closest SARIF seems to have to nested diagnostics is the
289 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
290 so we lazily set this property and populate the array if and when
291 secondary diagnostics occur (such as notes to a warning). */
293 void
294 sarif_result::on_nested_diagnostic (diagnostic_context *context,
295 diagnostic_info *diagnostic,
296 diagnostic_t /*orig_diag_kind*/,
297 sarif_builder *builder)
299 /* We don't yet generate meaningful logical locations for notes;
300 sometimes these will related to current_function_decl, but
301 often they won't. */
302 json::object *location_obj
303 = builder->make_location_object (*diagnostic->richloc, NULL);
304 json::object *message_obj
305 = builder->make_message_object (pp_formatted_text (context->printer));
306 pp_clear_output_area (context->printer);
307 location_obj->set ("message", message_obj);
309 add_related_location (location_obj);
312 /* Handle diagrams that occur within a diagnostic group.
313 The closest thing in SARIF seems to be to add a location to the
314 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
315 and to put the diagram into the "message" property of that location
316 (SARIF v2.1.0 section 3.28.5). */
318 void
319 sarif_result::on_diagram (diagnostic_context *context,
320 const diagnostic_diagram &diagram,
321 sarif_builder *builder)
323 json::object *location_obj = new json::object ();
324 json::object *message_obj
325 = builder->make_message_object_for_diagram (context, diagram);
326 location_obj->set ("message", message_obj);
328 add_related_location (location_obj);
331 /* Add LOCATION_OBJ to this result's "relatedLocations" array,
332 creating it if it doesn't yet exist. */
334 void
335 sarif_result::add_related_location (json::object *location_obj)
337 if (!m_related_locations_arr)
339 m_related_locations_arr = new json::array ();
340 set ("relatedLocations", m_related_locations_arr);
342 m_related_locations_arr->append (location_obj);
345 /* class sarif_ice_notification : public sarif_object. */
347 /* sarif_ice_notification's ctor.
348 DIAGNOSTIC is an internal compiler error. */
350 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
351 diagnostic_info *diagnostic,
352 sarif_builder *builder)
354 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
355 json::array *locations_arr = builder->make_locations_arr (diagnostic);
356 set ("locations", locations_arr);
358 /* "message" property (SARIF v2.1.0 section 3.85.5). */
359 json::object *message_obj
360 = builder->make_message_object (pp_formatted_text (context->printer));
361 pp_clear_output_area (context->printer);
362 set ("message", message_obj);
364 /* "level" property (SARIF v2.1.0 section 3.58.6). */
365 set ("level", new json::string ("error"));
368 /* class sarif_builder. */
370 /* sarif_builder's ctor. */
372 sarif_builder::sarif_builder (diagnostic_context *context)
373 : m_context (context),
374 m_invocation_obj (new sarif_invocation ()),
375 m_results_array (new json::array ()),
376 m_cur_group_result (NULL),
377 m_seen_any_relative_paths (false),
378 m_rule_id_set (),
379 m_rules_arr (new json::array ()),
380 m_tabstop (context->tabstop)
384 /* Implementation of "end_diagnostic" for SARIF output. */
386 void
387 sarif_builder::end_diagnostic (diagnostic_context *context,
388 diagnostic_info *diagnostic,
389 diagnostic_t orig_diag_kind)
391 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
393 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
394 return;
397 if (m_cur_group_result)
398 /* Nested diagnostic. */
399 m_cur_group_result->on_nested_diagnostic (context,
400 diagnostic,
401 orig_diag_kind,
402 this);
403 else
405 /* Top-level diagnostic. */
406 sarif_result *result_obj
407 = make_result_object (context, diagnostic, orig_diag_kind);
408 m_results_array->append (result_obj);
409 m_cur_group_result = result_obj;
413 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
414 for SARIF output. */
416 void
417 sarif_builder::emit_diagram (diagnostic_context *context,
418 const diagnostic_diagram &diagram)
420 /* We must be within the emission of a top-level diagnostic. */
421 gcc_assert (m_cur_group_result);
422 m_cur_group_result->on_diagram (context, diagram, this);
425 /* Implementation of "end_group_cb" for SARIF output. */
427 void
428 sarif_builder::end_group ()
430 m_cur_group_result = NULL;
433 /* Create a top-level object, and add it to all the results
434 (and other entities) we've seen so far.
436 Flush it all to OUTF. */
438 void
439 sarif_builder::flush_to_file (FILE *outf)
441 m_invocation_obj->prepare_to_flush (m_context);
442 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
443 top->dump (outf);
444 m_invocation_obj = NULL;
445 m_results_array = NULL;
446 fprintf (outf, "\n");
447 delete top;
450 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
451 property (SARIF v2.1.0 section 3.27.10).
453 Return NULL if there isn't one. */
455 static const char *
456 maybe_get_sarif_level (diagnostic_t diag_kind)
458 switch (diag_kind)
460 case DK_WARNING:
461 return "warning";
462 case DK_ERROR:
463 return "error";
464 case DK_NOTE:
465 case DK_ANACHRONISM:
466 return "note";
467 default:
468 return NULL;
472 /* Make a string for DIAG_KIND suitable for use a ruleId
473 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
474 have anything better to use. */
476 static char *
477 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
479 static const char *const diagnostic_kind_text[] = {
480 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
481 #include "diagnostic.def"
482 #undef DEFINE_DIAGNOSTIC_KIND
483 "must-not-happen"
485 /* Lose the trailing ": ". */
486 const char *kind_text = diagnostic_kind_text[diag_kind];
487 size_t len = strlen (kind_text);
488 gcc_assert (len > 2);
489 gcc_assert (kind_text[len - 2] == ':');
490 gcc_assert (kind_text[len - 1] == ' ');
491 char *rstrip = xstrdup (kind_text);
492 rstrip[len - 2] = '\0';
493 return rstrip;
496 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
498 sarif_result *
499 sarif_builder::make_result_object (diagnostic_context *context,
500 diagnostic_info *diagnostic,
501 diagnostic_t orig_diag_kind)
503 sarif_result *result_obj = new sarif_result ();
505 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
506 /* Ideally we'd have an option_name for these. */
507 if (char *option_text
508 = context->option_name (context, diagnostic->option_index,
509 orig_diag_kind, diagnostic->kind))
511 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
512 Set ruleId referencing them. */
513 result_obj->set ("ruleId", new json::string (option_text));
514 if (m_rule_id_set.contains (option_text))
515 free (option_text);
516 else
518 /* This is the first time we've seen this ruleId. */
519 /* Add to set, taking ownership. */
520 m_rule_id_set.add (option_text);
522 json::object *reporting_desc_obj
523 = make_reporting_descriptor_object_for_warning (context,
524 diagnostic,
525 orig_diag_kind,
526 option_text);
527 m_rules_arr->append (reporting_desc_obj);
530 else
532 /* Otherwise, we have an "error" or a stray "note"; use the
533 diagnostic kind as the ruleId, so that the result object at least
534 has a ruleId.
535 We don't bother creating reportingDescriptor objects for these. */
536 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
537 result_obj->set ("ruleId", new json::string (rule_id));
538 free (rule_id);
541 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
542 if (diagnostic->metadata)
543 if (int cwe_id = diagnostic->metadata->get_cwe ())
545 json::array *taxa_arr = new json::array ();
546 json::object *cwe_id_obj
547 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
548 taxa_arr->append (cwe_id_obj);
549 result_obj->set ("taxa", taxa_arr);
552 /* "level" property (SARIF v2.1.0 section 3.27.10). */
553 if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
554 result_obj->set ("level", new json::string (sarif_level));
556 /* "message" property (SARIF v2.1.0 section 3.27.11). */
557 json::object *message_obj
558 = make_message_object (pp_formatted_text (context->printer));
559 pp_clear_output_area (context->printer);
560 result_obj->set ("message", message_obj);
562 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
563 json::array *locations_arr = make_locations_arr (diagnostic);
564 result_obj->set ("locations", locations_arr);
566 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
567 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
569 json::array *code_flows_arr = new json::array ();
570 json::object *code_flow_obj = make_code_flow_object (*path);
571 code_flows_arr->append (code_flow_obj);
572 result_obj->set ("codeFlows", code_flows_arr);
575 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
576 set up later, if any nested diagnostics occur within this diagnostic
577 group. */
579 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
580 const rich_location *richloc = diagnostic->richloc;
581 if (richloc->get_num_fixit_hints ())
583 json::array *fix_arr = new json::array ();
584 json::object *fix_obj = make_fix_object (*richloc);
585 fix_arr->append (fix_obj);
586 result_obj->set ("fixes", fix_arr);
589 return result_obj;
592 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
593 for a GCC warning. */
595 json::object *
596 sarif_builder::
597 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
598 diagnostic_info *diagnostic,
599 diagnostic_t /*orig_diag_kind*/,
600 const char *option_text)
602 json::object *reporting_desc = new json::object ();
604 /* "id" property (SARIF v2.1.0 section 3.49.3). */
605 reporting_desc->set ("id", new json::string (option_text));
607 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
608 it seems redundant compared to "id". */
610 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
611 if (context->get_option_url)
613 char *option_url
614 = context->get_option_url (context, diagnostic->option_index);
615 if (option_url)
617 reporting_desc->set ("helpUri", new json::string (option_url));
618 free (option_url);
622 return reporting_desc;
625 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
626 for CWE_ID, for use within the CWE taxa array. */
628 json::object *
629 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
631 json::object *reporting_desc = new json::object ();
633 /* "id" property (SARIF v2.1.0 section 3.49.3). */
635 pretty_printer pp;
636 pp_printf (&pp, "%i", cwe_id);
637 reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
640 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
642 char *url = get_cwe_url (cwe_id);
643 reporting_desc->set ("helpUri", new json::string (url));
644 free (url);
647 return reporting_desc;
650 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
651 referencing CWE_ID, for use within a result object.
652 Also, add CWE_ID to m_cwe_id_set. */
654 json::object *
655 sarif_builder::
656 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
658 json::object *desc_ref_obj = new json::object ();
660 /* "id" property (SARIF v2.1.0 section 3.52.4). */
662 pretty_printer pp;
663 pp_printf (&pp, "%i", cwe_id);
664 desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
667 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
668 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
669 desc_ref_obj->set ("toolComponent", comp_ref_obj);
671 /* Add CWE_ID to our set. */
672 gcc_assert (cwe_id > 0);
673 m_cwe_id_set.add (cwe_id);
675 return desc_ref_obj;
678 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
679 references the CWE taxonomy. */
681 json::object *
682 sarif_builder::
683 make_tool_component_reference_object_for_cwe () const
685 json::object *comp_ref_obj = new json::object ();
687 /* "name" property (SARIF v2.1.0 section 3.54.3). */
688 comp_ref_obj->set ("name", new json::string ("cwe"));
690 return comp_ref_obj;
693 /* Make an array suitable for use as the "locations" property of:
694 - a "result" object (SARIF v2.1.0 section 3.27.12), or
695 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
697 json::array *
698 sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
700 json::array *locations_arr = new json::array ();
701 const logical_location *logical_loc = NULL;
702 if (m_context->m_client_data_hooks)
703 logical_loc
704 = m_context->m_client_data_hooks->get_current_logical_location ();
706 json::object *location_obj
707 = make_location_object (*diagnostic->richloc, logical_loc);
708 locations_arr->append (location_obj);
709 return locations_arr;
712 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
713 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
715 void
716 sarif_builder::
717 set_any_logical_locs_arr (json::object *location_obj,
718 const logical_location *logical_loc)
720 if (!logical_loc)
721 return;
722 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
723 json::array *location_locs_arr = new json::array ();
724 location_locs_arr->append (logical_loc_obj);
725 location_obj->set ("logicalLocations", location_locs_arr);
728 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
729 and LOGICAL_LOC. */
731 json::object *
732 sarif_builder::make_location_object (const rich_location &rich_loc,
733 const logical_location *logical_loc)
735 json::object *location_obj = new json::object ();
737 /* Get primary loc from RICH_LOC. */
738 location_t loc = rich_loc.get_loc ();
740 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
741 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
742 location_obj->set ("physicalLocation", phs_loc_obj);
744 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
745 set_any_logical_locs_arr (location_obj, logical_loc);
747 return location_obj;
750 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
751 within a diagnostic_path. */
753 json::object *
754 sarif_builder::make_location_object (const diagnostic_event &event)
756 json::object *location_obj = new json::object ();
758 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
759 location_t loc = event.get_location ();
760 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
761 location_obj->set ("physicalLocation", phs_loc_obj);
763 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
764 const logical_location *logical_loc = event.get_logical_location ();
765 set_any_logical_locs_arr (location_obj, logical_loc);
767 /* "message" property (SARIF v2.1.0 section 3.28.5). */
768 label_text ev_desc = event.get_desc (false);
769 json::object *message_obj = make_message_object (ev_desc.get ());
770 location_obj->set ("message", message_obj);
772 return location_obj;
775 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
776 or return NULL;
777 Add any filename to the m_artifacts. */
779 json::object *
780 sarif_builder::maybe_make_physical_location_object (location_t loc)
782 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
783 return NULL;
785 json::object *phys_loc_obj = new json::object ();
787 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
788 json::object *artifact_loc_obj = make_artifact_location_object (loc);
789 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
790 m_filenames.add (LOCATION_FILE (loc));
792 /* "region" property (SARIF v2.1.0 section 3.29.4). */
793 if (json::object *region_obj = maybe_make_region_object (loc))
794 phys_loc_obj->set ("region", region_obj);
796 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
797 if (json::object *context_region_obj
798 = maybe_make_region_object_for_context (loc))
799 phys_loc_obj->set ("contextRegion", context_region_obj);
801 /* Instead, we add artifacts to the run as a whole,
802 with artifact.contents.
803 Could do both, though. */
805 return phys_loc_obj;
808 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
809 or return NULL. */
811 json::object *
812 sarif_builder::make_artifact_location_object (location_t loc)
814 return make_artifact_location_object (LOCATION_FILE (loc));
817 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
818 for when we need to express paths relative to PWD. */
820 #define PWD_PROPERTY_NAME ("PWD")
822 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
823 or return NULL. */
825 json::object *
826 sarif_builder::make_artifact_location_object (const char *filename)
828 json::object *artifact_loc_obj = new json::object ();
830 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
831 artifact_loc_obj->set ("uri", new json::string (filename));
833 if (filename[0] != '/')
835 /* If we have a relative path, set the "uriBaseId" property
836 (SARIF v2.1.0 section 3.4.4). */
837 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
838 m_seen_any_relative_paths = true;
841 return artifact_loc_obj;
844 /* Get the PWD, or NULL, as an absolute file-based URI,
845 adding a trailing forward slash (as required by SARIF v2.1.0
846 section 3.14.14). */
848 static char *
849 make_pwd_uri_str ()
851 /* The prefix of a file-based URI, up to, but not including the path. */
852 #define FILE_PREFIX ("file://")
854 const char *pwd = getpwd ();
855 if (!pwd)
856 return NULL;
857 size_t len = strlen (pwd);
858 if (len == 0 || pwd[len - 1] != '/')
859 return concat (FILE_PREFIX, pwd, "/", NULL);
860 else
862 gcc_assert (pwd[len - 1] == '/');
863 return concat (FILE_PREFIX, pwd, NULL);
867 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
868 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
869 section 3.14.14) when we have any relative paths. */
871 json::object *
872 sarif_builder::make_artifact_location_object_for_pwd () const
874 json::object *artifact_loc_obj = new json::object ();
876 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
877 if (char *pwd = make_pwd_uri_str ())
879 gcc_assert (strlen (pwd) > 0);
880 gcc_assert (pwd[strlen (pwd) - 1] == '/');
881 artifact_loc_obj->set ("uri", new json::string (pwd));
882 free (pwd);
885 return artifact_loc_obj;
888 /* Get the column number within EXPLOC. */
891 sarif_builder::get_sarif_column (expanded_location exploc) const
893 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
894 return location_compute_display_column (exploc, policy);
897 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
898 or return NULL. */
900 json::object *
901 sarif_builder::maybe_make_region_object (location_t loc) const
903 location_t caret_loc = get_pure_location (loc);
905 if (caret_loc <= BUILTINS_LOCATION)
906 return NULL;
908 location_t start_loc = get_start (loc);
909 location_t finish_loc = get_finish (loc);
911 expanded_location exploc_caret = expand_location (caret_loc);
912 expanded_location exploc_start = expand_location (start_loc);
913 expanded_location exploc_finish = expand_location (finish_loc);
915 if (exploc_start.file !=exploc_caret.file)
916 return NULL;
917 if (exploc_finish.file !=exploc_caret.file)
918 return NULL;
920 json::object *region_obj = new json::object ();
922 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
923 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
925 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
926 region_obj->set ("startColumn",
927 new json::integer_number (get_sarif_column (exploc_start)));
929 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
930 if (exploc_finish.line != exploc_start.line)
931 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
933 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
934 This expresses the column immediately beyond the range. */
936 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
937 region_obj->set ("endColumn", new json::integer_number (next_column));
940 return region_obj;
943 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
944 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
946 This is similar to maybe_make_region_object, but ignores column numbers,
947 covering the line(s) as a whole, and including a "snippet" property
948 embedding those source lines, making it easier for consumers to show
949 the pertinent source. */
951 json::object *
952 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
954 location_t caret_loc = get_pure_location (loc);
956 if (caret_loc <= BUILTINS_LOCATION)
957 return NULL;
959 location_t start_loc = get_start (loc);
960 location_t finish_loc = get_finish (loc);
962 expanded_location exploc_caret = expand_location (caret_loc);
963 expanded_location exploc_start = expand_location (start_loc);
964 expanded_location exploc_finish = expand_location (finish_loc);
966 if (exploc_start.file !=exploc_caret.file)
967 return NULL;
968 if (exploc_finish.file !=exploc_caret.file)
969 return NULL;
971 json::object *region_obj = new json::object ();
973 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
974 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
976 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
977 if (exploc_finish.line != exploc_start.line)
978 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
980 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
981 if (json::object *artifact_content_obj
982 = maybe_make_artifact_content_object (exploc_start.file,
983 exploc_start.line,
984 exploc_finish.line))
985 region_obj->set ("snippet", artifact_content_obj);
987 return region_obj;
990 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
991 of HINT (as per SARIF v2.1.0 section 3.57.3). */
993 json::object *
994 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
996 location_t start_loc = hint.get_start_loc ();
997 location_t next_loc = hint.get_next_loc ();
999 expanded_location exploc_start = expand_location (start_loc);
1000 expanded_location exploc_next = expand_location (next_loc);
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 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1008 int start_col = get_sarif_column (exploc_start);
1009 region_obj->set ("startColumn",
1010 new json::integer_number (start_col));
1012 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1013 if (exploc_next.line != exploc_start.line)
1014 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
1016 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1017 This expresses the column immediately beyond the range. */
1018 int next_col = get_sarif_column (exploc_next);
1019 region_obj->set ("endColumn", new json::integer_number (next_col));
1021 return region_obj;
1024 /* Attempt to get a string for a logicalLocation's "kind" property
1025 (SARIF v2.1.0 section 3.33.7).
1026 Return NULL if unknown. */
1028 static const char *
1029 maybe_get_sarif_kind (enum logical_location_kind kind)
1031 switch (kind)
1033 default:
1034 gcc_unreachable ();
1035 case LOGICAL_LOCATION_KIND_UNKNOWN:
1036 return NULL;
1038 case LOGICAL_LOCATION_KIND_FUNCTION:
1039 return "function";
1040 case LOGICAL_LOCATION_KIND_MEMBER:
1041 return "member";
1042 case LOGICAL_LOCATION_KIND_MODULE:
1043 return "module";
1044 case LOGICAL_LOCATION_KIND_NAMESPACE:
1045 return "namespace";
1046 case LOGICAL_LOCATION_KIND_TYPE:
1047 return "type";
1048 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1049 return "returnType";
1050 case LOGICAL_LOCATION_KIND_PARAMETER:
1051 return "parameter";
1052 case LOGICAL_LOCATION_KIND_VARIABLE:
1053 return "variable";
1057 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1058 or return NULL. */
1060 json::object *
1061 sarif_builder::
1062 make_logical_location_object (const logical_location &logical_loc) const
1064 json::object *logical_loc_obj = new json::object ();
1066 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1067 if (const char *short_name = logical_loc.get_short_name ())
1068 logical_loc_obj->set ("name", new json::string (short_name));
1070 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1071 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1072 logical_loc_obj->set ("fullyQualifiedName",
1073 new json::string (name_with_scope));
1075 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1076 if (const char *internal_name = logical_loc.get_internal_name ())
1077 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
1079 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1080 enum logical_location_kind kind = logical_loc.get_kind ();
1081 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1082 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
1084 return logical_loc_obj;
1087 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1089 json::object *
1090 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1092 json::object *code_flow_obj = new json::object ();
1094 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
1095 Currently we only support one thread per result. */
1096 json::array *thread_flows_arr = new json::array ();
1097 json::object *thread_flow_obj = make_thread_flow_object (path);
1098 thread_flows_arr->append (thread_flow_obj);
1099 code_flow_obj->set ("threadFlows", thread_flows_arr);
1101 return code_flow_obj;
1104 /* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
1106 json::object *
1107 sarif_builder::make_thread_flow_object (const diagnostic_path &path)
1109 json::object *thread_flow_obj = new json::object ();
1111 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
1112 json::array *locations_arr = new json::array ();
1113 for (unsigned i = 0; i < path.num_events (); i++)
1115 const diagnostic_event &event = path.get_event (i);
1116 json::object *thread_flow_loc_obj
1117 = make_thread_flow_location_object (event);
1118 locations_arr->append (thread_flow_loc_obj);
1120 thread_flow_obj->set ("locations", locations_arr);
1122 return thread_flow_obj;
1125 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1127 json::object *
1128 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
1130 json::object *thread_flow_loc_obj = new json::object ();
1132 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1133 json::object *location_obj = make_location_object (ev);
1134 thread_flow_loc_obj->set ("location", location_obj);
1136 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1137 diagnostic_event::meaning m = ev.get_meaning ();
1138 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1139 thread_flow_loc_obj->set ("kinds", kinds_arr);
1141 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1142 thread_flow_loc_obj->set ("nestingLevel",
1143 new json::integer_number (ev.get_stack_depth ()));
1145 /* It might be nice to eventually implement the following for -fanalyzer:
1146 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1147 - the "state" property (SARIF v2.1.0 section 3.38.9)
1148 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1150 return thread_flow_loc_obj;
1153 /* If M has any known meaning, make a json array suitable for the "kinds"
1154 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1156 Otherwise, return NULL. */
1158 json::array *
1159 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1161 if (m.m_verb == diagnostic_event::VERB_unknown
1162 && m.m_noun == diagnostic_event::NOUN_unknown
1163 && m.m_property == diagnostic_event::PROPERTY_unknown)
1164 return NULL;
1166 json::array *kinds_arr = new json::array ();
1167 if (const char *verb_str
1168 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1169 kinds_arr->append (new json::string (verb_str));
1170 if (const char *noun_str
1171 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1172 kinds_arr->append (new json::string (noun_str));
1173 if (const char *property_str
1174 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1175 kinds_arr->append (new json::string (property_str));
1176 return kinds_arr;
1179 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1181 json::object *
1182 sarif_builder::make_message_object (const char *msg) const
1184 json::object *message_obj = new json::object ();
1186 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1187 message_obj->set ("text", new json::string (msg));
1189 return message_obj;
1192 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1193 We emit the diagram as a code block within the Markdown part
1194 of the message. */
1196 json::object *
1197 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1198 const diagnostic_diagram &diagram)
1200 json::object *message_obj = new json::object ();
1202 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1203 message_obj->set ("text", new json::string (diagram.get_alt_text ()));
1205 char *saved_prefix = pp_take_prefix (context->printer);
1206 pp_set_prefix (context->printer, NULL);
1208 /* "To produce a code block in Markdown, simply indent every line of
1209 the block by at least 4 spaces or 1 tab."
1210 Here we use 4 spaces. */
1211 diagram.get_canvas ().print_to_pp (context->printer, " ");
1212 pp_set_prefix (context->printer, saved_prefix);
1214 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1215 message_obj->set ("markdown",
1216 new json::string (pp_formatted_text (context->printer)));
1218 pp_clear_output_area (context->printer);
1220 return message_obj;
1223 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1224 for MSG. */
1226 json::object *
1227 sarif_builder::make_multiformat_message_string (const char *msg) const
1229 json::object *message_obj = new json::object ();
1231 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1232 message_obj->set ("text", new json::string (msg));
1234 return message_obj;
1237 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1238 #define SARIF_VERSION "2.1.0"
1240 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1241 Take ownership of INVOCATION_OBJ and RESULTS. */
1243 json::object *
1244 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1245 json::array *results)
1247 json::object *log_obj = new json::object ();
1249 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1250 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1252 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1253 log_obj->set ("version", new json::string (SARIF_VERSION));
1255 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1256 json::array *run_arr = new json::array ();
1257 json::object *run_obj = make_run_object (invocation_obj, results);
1258 run_arr->append (run_obj);
1259 log_obj->set ("runs", run_arr);
1261 return log_obj;
1264 /* Make a run object (SARIF v2.1.0 section 3.14).
1265 Take ownership of INVOCATION_OBJ and RESULTS. */
1267 json::object *
1268 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1269 json::array *results)
1271 json::object *run_obj = new json::object ();
1273 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1274 json::object *tool_obj = make_tool_object ();
1275 run_obj->set ("tool", tool_obj);
1277 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1278 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1279 run_obj->set ("taxonomies", taxonomies_arr);
1281 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1283 json::array *invocations_arr = new json::array ();
1284 invocations_arr->append (invocation_obj);
1285 run_obj->set ("invocations", invocations_arr);
1288 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1289 if (m_seen_any_relative_paths)
1291 json::object *orig_uri_base_ids = new json::object ();
1292 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1293 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1294 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1297 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1298 json::array *artifacts_arr = new json::array ();
1299 for (auto iter : m_filenames)
1301 json::object *artifact_obj = make_artifact_object (iter);
1302 artifacts_arr->append (artifact_obj);
1304 run_obj->set ("artifacts", artifacts_arr);
1306 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1307 run_obj->set ("results", results);
1309 return run_obj;
1312 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1314 json::object *
1315 sarif_builder::make_tool_object () const
1317 json::object *tool_obj = new json::object ();
1319 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1320 json::object *driver_obj = make_driver_tool_component_object ();
1321 tool_obj->set ("driver", driver_obj);
1323 /* Report plugins via the "extensions" property
1324 (SARIF v2.1.0 section 3.18.3). */
1325 if (m_context->m_client_data_hooks)
1326 if (const client_version_info *vinfo
1327 = m_context->m_client_data_hooks->get_any_version_info ())
1329 class my_plugin_visitor : public client_version_info :: plugin_visitor
1331 public:
1332 void on_plugin (const diagnostic_client_plugin_info &p) final override
1334 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1335 for the plugin. */
1336 json::object *plugin_obj = new json::object ();
1337 m_plugin_objs.safe_push (plugin_obj);
1339 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1340 if (const char *short_name = p.get_short_name ())
1341 plugin_obj->set ("name", new json::string (short_name));
1343 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1344 if (const char *full_name = p.get_full_name ())
1345 plugin_obj->set ("fullName", new json::string (full_name));
1347 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1348 if (const char *version = p.get_version ())
1349 plugin_obj->set ("version", new json::string (version));
1351 auto_vec <json::object *> m_plugin_objs;
1353 my_plugin_visitor v;
1354 vinfo->for_each_plugin (v);
1355 if (v.m_plugin_objs.length () > 0)
1357 json::array *extensions_arr = new json::array ();
1358 tool_obj->set ("extensions", extensions_arr);
1359 for (auto iter : v.m_plugin_objs)
1360 extensions_arr->append (iter);
1364 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1365 "extensions" (see toplev.cc: print_version). */
1367 return tool_obj;
1370 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1371 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1373 json::object *
1374 sarif_builder::make_driver_tool_component_object () const
1376 json::object *driver_obj = new json::object ();
1378 if (m_context->m_client_data_hooks)
1379 if (const client_version_info *vinfo
1380 = m_context->m_client_data_hooks->get_any_version_info ())
1382 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1383 if (const char *name = vinfo->get_tool_name ())
1384 driver_obj->set ("name", new json::string (name));
1386 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1387 if (char *full_name = vinfo->maybe_make_full_name ())
1389 driver_obj->set ("fullName", new json::string (full_name));
1390 free (full_name);
1393 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1394 if (const char *version = vinfo->get_version_string ())
1395 driver_obj->set ("version", new json::string (version));
1397 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1398 if (char *version_url = vinfo->maybe_make_version_url ())
1400 driver_obj->set ("informationUri", new json::string (version_url));
1401 free (version_url);
1405 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1406 driver_obj->set ("rules", m_rules_arr);
1408 return driver_obj;
1411 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1412 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1413 toolComponent (3.19) as per 3.19.3, representing the CWE.
1415 Otherwise return NULL. */
1417 json::array *
1418 sarif_builder::maybe_make_taxonomies_array () const
1420 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1421 if (!cwe_obj)
1422 return NULL;
1424 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1425 json::array *taxonomies_arr = new json::array ();
1426 taxonomies_arr->append (cwe_obj);
1427 return taxonomies_arr;
1430 /* If we've seen any CWE IDs, make a toolComponent object
1431 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1432 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1434 Otherwise return NULL. */
1436 json::object *
1437 sarif_builder::maybe_make_cwe_taxonomy_object () const
1439 if (m_cwe_id_set.is_empty ())
1440 return NULL;
1442 json::object *taxonomy_obj = new json::object ();
1444 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1445 taxonomy_obj->set ("name", new json::string ("CWE"));
1447 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1448 taxonomy_obj->set ("version", new json::string ("4.7"));
1450 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1451 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1453 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1454 json::object *short_desc
1455 = make_multiformat_message_string ("The MITRE"
1456 " Common Weakness Enumeration");
1457 taxonomy_obj->set ("shortDescription", short_desc);
1459 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1460 json::array *taxa_arr = new json::array ();
1461 for (auto cwe_id : m_cwe_id_set)
1463 json::object *cwe_taxon
1464 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1465 taxa_arr->append (cwe_taxon);
1467 taxonomy_obj->set ("taxa", taxa_arr);
1469 return taxonomy_obj;
1472 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1474 json::object *
1475 sarif_builder::make_artifact_object (const char *filename)
1477 json::object *artifact_obj = new json::object ();
1479 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1480 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1481 artifact_obj->set ("location", artifact_loc_obj);
1483 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1484 if (json::object *artifact_content_obj
1485 = maybe_make_artifact_content_object (filename))
1486 artifact_obj->set ("contents", artifact_content_obj);
1488 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1489 if (m_context->m_client_data_hooks)
1490 if (const char *source_lang
1491 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1492 (filename))
1493 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1495 return artifact_obj;
1498 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1499 full contents of FILENAME. */
1501 json::object *
1502 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1504 /* Let input.cc handle any charset conversion. */
1505 char_span utf8_content = get_source_file_content (filename);
1506 if (!utf8_content)
1507 return NULL;
1509 /* Don't add it if it's not valid UTF-8. */
1510 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1511 return NULL;
1513 json::object *artifact_content_obj = new json::object ();
1514 artifact_content_obj->set ("text",
1515 new json::string (utf8_content.get_buffer (),
1516 utf8_content.length ()));
1517 return artifact_content_obj;
1520 /* Attempt to read the given range of lines from FILENAME; return
1521 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1523 static char *
1524 get_source_lines (const char *filename,
1525 int start_line,
1526 int end_line)
1528 auto_vec<char> result;
1530 for (int line = start_line; line <= end_line; line++)
1532 char_span line_content = location_get_source_line (filename, line);
1533 if (!line_content.get_buffer ())
1534 return NULL;
1535 result.reserve (line_content.length () + 1);
1536 for (size_t i = 0; i < line_content.length (); i++)
1537 result.quick_push (line_content[i]);
1538 result.quick_push ('\n');
1540 result.safe_push ('\0');
1542 return xstrdup (result.address ());
1545 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1546 run of lines within FILENAME (including the endpoints). */
1548 json::object *
1549 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1550 int start_line,
1551 int end_line) const
1553 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1555 if (!text_utf8)
1556 return NULL;
1558 /* Don't add it if it's not valid UTF-8. */
1559 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1561 free (text_utf8);
1562 return NULL;
1565 json::object *artifact_content_obj = new json::object ();
1566 artifact_content_obj->set ("text", new json::string (text_utf8));
1567 free (text_utf8);
1569 return artifact_content_obj;
1572 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1574 json::object *
1575 sarif_builder::make_fix_object (const rich_location &richloc)
1577 json::object *fix_obj = new json::object ();
1579 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1580 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1581 json::array *artifact_change_arr = new json::array ();
1582 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1583 artifact_change_arr->append (artifact_change_obj);
1584 fix_obj->set ("artifactChanges", artifact_change_arr);
1586 return fix_obj;
1589 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1591 json::object *
1592 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1594 json::object *artifact_change_obj = new json::object ();
1596 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1597 json::object *artifact_location_obj
1598 = make_artifact_location_object (richloc.get_loc ());
1599 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1601 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1602 json::array *replacement_arr = new json::array ();
1603 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1605 const fixit_hint *hint = richloc.get_fixit_hint (i);
1606 json::object *replacement_obj = make_replacement_object (*hint);
1607 replacement_arr->append (replacement_obj);
1609 artifact_change_obj->set ("replacements", replacement_arr);
1611 return artifact_change_obj;
1614 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1616 json::object *
1617 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1619 json::object *replacement_obj = new json::object ();
1621 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1622 json::object *region_obj = make_region_object_for_hint (hint);
1623 replacement_obj->set ("deletedRegion", region_obj);
1625 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1626 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1627 replacement_obj->set ("insertedContent", content_obj);
1629 return replacement_obj;
1632 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1634 json::object *
1635 sarif_builder::make_artifact_content_object (const char *text) const
1637 json::object *content_obj = new json::object ();
1639 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1640 content_obj->set ("text", new json::string (text));
1642 return content_obj;
1645 /* No-op implementation of "begin_diagnostic" for SARIF output. */
1647 static void
1648 sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
1652 /* Implementation of "end_diagnostic" for SARIF output. */
1654 static void
1655 sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
1656 diagnostic_t orig_diag_kind)
1658 gcc_assert (the_builder);
1659 the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
1662 /* No-op implementation of "begin_group_cb" for SARIF output. */
1664 static void
1665 sarif_begin_group (diagnostic_context *)
1669 /* Implementation of "end_group_cb" for SARIF output. */
1671 static void
1672 sarif_end_group (diagnostic_context *)
1674 gcc_assert (the_builder);
1675 the_builder->end_group ();
1678 /* Flush the top-level array to OUTF. */
1680 static void
1681 sarif_flush_to_file (FILE *outf)
1683 gcc_assert (the_builder);
1684 the_builder->flush_to_file (outf);
1685 delete the_builder;
1686 the_builder = NULL;
1689 /* Callback for final cleanup for SARIF output to stderr. */
1691 static void
1692 sarif_stderr_final_cb (diagnostic_context *)
1694 gcc_assert (the_builder);
1695 sarif_flush_to_file (stderr);
1698 static char *sarif_output_base_file_name;
1700 /* Callback for final cleanup for SARIF output to a file. */
1702 static void
1703 sarif_file_final_cb (diagnostic_context *)
1705 char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
1706 FILE *outf = fopen (filename, "w");
1707 if (!outf)
1709 const char *errstr = xstrerror (errno);
1710 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1711 filename, errstr);
1712 free (filename);
1713 return;
1715 gcc_assert (the_builder);
1716 sarif_flush_to_file (outf);
1717 fclose (outf);
1718 free (filename);
1721 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1722 occurs. */
1724 static void
1725 sarif_ice_handler (diagnostic_context *context)
1727 /* Attempt to ensure that a .sarif file is written out. */
1728 diagnostic_finish (context);
1730 /* Print a header for the remaining output to stderr, and
1731 return, attempting to print the usual ICE messages to
1732 stderr. Hopefully this will be helpful to the user in
1733 indicating what's gone wrong (also for DejaGnu, for pruning
1734 those messages). */
1735 fnotice (stderr, "Internal compiler error:\n");
1738 /* Callback for diagnostic_context::m_diagrams.m_emission_cb. */
1740 static void
1741 sarif_emit_diagram (diagnostic_context *context,
1742 const diagnostic_diagram &diagram)
1744 gcc_assert (the_builder);
1745 the_builder->emit_diagram (context, diagram);
1748 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1749 to a file). */
1751 static void
1752 diagnostic_output_format_init_sarif (diagnostic_context *context)
1754 the_builder = new sarif_builder (context);
1756 /* Override callbacks. */
1757 context->begin_diagnostic = sarif_begin_diagnostic;
1758 context->end_diagnostic = sarif_end_diagnostic;
1759 context->begin_group_cb = sarif_begin_group;
1760 context->end_group_cb = sarif_end_group;
1761 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
1762 context->ice_handler_cb = sarif_ice_handler;
1763 context->m_diagrams.m_emission_cb = sarif_emit_diagram;
1765 /* The metadata is handled in SARIF format, rather than as text. */
1766 context->show_cwe = false;
1767 context->show_rules = false;
1769 /* The option is handled in SARIF format, rather than as text. */
1770 context->show_option_requested = false;
1772 /* Don't colorize the text. */
1773 pp_show_color (context->printer) = false;
1776 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1778 void
1779 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1781 diagnostic_output_format_init_sarif (context);
1782 context->final_cb = sarif_stderr_final_cb;
1785 /* Populate CONTEXT in preparation for SARIF output to a file named
1786 BASE_FILE_NAME.sarif. */
1788 void
1789 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1790 const char *base_file_name)
1792 diagnostic_output_format_init_sarif (context);
1793 context->final_cb = sarif_file_final_cb;
1794 sarif_output_base_file_name = xstrdup (base_file_name);