ada: output.adb: fix newline being inserted when buffer is full
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
blobf8fdd586ff025958f542b144261febe9d7c8a0db
1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "diagnostic.h"
26 #include "diagnostic-metadata.h"
27 #include "diagnostic-path.h"
28 #include "json.h"
29 #include "cpplib.h"
30 #include "logical-location.h"
31 #include "diagnostic-client-data-hooks.h"
33 class sarif_builder;
35 /* Subclass of json::object for SARIF result objects
36 (SARIF v2.1.0 section 3.27. */
38 class sarif_result : public json::object
40 public:
41 sarif_result () : m_related_locations_arr (NULL) {}
43 void
44 on_nested_diagnostic (diagnostic_context *context,
45 diagnostic_info *diagnostic,
46 diagnostic_t orig_diag_kind,
47 sarif_builder *builder);
49 private:
50 json::array *m_related_locations_arr;
53 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
54 and -fdiagnostics-format=sarif-file).
56 As diagnostics occur, we build "result" JSON objects, and
57 accumulate state:
58 - which source files are referenced
59 - which warnings are emitted
60 - which CWEs are used
62 At the end of the compile, we use the above to build the full SARIF
63 object tree, adding the result objects to the correct place, and
64 creating objects for the various source files, warnings and CWEs
65 referenced.
67 Implemented:
68 - fix-it hints
69 - CWE metadata
70 - diagnostic groups (see limitations below)
71 - logical locations (e.g. cfun)
73 Known limitations:
74 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
75 but we only capture location and message information from such nested
76 diagnostics (e.g. we ignore fix-it hints on them)
77 - doesn't yet capture command-line arguments: would be run.invocations
78 property (SARIF v2.1.0 section 3.14.11), as invocation objects
79 (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
80 toplev::main, and the response files.
81 - doesn't capture escape_on_output_p
82 - doesn't capture secondary locations within a rich_location
83 (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
84 section 3.27.22)
85 - doesn't capture "artifact.encoding" property
86 (SARIF v2.1.0 section 3.24.9).
87 - doesn't capture hashes of the source files
88 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
89 - doesn't capture the "analysisTarget" property
90 (SARIF v2.1.0 section 3.27.13).
91 - doesn't capture labelled ranges
92 - doesn't capture -Werror cleanly
93 - doesn't capture inlining information (can SARIF handle this?)
94 - doesn't capture macro expansion information (can SARIF handle this?). */
96 class sarif_builder
98 public:
99 sarif_builder (diagnostic_context *context);
101 void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
102 diagnostic_t orig_diag_kind);
104 void end_group ();
106 void flush_to_file (FILE *outf);
108 json::object *make_location_object (const rich_location &rich_loc,
109 const logical_location *logical_loc);
110 json::object *make_message_object (const char *msg) const;
112 private:
113 sarif_result *make_result_object (diagnostic_context *context,
114 diagnostic_info *diagnostic,
115 diagnostic_t orig_diag_kind);
116 void set_any_logical_locs_arr (json::object *location_obj,
117 const logical_location *logical_loc);
118 json::object *make_location_object (const diagnostic_event &event);
119 json::object *
120 make_logical_location_object (const logical_location &logical_loc) const;
121 json::object *make_code_flow_object (const diagnostic_path &path);
122 json::object *make_thread_flow_object (const diagnostic_path &path);
123 json::object *
124 make_thread_flow_location_object (const diagnostic_event &event);
125 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
126 json::object *maybe_make_physical_location_object (location_t loc);
127 json::object *make_artifact_location_object (location_t loc);
128 json::object *make_artifact_location_object (const char *filename);
129 json::object *make_artifact_location_object_for_pwd () const;
130 json::object *maybe_make_region_object (location_t loc) const;
131 json::object *maybe_make_region_object_for_context (location_t loc) const;
132 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
133 json::object *make_multiformat_message_string (const char *msg) const;
134 json::object *make_top_level_object (json::array *results);
135 json::object *make_run_object (json::array *results);
136 json::object *make_tool_object () const;
137 json::object *make_driver_tool_component_object () const;
138 json::array *maybe_make_taxonomies_array () const;
139 json::object *maybe_make_cwe_taxonomy_object () const;
140 json::object *make_tool_component_reference_object_for_cwe () const;
141 json::object *
142 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
143 diagnostic_info *diagnostic,
144 diagnostic_t orig_diag_kind,
145 const char *option_text);
146 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
147 json::object *
148 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
149 json::object *make_artifact_object (const char *filename);
150 json::object *maybe_make_artifact_content_object (const char *filename) const;
151 json::object *maybe_make_artifact_content_object (const char *filename,
152 int start_line,
153 int end_line) const;
154 json::object *make_fix_object (const rich_location &rich_loc);
155 json::object *make_artifact_change_object (const rich_location &richloc);
156 json::object *make_replacement_object (const fixit_hint &hint) const;
157 json::object *make_artifact_content_object (const char *text) const;
158 int get_sarif_column (expanded_location exploc) const;
160 diagnostic_context *m_context;
162 /* The JSON array of pending diagnostics. */
163 json::array *m_results_array;
165 /* The JSON object for the result object (if any) in the current
166 diagnostic group. */
167 sarif_result *m_cur_group_result;
169 hash_set <const char *> m_filenames;
170 bool m_seen_any_relative_paths;
171 hash_set <free_string_hash> m_rule_id_set;
172 json::array *m_rules_arr;
174 /* The set of all CWE IDs we've seen, if any. */
175 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
177 int m_tabstop;
180 static sarif_builder *the_builder;
182 /* class sarif_result : public json::object. */
184 /* Handle secondary diagnostics that occur within a diagnostic group.
185 The closest SARIF seems to have to nested diagnostics is the
186 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
187 so we lazily set this property and populate the array if and when
188 secondary diagnostics occur (such as notes to a warning). */
190 void
191 sarif_result::on_nested_diagnostic (diagnostic_context *context,
192 diagnostic_info *diagnostic,
193 diagnostic_t /*orig_diag_kind*/,
194 sarif_builder *builder)
196 if (!m_related_locations_arr)
198 m_related_locations_arr = new json::array ();
199 set ("relatedLocations", m_related_locations_arr);
202 /* We don't yet generate meaningful logical locations for notes;
203 sometimes these will related to current_function_decl, but
204 often they won't. */
205 json::object *location_obj
206 = builder->make_location_object (*diagnostic->richloc, NULL);
207 json::object *message_obj
208 = builder->make_message_object (pp_formatted_text (context->printer));
209 pp_clear_output_area (context->printer);
210 location_obj->set ("message", message_obj);
212 m_related_locations_arr->append (location_obj);
215 /* class sarif_builder. */
217 /* sarif_builder's ctor. */
219 sarif_builder::sarif_builder (diagnostic_context *context)
220 : m_context (context),
221 m_results_array (new json::array ()),
222 m_cur_group_result (NULL),
223 m_seen_any_relative_paths (false),
224 m_rule_id_set (),
225 m_rules_arr (new json::array ()),
226 m_tabstop (context->tabstop)
230 /* Implementation of "end_diagnostic" for SARIF output. */
232 void
233 sarif_builder::end_diagnostic (diagnostic_context *context,
234 diagnostic_info *diagnostic,
235 diagnostic_t orig_diag_kind)
238 if (m_cur_group_result)
239 /* Nested diagnostic. */
240 m_cur_group_result->on_nested_diagnostic (context,
241 diagnostic,
242 orig_diag_kind,
243 this);
244 else
246 /* Top-level diagnostic. */
247 sarif_result *result_obj
248 = make_result_object (context, diagnostic, orig_diag_kind);
249 m_results_array->append (result_obj);
250 m_cur_group_result = result_obj;
254 /* Implementation of "end_group_cb" for SARIF output. */
256 void
257 sarif_builder::end_group ()
259 m_cur_group_result = NULL;
262 /* Create a top-level object, and add it to all the results
263 (and other entities) we've seen so far.
265 Flush it all to OUTF. */
267 void
268 sarif_builder::flush_to_file (FILE *outf)
270 json::object *top = make_top_level_object (m_results_array);
271 top->dump (outf);
272 m_results_array = NULL;
273 fprintf (outf, "\n");
274 delete top;
277 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
278 property (SARIF v2.1.0 section 3.27.10).
280 Return NULL if there isn't one. */
282 static const char *
283 maybe_get_sarif_level (diagnostic_t diag_kind)
285 switch (diag_kind)
287 case DK_WARNING:
288 return "warning";
289 case DK_ERROR:
290 return "error";
291 case DK_NOTE:
292 case DK_ANACHRONISM:
293 return "note";
294 default:
295 return NULL;
299 /* Make a string for DIAG_KIND suitable for use a ruleId
300 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
301 have anything better to use. */
303 static char *
304 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
306 static const char *const diagnostic_kind_text[] = {
307 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
308 #include "diagnostic.def"
309 #undef DEFINE_DIAGNOSTIC_KIND
310 "must-not-happen"
312 /* Lose the trailing ": ". */
313 const char *kind_text = diagnostic_kind_text[diag_kind];
314 size_t len = strlen (kind_text);
315 gcc_assert (len > 2);
316 gcc_assert (kind_text[len - 2] == ':');
317 gcc_assert (kind_text[len - 1] == ' ');
318 char *rstrip = xstrdup (kind_text);
319 rstrip[len - 2] = '\0';
320 return rstrip;
323 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
325 sarif_result *
326 sarif_builder::make_result_object (diagnostic_context *context,
327 diagnostic_info *diagnostic,
328 diagnostic_t orig_diag_kind)
330 sarif_result *result_obj = new sarif_result ();
332 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
333 /* Ideally we'd have an option_name for these. */
334 if (char *option_text
335 = context->option_name (context, diagnostic->option_index,
336 orig_diag_kind, diagnostic->kind))
338 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
339 Set ruleId referencing them. */
340 result_obj->set ("ruleId", new json::string (option_text));
341 if (m_rule_id_set.contains (option_text))
342 free (option_text);
343 else
345 /* This is the first time we've seen this ruleId. */
346 /* Add to set, taking ownership. */
347 m_rule_id_set.add (option_text);
349 json::object *reporting_desc_obj
350 = make_reporting_descriptor_object_for_warning (context,
351 diagnostic,
352 orig_diag_kind,
353 option_text);
354 m_rules_arr->append (reporting_desc_obj);
357 else
359 /* Otherwise, we have an "error" or a stray "note"; use the
360 diagnostic kind as the ruleId, so that the result object at least
361 has a ruleId.
362 We don't bother creating reportingDescriptor objects for these. */
363 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
364 result_obj->set ("ruleId", new json::string (rule_id));
365 free (rule_id);
368 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
369 if (diagnostic->metadata)
370 if (int cwe_id = diagnostic->metadata->get_cwe ())
372 json::array *taxa_arr = new json::array ();
373 json::object *cwe_id_obj
374 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
375 taxa_arr->append (cwe_id_obj);
376 result_obj->set ("taxa", taxa_arr);
379 /* "level" property (SARIF v2.1.0 section 3.27.10). */
380 if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
381 result_obj->set ("level", new json::string (sarif_level));
383 /* "message" property (SARIF v2.1.0 section 3.27.11). */
384 json::object *message_obj
385 = make_message_object (pp_formatted_text (context->printer));
386 pp_clear_output_area (context->printer);
387 result_obj->set ("message", message_obj);
389 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
390 json::array *locations_arr = new json::array ();
391 const logical_location *logical_loc = NULL;
392 if (m_context->m_client_data_hooks)
393 logical_loc
394 = m_context->m_client_data_hooks->get_current_logical_location ();
396 json::object *location_obj
397 = make_location_object (*diagnostic->richloc, logical_loc);
398 locations_arr->append (location_obj);
399 result_obj->set ("locations", locations_arr);
401 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
402 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
404 json::array *code_flows_arr = new json::array ();
405 json::object *code_flow_obj = make_code_flow_object (*path);
406 code_flows_arr->append (code_flow_obj);
407 result_obj->set ("codeFlows", code_flows_arr);
410 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
411 set up later, if any nested diagnostics occur within this diagnostic
412 group. */
414 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
415 const rich_location *richloc = diagnostic->richloc;
416 if (richloc->get_num_fixit_hints ())
418 json::array *fix_arr = new json::array ();
419 json::object *fix_obj = make_fix_object (*richloc);
420 fix_arr->append (fix_obj);
421 result_obj->set ("fixes", fix_arr);
424 return result_obj;
427 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
428 for a GCC warning. */
430 json::object *
431 sarif_builder::
432 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
433 diagnostic_info *diagnostic,
434 diagnostic_t /*orig_diag_kind*/,
435 const char *option_text)
437 json::object *reporting_desc = new json::object ();
439 /* "id" property (SARIF v2.1.0 section 3.49.3). */
440 reporting_desc->set ("id", new json::string (option_text));
442 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
443 it seems redundant compared to "id". */
445 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
446 if (context->get_option_url)
448 char *option_url
449 = context->get_option_url (context, diagnostic->option_index);
450 if (option_url)
452 reporting_desc->set ("helpUri", new json::string (option_url));
453 free (option_url);
457 return reporting_desc;
460 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
461 for CWE_ID, for use within the CWE taxa array. */
463 json::object *
464 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
466 json::object *reporting_desc = new json::object ();
468 /* "id" property (SARIF v2.1.0 section 3.49.3). */
470 pretty_printer pp;
471 pp_printf (&pp, "%i", cwe_id);
472 reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
475 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
477 char *url = get_cwe_url (cwe_id);
478 reporting_desc->set ("helpUri", new json::string (url));
479 free (url);
482 return reporting_desc;
485 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
486 referencing CWE_ID, for use within a result object.
487 Also, add CWE_ID to m_cwe_id_set. */
489 json::object *
490 sarif_builder::
491 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
493 json::object *desc_ref_obj = new json::object ();
495 /* "id" property (SARIF v2.1.0 section 3.52.4). */
497 pretty_printer pp;
498 pp_printf (&pp, "%i", cwe_id);
499 desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
502 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
503 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
504 desc_ref_obj->set ("toolComponent", comp_ref_obj);
506 /* Add CWE_ID to our set. */
507 gcc_assert (cwe_id > 0);
508 m_cwe_id_set.add (cwe_id);
510 return desc_ref_obj;
513 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
514 references the CWE taxonomy. */
516 json::object *
517 sarif_builder::
518 make_tool_component_reference_object_for_cwe () const
520 json::object *comp_ref_obj = new json::object ();
522 /* "name" property (SARIF v2.1.0 section 3.54.3). */
523 comp_ref_obj->set ("name", new json::string ("cwe"));
525 return comp_ref_obj;
528 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
529 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
531 void
532 sarif_builder::
533 set_any_logical_locs_arr (json::object *location_obj,
534 const logical_location *logical_loc)
536 if (!logical_loc)
537 return;
538 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
539 json::array *location_locs_arr = new json::array ();
540 location_locs_arr->append (logical_loc_obj);
541 location_obj->set ("logicalLocations", location_locs_arr);
544 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
545 and LOGICAL_LOC. */
547 json::object *
548 sarif_builder::make_location_object (const rich_location &rich_loc,
549 const logical_location *logical_loc)
551 json::object *location_obj = new json::object ();
553 /* Get primary loc from RICH_LOC. */
554 location_t loc = rich_loc.get_loc ();
556 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
557 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
558 location_obj->set ("physicalLocation", phs_loc_obj);
560 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
561 set_any_logical_locs_arr (location_obj, logical_loc);
563 return location_obj;
566 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
567 within a diagnostic_path. */
569 json::object *
570 sarif_builder::make_location_object (const diagnostic_event &event)
572 json::object *location_obj = new json::object ();
574 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
575 location_t loc = event.get_location ();
576 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
577 location_obj->set ("physicalLocation", phs_loc_obj);
579 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
580 const logical_location *logical_loc = event.get_logical_location ();
581 set_any_logical_locs_arr (location_obj, logical_loc);
583 /* "message" property (SARIF v2.1.0 section 3.28.5). */
584 label_text ev_desc = event.get_desc (false);
585 json::object *message_obj = make_message_object (ev_desc.get ());
586 location_obj->set ("message", message_obj);
588 return location_obj;
591 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
592 or return NULL;
593 Add any filename to the m_artifacts. */
595 json::object *
596 sarif_builder::maybe_make_physical_location_object (location_t loc)
598 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
599 return NULL;
601 json::object *phys_loc_obj = new json::object ();
603 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
604 json::object *artifact_loc_obj = make_artifact_location_object (loc);
605 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
606 m_filenames.add (LOCATION_FILE (loc));
608 /* "region" property (SARIF v2.1.0 section 3.29.4). */
609 if (json::object *region_obj = maybe_make_region_object (loc))
610 phys_loc_obj->set ("region", region_obj);
612 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
613 if (json::object *context_region_obj
614 = maybe_make_region_object_for_context (loc))
615 phys_loc_obj->set ("contextRegion", context_region_obj);
617 /* Instead, we add artifacts to the run as a whole,
618 with artifact.contents.
619 Could do both, though. */
621 return phys_loc_obj;
624 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
625 or return NULL. */
627 json::object *
628 sarif_builder::make_artifact_location_object (location_t loc)
630 return make_artifact_location_object (LOCATION_FILE (loc));
633 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
634 for when we need to express paths relative to PWD. */
636 #define PWD_PROPERTY_NAME ("PWD")
638 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
639 or return NULL. */
641 json::object *
642 sarif_builder::make_artifact_location_object (const char *filename)
644 json::object *artifact_loc_obj = new json::object ();
646 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
647 artifact_loc_obj->set ("uri", new json::string (filename));
649 if (filename[0] != '/')
651 /* If we have a relative path, set the "uriBaseId" property
652 (SARIF v2.1.0 section 3.4.4). */
653 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
654 m_seen_any_relative_paths = true;
657 return artifact_loc_obj;
660 /* Get the PWD, or NULL, as an absolute file-based URI,
661 adding a trailing forward slash (as required by SARIF v2.1.0
662 section 3.14.14). */
664 static char *
665 make_pwd_uri_str ()
667 /* The prefix of a file-based URI, up to, but not including the path. */
668 #define FILE_PREFIX ("file://")
670 const char *pwd = getpwd ();
671 if (!pwd)
672 return NULL;
673 size_t len = strlen (pwd);
674 if (len == 0 || pwd[len - 1] != '/')
675 return concat (FILE_PREFIX, pwd, "/", NULL);
676 else
678 gcc_assert (pwd[len - 1] == '/');
679 return concat (FILE_PREFIX, pwd, NULL);
683 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
684 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
685 section 3.14.14) when we have any relative paths. */
687 json::object *
688 sarif_builder::make_artifact_location_object_for_pwd () const
690 json::object *artifact_loc_obj = new json::object ();
692 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
693 if (char *pwd = make_pwd_uri_str ())
695 gcc_assert (strlen (pwd) > 0);
696 gcc_assert (pwd[strlen (pwd) - 1] == '/');
697 artifact_loc_obj->set ("uri", new json::string (pwd));
698 free (pwd);
701 return artifact_loc_obj;
704 /* Get the column number within EXPLOC. */
707 sarif_builder::get_sarif_column (expanded_location exploc) const
709 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
710 return location_compute_display_column (exploc, policy);
713 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
714 or return NULL. */
716 json::object *
717 sarif_builder::maybe_make_region_object (location_t loc) const
719 location_t caret_loc = get_pure_location (loc);
721 if (caret_loc <= BUILTINS_LOCATION)
722 return NULL;
724 location_t start_loc = get_start (loc);
725 location_t finish_loc = get_finish (loc);
727 expanded_location exploc_caret = expand_location (caret_loc);
728 expanded_location exploc_start = expand_location (start_loc);
729 expanded_location exploc_finish = expand_location (finish_loc);
731 if (exploc_start.file !=exploc_caret.file)
732 return NULL;
733 if (exploc_finish.file !=exploc_caret.file)
734 return NULL;
736 json::object *region_obj = new json::object ();
738 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
739 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
741 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
742 region_obj->set ("startColumn",
743 new json::integer_number (get_sarif_column (exploc_start)));
745 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
746 if (exploc_finish.line != exploc_start.line)
747 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
749 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
750 This expresses the column immediately beyond the range. */
752 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
753 region_obj->set ("endColumn", new json::integer_number (next_column));
756 return region_obj;
759 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
760 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
762 This is similar to maybe_make_region_object, but ignores column numbers,
763 covering the line(s) as a whole, and including a "snippet" property
764 embedding those source lines, making it easier for consumers to show
765 the pertinent source. */
767 json::object *
768 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
770 location_t caret_loc = get_pure_location (loc);
772 if (caret_loc <= BUILTINS_LOCATION)
773 return NULL;
775 location_t start_loc = get_start (loc);
776 location_t finish_loc = get_finish (loc);
778 expanded_location exploc_caret = expand_location (caret_loc);
779 expanded_location exploc_start = expand_location (start_loc);
780 expanded_location exploc_finish = expand_location (finish_loc);
782 if (exploc_start.file !=exploc_caret.file)
783 return NULL;
784 if (exploc_finish.file !=exploc_caret.file)
785 return NULL;
787 json::object *region_obj = new json::object ();
789 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
790 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
792 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
793 if (exploc_finish.line != exploc_start.line)
794 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
796 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
797 if (json::object *artifact_content_obj
798 = maybe_make_artifact_content_object (exploc_start.file,
799 exploc_start.line,
800 exploc_finish.line))
801 region_obj->set ("snippet", artifact_content_obj);
803 return region_obj;
806 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
807 of HINT (as per SARIF v2.1.0 section 3.57.3). */
809 json::object *
810 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
812 location_t start_loc = hint.get_start_loc ();
813 location_t next_loc = hint.get_next_loc ();
815 expanded_location exploc_start = expand_location (start_loc);
816 expanded_location exploc_next = expand_location (next_loc);
818 json::object *region_obj = new json::object ();
820 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
821 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
823 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
824 int start_col = get_sarif_column (exploc_start);
825 region_obj->set ("startColumn",
826 new json::integer_number (start_col));
828 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
829 if (exploc_next.line != exploc_start.line)
830 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
832 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
833 This expresses the column immediately beyond the range. */
834 int next_col = get_sarif_column (exploc_next);
835 region_obj->set ("endColumn", new json::integer_number (next_col));
837 return region_obj;
840 /* Attempt to get a string for a logicalLocation's "kind" property
841 (SARIF v2.1.0 section 3.33.7).
842 Return NULL if unknown. */
844 static const char *
845 maybe_get_sarif_kind (enum logical_location_kind kind)
847 switch (kind)
849 default:
850 gcc_unreachable ();
851 case LOGICAL_LOCATION_KIND_UNKNOWN:
852 return NULL;
854 case LOGICAL_LOCATION_KIND_FUNCTION:
855 return "function";
856 case LOGICAL_LOCATION_KIND_MEMBER:
857 return "member";
858 case LOGICAL_LOCATION_KIND_MODULE:
859 return "module";
860 case LOGICAL_LOCATION_KIND_NAMESPACE:
861 return "namespace";
862 case LOGICAL_LOCATION_KIND_TYPE:
863 return "type";
864 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
865 return "returnType";
866 case LOGICAL_LOCATION_KIND_PARAMETER:
867 return "parameter";
868 case LOGICAL_LOCATION_KIND_VARIABLE:
869 return "variable";
873 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
874 or return NULL. */
876 json::object *
877 sarif_builder::
878 make_logical_location_object (const logical_location &logical_loc) const
880 json::object *logical_loc_obj = new json::object ();
882 /* "name" property (SARIF v2.1.0 section 3.33.4). */
883 if (const char *short_name = logical_loc.get_short_name ())
884 logical_loc_obj->set ("name", new json::string (short_name));
886 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
887 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
888 logical_loc_obj->set ("fullyQualifiedName",
889 new json::string (name_with_scope));
891 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
892 if (const char *internal_name = logical_loc.get_internal_name ())
893 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
895 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
896 enum logical_location_kind kind = logical_loc.get_kind ();
897 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
898 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
900 return logical_loc_obj;
903 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
905 json::object *
906 sarif_builder::make_code_flow_object (const diagnostic_path &path)
908 json::object *code_flow_obj = new json::object ();
910 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
911 Currently we only support one thread per result. */
912 json::array *thread_flows_arr = new json::array ();
913 json::object *thread_flow_obj = make_thread_flow_object (path);
914 thread_flows_arr->append (thread_flow_obj);
915 code_flow_obj->set ("threadFlows", thread_flows_arr);
917 return code_flow_obj;
920 /* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
922 json::object *
923 sarif_builder::make_thread_flow_object (const diagnostic_path &path)
925 json::object *thread_flow_obj = new json::object ();
927 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
928 json::array *locations_arr = new json::array ();
929 for (unsigned i = 0; i < path.num_events (); i++)
931 const diagnostic_event &event = path.get_event (i);
932 json::object *thread_flow_loc_obj
933 = make_thread_flow_location_object (event);
934 locations_arr->append (thread_flow_loc_obj);
936 thread_flow_obj->set ("locations", locations_arr);
938 return thread_flow_obj;
941 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
943 json::object *
944 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
946 json::object *thread_flow_loc_obj = new json::object ();
948 /* "location" property (SARIF v2.1.0 section 3.38.3). */
949 json::object *location_obj = make_location_object (ev);
950 thread_flow_loc_obj->set ("location", location_obj);
952 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
953 diagnostic_event::meaning m = ev.get_meaning ();
954 if (json::array *kinds_arr = maybe_make_kinds_array (m))
955 thread_flow_loc_obj->set ("kinds", kinds_arr);
957 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
958 thread_flow_loc_obj->set ("nestingLevel",
959 new json::integer_number (ev.get_stack_depth ()));
961 /* It might be nice to eventually implement the following for -fanalyzer:
962 - the "stack" property (SARIF v2.1.0 section 3.38.5)
963 - the "state" property (SARIF v2.1.0 section 3.38.9)
964 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
966 return thread_flow_loc_obj;
969 /* If M has any known meaning, make a json array suitable for the "kinds"
970 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
972 Otherwise, return NULL. */
974 json::array *
975 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
977 if (m.m_verb == diagnostic_event::VERB_unknown
978 && m.m_noun == diagnostic_event::NOUN_unknown
979 && m.m_property == diagnostic_event::PROPERTY_unknown)
980 return NULL;
982 json::array *kinds_arr = new json::array ();
983 if (const char *verb_str
984 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
985 kinds_arr->append (new json::string (verb_str));
986 if (const char *noun_str
987 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
988 kinds_arr->append (new json::string (noun_str));
989 if (const char *property_str
990 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
991 kinds_arr->append (new json::string (property_str));
992 return kinds_arr;
995 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
997 json::object *
998 sarif_builder::make_message_object (const char *msg) const
1000 json::object *message_obj = new json::object ();
1002 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1003 message_obj->set ("text", new json::string (msg));
1005 return message_obj;
1008 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1009 for MSG. */
1011 json::object *
1012 sarif_builder::make_multiformat_message_string (const char *msg) const
1014 json::object *message_obj = new json::object ();
1016 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1017 message_obj->set ("text", new json::string (msg));
1019 return message_obj;
1022 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1023 #define SARIF_VERSION "2.1.0"
1025 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1026 Take ownership of RESULTS. */
1028 json::object *
1029 sarif_builder::make_top_level_object (json::array *results)
1031 json::object *log_obj = new json::object ();
1033 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1034 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1036 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1037 log_obj->set ("version", new json::string (SARIF_VERSION));
1039 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1040 json::array *run_arr = new json::array ();
1041 json::object *run_obj = make_run_object (results);
1042 run_arr->append (run_obj);
1043 log_obj->set ("runs", run_arr);
1045 return log_obj;
1048 /* Make a run object (SARIF v2.1.0 section 3.14).
1049 Take ownership of RESULTS. */
1051 json::object *
1052 sarif_builder::make_run_object (json::array *results)
1054 json::object *run_obj = new json::object ();
1056 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1057 json::object *tool_obj = make_tool_object ();
1058 run_obj->set ("tool", tool_obj);
1060 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1061 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1062 run_obj->set ("taxonomies", taxonomies_arr);
1064 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1065 if (m_seen_any_relative_paths)
1067 json::object *orig_uri_base_ids = new json::object ();
1068 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1069 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1070 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1073 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1074 json::array *artifacts_arr = new json::array ();
1075 for (auto iter : m_filenames)
1077 json::object *artifact_obj = make_artifact_object (iter);
1078 artifacts_arr->append (artifact_obj);
1080 run_obj->set ("artifacts", artifacts_arr);
1082 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1083 run_obj->set ("results", results);
1085 return run_obj;
1088 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1090 json::object *
1091 sarif_builder::make_tool_object () const
1093 json::object *tool_obj = new json::object ();
1095 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1096 json::object *driver_obj = make_driver_tool_component_object ();
1097 tool_obj->set ("driver", driver_obj);
1099 /* Report plugins via the "extensions" property
1100 (SARIF v2.1.0 section 3.18.3). */
1101 if (m_context->m_client_data_hooks)
1102 if (const client_version_info *vinfo
1103 = m_context->m_client_data_hooks->get_any_version_info ())
1105 class my_plugin_visitor : public client_version_info :: plugin_visitor
1107 public:
1108 void on_plugin (const diagnostic_client_plugin_info &p) final override
1110 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1111 for the plugin. */
1112 json::object *plugin_obj = new json::object ();
1113 m_plugin_objs.safe_push (plugin_obj);
1115 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1116 if (const char *short_name = p.get_short_name ())
1117 plugin_obj->set ("name", new json::string (short_name));
1119 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1120 if (const char *full_name = p.get_full_name ())
1121 plugin_obj->set ("fullName", new json::string (full_name));
1123 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1124 if (const char *version = p.get_version ())
1125 plugin_obj->set ("version", new json::string (version));
1127 auto_vec <json::object *> m_plugin_objs;
1129 my_plugin_visitor v;
1130 vinfo->for_each_plugin (v);
1131 if (v.m_plugin_objs.length () > 0)
1133 json::array *extensions_arr = new json::array ();
1134 tool_obj->set ("extensions", extensions_arr);
1135 for (auto iter : v.m_plugin_objs)
1136 extensions_arr->append (iter);
1140 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1141 "extensions" (see toplev.cc: print_version). */
1143 return tool_obj;
1146 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1147 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1149 json::object *
1150 sarif_builder::make_driver_tool_component_object () const
1152 json::object *driver_obj = new json::object ();
1154 if (m_context->m_client_data_hooks)
1155 if (const client_version_info *vinfo
1156 = m_context->m_client_data_hooks->get_any_version_info ())
1158 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1159 if (const char *name = vinfo->get_tool_name ())
1160 driver_obj->set ("name", new json::string (name));
1162 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1163 if (char *full_name = vinfo->maybe_make_full_name ())
1165 driver_obj->set ("fullName", new json::string (full_name));
1166 free (full_name);
1169 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1170 if (const char *version = vinfo->get_version_string ())
1171 driver_obj->set ("version", new json::string (version));
1173 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1174 if (char *version_url = vinfo->maybe_make_version_url ())
1176 driver_obj->set ("informationUri", new json::string (version_url));
1177 free (version_url);
1181 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1182 driver_obj->set ("rules", m_rules_arr);
1184 return driver_obj;
1187 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1188 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1189 toolComponent (3.19) as per 3.19.3, representing the CWE.
1191 Otherwise return NULL. */
1193 json::array *
1194 sarif_builder::maybe_make_taxonomies_array () const
1196 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1197 if (!cwe_obj)
1198 return NULL;
1200 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1201 json::array *taxonomies_arr = new json::array ();
1202 taxonomies_arr->append (cwe_obj);
1203 return taxonomies_arr;
1206 /* If we've seen any CWE IDs, make a toolComponent object
1207 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1208 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1210 Otherwise return NULL. */
1212 json::object *
1213 sarif_builder::maybe_make_cwe_taxonomy_object () const
1215 if (m_cwe_id_set.is_empty ())
1216 return NULL;
1218 json::object *taxonomy_obj = new json::object ();
1220 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1221 taxonomy_obj->set ("name", new json::string ("CWE"));
1223 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1224 taxonomy_obj->set ("version", new json::string ("4.7"));
1226 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1227 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1229 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1230 json::object *short_desc
1231 = make_multiformat_message_string ("The MITRE"
1232 " Common Weakness Enumeration");
1233 taxonomy_obj->set ("shortDescription", short_desc);
1235 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1236 json::array *taxa_arr = new json::array ();
1237 for (auto cwe_id : m_cwe_id_set)
1239 json::object *cwe_taxon
1240 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1241 taxa_arr->append (cwe_taxon);
1243 taxonomy_obj->set ("taxa", taxa_arr);
1245 return taxonomy_obj;
1248 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1250 json::object *
1251 sarif_builder::make_artifact_object (const char *filename)
1253 json::object *artifact_obj = new json::object ();
1255 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1256 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1257 artifact_obj->set ("location", artifact_loc_obj);
1259 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1260 if (json::object *artifact_content_obj
1261 = maybe_make_artifact_content_object (filename))
1262 artifact_obj->set ("contents", artifact_content_obj);
1264 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1265 if (m_context->m_client_data_hooks)
1266 if (const char *source_lang
1267 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1268 (filename))
1269 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1271 return artifact_obj;
1274 /* Read all data from F_IN until EOF.
1275 Return a NULL-terminated buffer containing the data, which must be
1276 freed by the caller.
1277 Return NULL on errors. */
1279 static char *
1280 read_until_eof (FILE *f_in)
1282 /* Read content, allocating a buffer for it. */
1283 char *result = NULL;
1284 size_t total_sz = 0;
1285 size_t alloc_sz = 0;
1286 char buf[4096];
1287 size_t iter_sz_in;
1289 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
1291 gcc_assert (alloc_sz >= total_sz);
1292 size_t old_total_sz = total_sz;
1293 total_sz += iter_sz_in;
1294 /* Allow 1 extra byte for 0-termination. */
1295 if (alloc_sz < (total_sz + 1))
1297 size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
1298 result = (char *)xrealloc (result, new_alloc_sz);
1299 alloc_sz = new_alloc_sz;
1301 memcpy (result + old_total_sz, buf, iter_sz_in);
1304 if (!feof (f_in))
1305 return NULL;
1307 /* 0-terminate the buffer. */
1308 gcc_assert (total_sz < alloc_sz);
1309 result[total_sz] = '\0';
1311 return result;
1314 /* Read all data from FILENAME until EOF.
1315 Return a NULL-terminated buffer containing the data, which must be
1316 freed by the caller.
1317 Return NULL on errors. */
1319 static char *
1320 maybe_read_file (const char *filename)
1322 FILE *f_in = fopen (filename, "r");
1323 if (!f_in)
1324 return NULL;
1325 char *result = read_until_eof (f_in);
1326 fclose (f_in);
1327 return result;
1330 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1331 full contents of FILENAME. */
1333 json::object *
1334 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1336 char *text_utf8 = maybe_read_file (filename);
1337 if (!text_utf8)
1338 return NULL;
1340 json::object *artifact_content_obj = new json::object ();
1341 artifact_content_obj->set ("text", new json::string (text_utf8));
1342 free (text_utf8);
1344 return artifact_content_obj;
1347 /* Attempt to read the given range of lines from FILENAME; return
1348 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1350 static char *
1351 get_source_lines (const char *filename,
1352 int start_line,
1353 int end_line)
1355 auto_vec<char> result;
1357 for (int line = start_line; line <= end_line; line++)
1359 char_span line_content = location_get_source_line (filename, line);
1360 if (!line_content.get_buffer ())
1361 return NULL;
1362 result.reserve (line_content.length () + 1);
1363 for (size_t i = 0; i < line_content.length (); i++)
1364 result.quick_push (line_content[i]);
1365 result.quick_push ('\n');
1367 result.safe_push ('\0');
1369 return xstrdup (result.address ());
1372 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1373 run of lines within FILENAME (including the endpoints). */
1375 json::object *
1376 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1377 int start_line,
1378 int end_line) const
1380 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1382 if (!text_utf8)
1383 return NULL;
1385 json::object *artifact_content_obj = new json::object ();
1386 artifact_content_obj->set ("text", new json::string (text_utf8));
1387 free (text_utf8);
1389 return artifact_content_obj;
1392 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1394 json::object *
1395 sarif_builder::make_fix_object (const rich_location &richloc)
1397 json::object *fix_obj = new json::object ();
1399 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1400 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1401 json::array *artifact_change_arr = new json::array ();
1402 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1403 artifact_change_arr->append (artifact_change_obj);
1404 fix_obj->set ("artifactChanges", artifact_change_arr);
1406 return fix_obj;
1409 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1411 json::object *
1412 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1414 json::object *artifact_change_obj = new json::object ();
1416 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1417 json::object *artifact_location_obj
1418 = make_artifact_location_object (richloc.get_loc ());
1419 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1421 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1422 json::array *replacement_arr = new json::array ();
1423 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1425 const fixit_hint *hint = richloc.get_fixit_hint (i);
1426 json::object *replacement_obj = make_replacement_object (*hint);
1427 replacement_arr->append (replacement_obj);
1429 artifact_change_obj->set ("replacements", replacement_arr);
1431 return artifact_change_obj;
1434 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1436 json::object *
1437 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1439 json::object *replacement_obj = new json::object ();
1441 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1442 json::object *region_obj = make_region_object_for_hint (hint);
1443 replacement_obj->set ("deletedRegion", region_obj);
1445 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1446 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1447 replacement_obj->set ("insertedContent", content_obj);
1449 return replacement_obj;
1452 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1454 json::object *
1455 sarif_builder::make_artifact_content_object (const char *text) const
1457 json::object *content_obj = new json::object ();
1459 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1460 content_obj->set ("text", new json::string (text));
1462 return content_obj;
1465 /* No-op implementation of "begin_diagnostic" for SARIF output. */
1467 static void
1468 sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
1472 /* Implementation of "end_diagnostic" for SARIF output. */
1474 static void
1475 sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
1476 diagnostic_t orig_diag_kind)
1478 gcc_assert (the_builder);
1479 the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
1482 /* No-op implementation of "begin_group_cb" for SARIF output. */
1484 static void
1485 sarif_begin_group (diagnostic_context *)
1489 /* Implementation of "end_group_cb" for SARIF output. */
1491 static void
1492 sarif_end_group (diagnostic_context *)
1494 gcc_assert (the_builder);
1495 the_builder->end_group ();
1498 /* Flush the top-level array to OUTF. */
1500 static void
1501 sarif_flush_to_file (FILE *outf)
1503 gcc_assert (the_builder);
1504 the_builder->flush_to_file (outf);
1505 delete the_builder;
1506 the_builder = NULL;
1509 /* Callback for final cleanup for SARIF output to stderr. */
1511 static void
1512 sarif_stderr_final_cb (diagnostic_context *)
1514 gcc_assert (the_builder);
1515 sarif_flush_to_file (stderr);
1518 static char *sarif_output_base_file_name;
1520 /* Callback for final cleanup for SARIF output to a file. */
1522 static void
1523 sarif_file_final_cb (diagnostic_context *)
1525 char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
1526 FILE *outf = fopen (filename, "w");
1527 if (!outf)
1529 const char *errstr = xstrerror (errno);
1530 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1531 filename, errstr);
1532 free (filename);
1533 return;
1535 gcc_assert (the_builder);
1536 sarif_flush_to_file (outf);
1537 fclose (outf);
1538 free (filename);
1541 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1542 to a file). */
1544 static void
1545 diagnostic_output_format_init_sarif (diagnostic_context *context)
1547 the_builder = new sarif_builder (context);
1549 /* Override callbacks. */
1550 context->begin_diagnostic = sarif_begin_diagnostic;
1551 context->end_diagnostic = sarif_end_diagnostic;
1552 context->begin_group_cb = sarif_begin_group;
1553 context->end_group_cb = sarif_end_group;
1554 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
1556 /* The metadata is handled in SARIF format, rather than as text. */
1557 context->show_cwe = false;
1558 context->show_rules = false;
1560 /* The option is handled in SARIF format, rather than as text. */
1561 context->show_option_requested = false;
1563 /* Don't colorize the text. */
1564 pp_show_color (context->printer) = false;
1567 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1569 void
1570 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1572 diagnostic_output_format_init_sarif (context);
1573 context->final_cb = sarif_stderr_final_cb;
1576 /* Populate CONTEXT in preparation for SARIF output to a file named
1577 BASE_FILE_NAME.sarif. */
1579 void
1580 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1581 const char *base_file_name)
1583 diagnostic_output_format_init_sarif (context);
1584 context->final_cb = sarif_file_final_cb;
1585 sarif_output_base_file_name = xstrdup (base_file_name);