compiler: don't use sink as parameter in method expression thunk
[official-gcc.git] / gcc / diagnostic-format-sarif.cc
bloba7bb9fb639d6dc37fd7559525f25fe26fc3dac91
1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2022 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.m_buffer);
586 location_obj->set ("message", message_obj);
587 ev_desc.maybe_free ();
589 return location_obj;
592 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
593 or return NULL;
594 Add any filename to the m_artifacts. */
596 json::object *
597 sarif_builder::maybe_make_physical_location_object (location_t loc)
599 if (loc <= BUILTINS_LOCATION)
600 return NULL;
602 json::object *phys_loc_obj = new json::object ();
604 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
605 json::object *artifact_loc_obj = make_artifact_location_object (loc);
606 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
607 m_filenames.add (LOCATION_FILE (loc));
609 /* "region" property (SARIF v2.1.0 section 3.29.4). */
610 if (json::object *region_obj = maybe_make_region_object (loc))
611 phys_loc_obj->set ("region", region_obj);
613 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
614 if (json::object *context_region_obj
615 = maybe_make_region_object_for_context (loc))
616 phys_loc_obj->set ("contextRegion", context_region_obj);
618 /* Instead, we add artifacts to the run as a whole,
619 with artifact.contents.
620 Could do both, though. */
622 return phys_loc_obj;
625 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
626 or return NULL. */
628 json::object *
629 sarif_builder::make_artifact_location_object (location_t loc)
631 return make_artifact_location_object (LOCATION_FILE (loc));
634 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
635 for when we need to express paths relative to PWD. */
637 #define PWD_PROPERTY_NAME ("PWD")
639 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
640 or return NULL. */
642 json::object *
643 sarif_builder::make_artifact_location_object (const char *filename)
645 json::object *artifact_loc_obj = new json::object ();
647 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
648 artifact_loc_obj->set ("uri", new json::string (filename));
650 if (filename[0] != '/')
652 /* If we have a relative path, set the "uriBaseId" property
653 (SARIF v2.1.0 section 3.4.4). */
654 artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
655 m_seen_any_relative_paths = true;
658 return artifact_loc_obj;
661 /* Get the PWD, or NULL, as an absolute file-based URI,
662 adding a trailing forward slash (as required by SARIF v2.1.0
663 section 3.14.14). */
665 static char *
666 make_pwd_uri_str ()
668 /* The prefix of a file-based URI, up to, but not including the path. */
669 #define FILE_PREFIX ("file://")
671 const char *pwd = getpwd ();
672 if (!pwd)
673 return NULL;
674 size_t len = strlen (pwd);
675 if (len == 0 || pwd[len - 1] != '/')
676 return concat (FILE_PREFIX, pwd, "/", NULL);
677 else
679 gcc_assert (pwd[len - 1] == '/');
680 return concat (FILE_PREFIX, pwd, NULL);
684 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
685 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
686 section 3.14.14) when we have any relative paths. */
688 json::object *
689 sarif_builder::make_artifact_location_object_for_pwd () const
691 json::object *artifact_loc_obj = new json::object ();
693 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
694 if (char *pwd = make_pwd_uri_str ())
696 gcc_assert (strlen (pwd) > 0);
697 gcc_assert (pwd[strlen (pwd) - 1] == '/');
698 artifact_loc_obj->set ("uri", new json::string (pwd));
699 free (pwd);
702 return artifact_loc_obj;
705 /* Get the column number within EXPLOC. */
708 sarif_builder::get_sarif_column (expanded_location exploc) const
710 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
711 return location_compute_display_column (exploc, policy);
714 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
715 or return NULL. */
717 json::object *
718 sarif_builder::maybe_make_region_object (location_t loc) const
720 location_t caret_loc = get_pure_location (loc);
722 if (caret_loc <= BUILTINS_LOCATION)
723 return NULL;
725 location_t start_loc = get_start (loc);
726 location_t finish_loc = get_finish (loc);
728 expanded_location exploc_caret = expand_location (caret_loc);
729 expanded_location exploc_start = expand_location (start_loc);
730 expanded_location exploc_finish = expand_location (finish_loc);
732 if (exploc_start.file !=exploc_caret.file)
733 return NULL;
734 if (exploc_finish.file !=exploc_caret.file)
735 return NULL;
737 json::object *region_obj = new json::object ();
739 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
740 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
742 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
743 region_obj->set ("startColumn",
744 new json::integer_number (get_sarif_column (exploc_start)));
746 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
747 if (exploc_finish.line != exploc_start.line)
748 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
750 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
751 This expresses the column immediately beyond the range. */
753 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
754 region_obj->set ("endColumn", new json::integer_number (next_column));
757 return region_obj;
760 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
761 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
763 This is similar to maybe_make_region_object, but ignores column numbers,
764 covering the line(s) as a whole, and including a "snippet" property
765 embedding those source lines, making it easier for consumers to show
766 the pertinent source. */
768 json::object *
769 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
771 location_t caret_loc = get_pure_location (loc);
773 if (caret_loc <= BUILTINS_LOCATION)
774 return NULL;
776 location_t start_loc = get_start (loc);
777 location_t finish_loc = get_finish (loc);
779 expanded_location exploc_caret = expand_location (caret_loc);
780 expanded_location exploc_start = expand_location (start_loc);
781 expanded_location exploc_finish = expand_location (finish_loc);
783 if (exploc_start.file !=exploc_caret.file)
784 return NULL;
785 if (exploc_finish.file !=exploc_caret.file)
786 return NULL;
788 json::object *region_obj = new json::object ();
790 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
791 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
793 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
794 if (exploc_finish.line != exploc_start.line)
795 region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
797 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
798 if (json::object *artifact_content_obj
799 = maybe_make_artifact_content_object (exploc_start.file,
800 exploc_start.line,
801 exploc_finish.line))
802 region_obj->set ("snippet", artifact_content_obj);
804 return region_obj;
807 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
808 of HINT (as per SARIF v2.1.0 section 3.57.3). */
810 json::object *
811 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
813 location_t start_loc = hint.get_start_loc ();
814 location_t next_loc = hint.get_next_loc ();
816 expanded_location exploc_start = expand_location (start_loc);
817 expanded_location exploc_next = expand_location (next_loc);
819 json::object *region_obj = new json::object ();
821 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
822 region_obj->set ("startLine", new json::integer_number (exploc_start.line));
824 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
825 int start_col = get_sarif_column (exploc_start);
826 region_obj->set ("startColumn",
827 new json::integer_number (start_col));
829 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
830 if (exploc_next.line != exploc_start.line)
831 region_obj->set ("endLine", new json::integer_number (exploc_next.line));
833 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
834 This expresses the column immediately beyond the range. */
835 int next_col = get_sarif_column (exploc_next);
836 region_obj->set ("endColumn", new json::integer_number (next_col));
838 return region_obj;
841 /* Attempt to get a string for a logicalLocation's "kind" property
842 (SARIF v2.1.0 section 3.33.7).
843 Return NULL if unknown. */
845 static const char *
846 maybe_get_sarif_kind (enum logical_location_kind kind)
848 switch (kind)
850 default:
851 gcc_unreachable ();
852 case LOGICAL_LOCATION_KIND_UNKNOWN:
853 return NULL;
855 case LOGICAL_LOCATION_KIND_FUNCTION:
856 return "function";
857 case LOGICAL_LOCATION_KIND_MEMBER:
858 return "member";
859 case LOGICAL_LOCATION_KIND_MODULE:
860 return "module";
861 case LOGICAL_LOCATION_KIND_NAMESPACE:
862 return "namespace";
863 case LOGICAL_LOCATION_KIND_TYPE:
864 return "type";
865 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
866 return "returnType";
867 case LOGICAL_LOCATION_KIND_PARAMETER:
868 return "parameter";
869 case LOGICAL_LOCATION_KIND_VARIABLE:
870 return "variable";
874 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
875 or return NULL. */
877 json::object *
878 sarif_builder::
879 make_logical_location_object (const logical_location &logical_loc) const
881 json::object *logical_loc_obj = new json::object ();
883 /* "name" property (SARIF v2.1.0 section 3.33.4). */
884 if (const char *short_name = logical_loc.get_short_name ())
885 logical_loc_obj->set ("name", new json::string (short_name));
887 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
888 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
889 logical_loc_obj->set ("fullyQualifiedName",
890 new json::string (name_with_scope));
892 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
893 if (const char *internal_name = logical_loc.get_internal_name ())
894 logical_loc_obj->set ("decoratedName", new json::string (internal_name));
896 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
897 enum logical_location_kind kind = logical_loc.get_kind ();
898 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
899 logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
901 return logical_loc_obj;
904 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
906 json::object *
907 sarif_builder::make_code_flow_object (const diagnostic_path &path)
909 json::object *code_flow_obj = new json::object ();
911 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
912 Currently we only support one thread per result. */
913 json::array *thread_flows_arr = new json::array ();
914 json::object *thread_flow_obj = make_thread_flow_object (path);
915 thread_flows_arr->append (thread_flow_obj);
916 code_flow_obj->set ("threadFlows", thread_flows_arr);
918 return code_flow_obj;
921 /* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
923 json::object *
924 sarif_builder::make_thread_flow_object (const diagnostic_path &path)
926 json::object *thread_flow_obj = new json::object ();
928 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
929 json::array *locations_arr = new json::array ();
930 for (unsigned i = 0; i < path.num_events (); i++)
932 const diagnostic_event &event = path.get_event (i);
933 json::object *thread_flow_loc_obj
934 = make_thread_flow_location_object (event);
935 locations_arr->append (thread_flow_loc_obj);
937 thread_flow_obj->set ("locations", locations_arr);
939 return thread_flow_obj;
942 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
944 json::object *
945 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
947 json::object *thread_flow_loc_obj = new json::object ();
949 /* "location" property (SARIF v2.1.0 section 3.38.3). */
950 json::object *location_obj = make_location_object (ev);
951 thread_flow_loc_obj->set ("location", location_obj);
953 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
954 diagnostic_event::meaning m = ev.get_meaning ();
955 if (json::array *kinds_arr = maybe_make_kinds_array (m))
956 thread_flow_loc_obj->set ("kinds", kinds_arr);
958 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
959 thread_flow_loc_obj->set ("nestingLevel",
960 new json::integer_number (ev.get_stack_depth ()));
962 /* It might be nice to eventually implement the following for -fanalyzer:
963 - the "stack" property (SARIF v2.1.0 section 3.38.5)
964 - the "state" property (SARIF v2.1.0 section 3.38.9)
965 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
967 return thread_flow_loc_obj;
970 /* If M has any known meaning, make a json array suitable for the "kinds"
971 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
973 Otherwise, return NULL. */
975 json::array *
976 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
978 if (m.m_verb == diagnostic_event::VERB_unknown
979 && m.m_noun == diagnostic_event::NOUN_unknown
980 && m.m_property == diagnostic_event::PROPERTY_unknown)
981 return NULL;
983 json::array *kinds_arr = new json::array ();
984 if (const char *verb_str
985 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
986 kinds_arr->append (new json::string (verb_str));
987 if (const char *noun_str
988 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
989 kinds_arr->append (new json::string (noun_str));
990 if (const char *property_str
991 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
992 kinds_arr->append (new json::string (property_str));
993 return kinds_arr;
996 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
998 json::object *
999 sarif_builder::make_message_object (const char *msg) const
1001 json::object *message_obj = new json::object ();
1003 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1004 message_obj->set ("text", new json::string (msg));
1006 return message_obj;
1009 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1010 for MSG. */
1012 json::object *
1013 sarif_builder::make_multiformat_message_string (const char *msg) const
1015 json::object *message_obj = new json::object ();
1017 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1018 message_obj->set ("text", new json::string (msg));
1020 return message_obj;
1023 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1024 #define SARIF_VERSION "2.1.0"
1026 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1027 Take ownership of RESULTS. */
1029 json::object *
1030 sarif_builder::make_top_level_object (json::array *results)
1032 json::object *log_obj = new json::object ();
1034 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1035 log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
1037 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1038 log_obj->set ("version", new json::string (SARIF_VERSION));
1040 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1041 json::array *run_arr = new json::array ();
1042 json::object *run_obj = make_run_object (results);
1043 run_arr->append (run_obj);
1044 log_obj->set ("runs", run_arr);
1046 return log_obj;
1049 /* Make a run object (SARIF v2.1.0 section 3.14).
1050 Take ownership of RESULTS. */
1052 json::object *
1053 sarif_builder::make_run_object (json::array *results)
1055 json::object *run_obj = new json::object ();
1057 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1058 json::object *tool_obj = make_tool_object ();
1059 run_obj->set ("tool", tool_obj);
1061 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1062 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1063 run_obj->set ("taxonomies", taxonomies_arr);
1065 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1066 if (m_seen_any_relative_paths)
1068 json::object *orig_uri_base_ids = new json::object ();
1069 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1070 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1071 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1074 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1075 json::array *artifacts_arr = new json::array ();
1076 for (auto iter : m_filenames)
1078 json::object *artifact_obj = make_artifact_object (iter);
1079 artifacts_arr->append (artifact_obj);
1081 run_obj->set ("artifacts", artifacts_arr);
1083 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1084 run_obj->set ("results", results);
1086 return run_obj;
1089 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1091 json::object *
1092 sarif_builder::make_tool_object () const
1094 json::object *tool_obj = new json::object ();
1096 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1097 json::object *driver_obj = make_driver_tool_component_object ();
1098 tool_obj->set ("driver", driver_obj);
1100 /* Report plugins via the "extensions" property
1101 (SARIF v2.1.0 section 3.18.3). */
1102 if (m_context->m_client_data_hooks)
1103 if (const client_version_info *vinfo
1104 = m_context->m_client_data_hooks->get_any_version_info ())
1106 class my_plugin_visitor : public client_version_info :: plugin_visitor
1108 public:
1109 void on_plugin (const diagnostic_client_plugin_info &p) final override
1111 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1112 for the plugin. */
1113 json::object *plugin_obj = new json::object ();
1114 m_plugin_objs.safe_push (plugin_obj);
1116 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1117 if (const char *short_name = p.get_short_name ())
1118 plugin_obj->set ("name", new json::string (short_name));
1120 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1121 if (const char *full_name = p.get_full_name ())
1122 plugin_obj->set ("fullName", new json::string (full_name));
1124 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1125 if (const char *version = p.get_version ())
1126 plugin_obj->set ("version", new json::string (version));
1128 auto_vec <json::object *> m_plugin_objs;
1130 my_plugin_visitor v;
1131 vinfo->for_each_plugin (v);
1132 if (v.m_plugin_objs.length () > 0)
1134 json::array *extensions_arr = new json::array ();
1135 tool_obj->set ("extensions", extensions_arr);
1136 for (auto iter : v.m_plugin_objs)
1137 extensions_arr->append (iter);
1141 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1142 "extensions" (see toplev.cc: print_version). */
1144 return tool_obj;
1147 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1148 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1150 json::object *
1151 sarif_builder::make_driver_tool_component_object () const
1153 json::object *driver_obj = new json::object ();
1155 if (m_context->m_client_data_hooks)
1156 if (const client_version_info *vinfo
1157 = m_context->m_client_data_hooks->get_any_version_info ())
1159 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1160 if (const char *name = vinfo->get_tool_name ())
1161 driver_obj->set ("name", new json::string (name));
1163 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1164 if (char *full_name = vinfo->maybe_make_full_name ())
1166 driver_obj->set ("fullName", new json::string (full_name));
1167 free (full_name);
1170 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1171 if (const char *version = vinfo->get_version_string ())
1172 driver_obj->set ("version", new json::string (version));
1174 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1175 if (char *version_url = vinfo->maybe_make_version_url ())
1177 driver_obj->set ("informationUri", new json::string (version_url));
1178 free (version_url);
1182 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1183 driver_obj->set ("rules", m_rules_arr);
1185 return driver_obj;
1188 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1189 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1190 toolComponent (3.19) as per 3.19.3, representing the CWE.
1192 Otherwise return NULL. */
1194 json::array *
1195 sarif_builder::maybe_make_taxonomies_array () const
1197 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1198 if (!cwe_obj)
1199 return NULL;
1201 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1202 json::array *taxonomies_arr = new json::array ();
1203 taxonomies_arr->append (cwe_obj);
1204 return taxonomies_arr;
1207 /* If we've seen any CWE IDs, make a toolComponent object
1208 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1209 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1211 Otherwise return NULL. */
1213 json::object *
1214 sarif_builder::maybe_make_cwe_taxonomy_object () const
1216 if (m_cwe_id_set.is_empty ())
1217 return NULL;
1219 json::object *taxonomy_obj = new json::object ();
1221 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1222 taxonomy_obj->set ("name", new json::string ("CWE"));
1224 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1225 taxonomy_obj->set ("version", new json::string ("4.7"));
1227 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1228 taxonomy_obj->set ("organization", new json::string ("MITRE"));
1230 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1231 json::object *short_desc
1232 = make_multiformat_message_string ("The MITRE"
1233 " Common Weakness Enumeration");
1234 taxonomy_obj->set ("shortDescription", short_desc);
1236 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1237 json::array *taxa_arr = new json::array ();
1238 for (auto cwe_id : m_cwe_id_set)
1240 json::object *cwe_taxon
1241 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1242 taxa_arr->append (cwe_taxon);
1244 taxonomy_obj->set ("taxa", taxa_arr);
1246 return taxonomy_obj;
1249 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1251 json::object *
1252 sarif_builder::make_artifact_object (const char *filename)
1254 json::object *artifact_obj = new json::object ();
1256 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1257 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1258 artifact_obj->set ("location", artifact_loc_obj);
1260 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1261 if (json::object *artifact_content_obj
1262 = maybe_make_artifact_content_object (filename))
1263 artifact_obj->set ("contents", artifact_content_obj);
1265 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1266 if (m_context->m_client_data_hooks)
1267 if (const char *source_lang
1268 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1269 (filename))
1270 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1272 return artifact_obj;
1275 /* Read all data from F_IN until EOF.
1276 Return a NULL-terminated buffer containing the data, which must be
1277 freed by the caller.
1278 Return NULL on errors. */
1280 static char *
1281 read_until_eof (FILE *f_in)
1283 /* Read content, allocating a buffer for it. */
1284 char *result = NULL;
1285 size_t total_sz = 0;
1286 size_t alloc_sz = 0;
1287 char buf[4096];
1288 size_t iter_sz_in;
1290 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
1292 gcc_assert (alloc_sz >= total_sz);
1293 size_t old_total_sz = total_sz;
1294 total_sz += iter_sz_in;
1295 /* Allow 1 extra byte for 0-termination. */
1296 if (alloc_sz < (total_sz + 1))
1298 size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
1299 result = (char *)xrealloc (result, new_alloc_sz);
1300 alloc_sz = new_alloc_sz;
1302 memcpy (result + old_total_sz, buf, iter_sz_in);
1305 if (!feof (f_in))
1306 return NULL;
1308 /* 0-terminate the buffer. */
1309 gcc_assert (total_sz < alloc_sz);
1310 result[total_sz] = '\0';
1312 return result;
1315 /* Read all data from FILENAME until EOF.
1316 Return a NULL-terminated buffer containing the data, which must be
1317 freed by the caller.
1318 Return NULL on errors. */
1320 static char *
1321 maybe_read_file (const char *filename)
1323 FILE *f_in = fopen (filename, "r");
1324 if (!f_in)
1325 return NULL;
1326 char *result = read_until_eof (f_in);
1327 fclose (f_in);
1328 return result;
1331 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1332 full contents of FILENAME. */
1334 json::object *
1335 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1337 char *text_utf8 = maybe_read_file (filename);
1338 if (!text_utf8)
1339 return NULL;
1341 json::object *artifact_content_obj = new json::object ();
1342 artifact_content_obj->set ("text", new json::string (text_utf8));
1343 free (text_utf8);
1345 return artifact_content_obj;
1348 /* Attempt to read the given range of lines from FILENAME; return
1349 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1351 static char *
1352 get_source_lines (const char *filename,
1353 int start_line,
1354 int end_line)
1356 auto_vec<char> result;
1358 for (int line = start_line; line <= end_line; line++)
1360 char_span line_content = location_get_source_line (filename, line);
1361 if (!line_content.get_buffer ())
1362 return NULL;
1363 result.reserve (line_content.length () + 1);
1364 for (size_t i = 0; i < line_content.length (); i++)
1365 result.quick_push (line_content[i]);
1366 result.quick_push ('\n');
1368 result.safe_push ('\0');
1370 return xstrdup (result.address ());
1373 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1374 run of lines within FILENAME (including the endpoints). */
1376 json::object *
1377 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1378 int start_line,
1379 int end_line) const
1381 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1383 if (!text_utf8)
1384 return NULL;
1386 json::object *artifact_content_obj = new json::object ();
1387 artifact_content_obj->set ("text", new json::string (text_utf8));
1388 free (text_utf8);
1390 return artifact_content_obj;
1393 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1395 json::object *
1396 sarif_builder::make_fix_object (const rich_location &richloc)
1398 json::object *fix_obj = new json::object ();
1400 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1401 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1402 json::array *artifact_change_arr = new json::array ();
1403 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1404 artifact_change_arr->append (artifact_change_obj);
1405 fix_obj->set ("artifactChanges", artifact_change_arr);
1407 return fix_obj;
1410 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1412 json::object *
1413 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1415 json::object *artifact_change_obj = new json::object ();
1417 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1418 json::object *artifact_location_obj
1419 = make_artifact_location_object (richloc.get_loc ());
1420 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1422 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1423 json::array *replacement_arr = new json::array ();
1424 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1426 const fixit_hint *hint = richloc.get_fixit_hint (i);
1427 json::object *replacement_obj = make_replacement_object (*hint);
1428 replacement_arr->append (replacement_obj);
1430 artifact_change_obj->set ("replacements", replacement_arr);
1432 return artifact_change_obj;
1435 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1437 json::object *
1438 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1440 json::object *replacement_obj = new json::object ();
1442 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1443 json::object *region_obj = make_region_object_for_hint (hint);
1444 replacement_obj->set ("deletedRegion", region_obj);
1446 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1447 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1448 replacement_obj->set ("insertedContent", content_obj);
1450 return replacement_obj;
1453 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1455 json::object *
1456 sarif_builder::make_artifact_content_object (const char *text) const
1458 json::object *content_obj = new json::object ();
1460 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1461 content_obj->set ("text", new json::string (text));
1463 return content_obj;
1466 /* No-op implementation of "begin_diagnostic" for SARIF output. */
1468 static void
1469 sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
1473 /* Implementation of "end_diagnostic" for SARIF output. */
1475 static void
1476 sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
1477 diagnostic_t orig_diag_kind)
1479 gcc_assert (the_builder);
1480 the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
1483 /* No-op implementation of "begin_group_cb" for SARIF output. */
1485 static void
1486 sarif_begin_group (diagnostic_context *)
1490 /* Implementation of "end_group_cb" for SARIF output. */
1492 static void
1493 sarif_end_group (diagnostic_context *)
1495 gcc_assert (the_builder);
1496 the_builder->end_group ();
1499 /* Flush the top-level array to OUTF. */
1501 static void
1502 sarif_flush_to_file (FILE *outf)
1504 gcc_assert (the_builder);
1505 the_builder->flush_to_file (outf);
1506 delete the_builder;
1507 the_builder = NULL;
1510 /* Callback for final cleanup for SARIF output to stderr. */
1512 static void
1513 sarif_stderr_final_cb (diagnostic_context *)
1515 gcc_assert (the_builder);
1516 sarif_flush_to_file (stderr);
1519 static char *sarif_output_base_file_name;
1521 /* Callback for final cleanup for SARIF output to a file. */
1523 static void
1524 sarif_file_final_cb (diagnostic_context *)
1526 char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
1527 FILE *outf = fopen (filename, "w");
1528 if (!outf)
1530 const char *errstr = xstrerror (errno);
1531 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1532 filename, errstr);
1533 free (filename);
1534 return;
1536 gcc_assert (the_builder);
1537 sarif_flush_to_file (outf);
1538 fclose (outf);
1539 free (filename);
1542 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1543 to a file). */
1545 static void
1546 diagnostic_output_format_init_sarif (diagnostic_context *context)
1548 the_builder = new sarif_builder (context);
1550 /* Override callbacks. */
1551 context->begin_diagnostic = sarif_begin_diagnostic;
1552 context->end_diagnostic = sarif_end_diagnostic;
1553 context->begin_group_cb = sarif_begin_group;
1554 context->end_group_cb = sarif_end_group;
1555 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
1557 /* The metadata is handled in SARIF format, rather than as text. */
1558 context->show_cwe = false;
1559 context->show_rules = false;
1561 /* The option is handled in SARIF format, rather than as text. */
1562 context->show_option_requested = false;
1564 /* Don't colorize the text. */
1565 pp_show_color (context->printer) = false;
1568 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1570 void
1571 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1573 diagnostic_output_format_init_sarif (context);
1574 context->final_cb = sarif_stderr_final_cb;
1577 /* Populate CONTEXT in preparation for SARIF output to a file named
1578 BASE_FILE_NAME.sarif. */
1580 void
1581 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1582 const char *base_file_name)
1584 diagnostic_output_format_init_sarif (context);
1585 context->final_cb = sarif_file_final_cb;
1586 sarif_output_base_file_name = xstrdup (base_file_name);