]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/diagnostic-format-sarif.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / diagnostic-format-sarif.cc
1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5 This file is part of GCC.
6
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.
11
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.
16
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/>. */
20
21
22 #include "config.h"
23 #define INCLUDE_VECTOR
24 #include "system.h"
25 #include "coretypes.h"
26 #include "diagnostic.h"
27 #include "diagnostic-metadata.h"
28 #include "diagnostic-path.h"
29 #include "json.h"
30 #include "cpplib.h"
31 #include "logical-location.h"
32 #include "diagnostic-client-data-hooks.h"
33 #include "diagnostic-diagram.h"
34 #include "text-art/canvas.h"
35 #include "diagnostic-format-sarif.h"
36
37 class sarif_builder;
38
39 /* Subclass of json::object for SARIF invocation objects
40 (SARIF v2.1.0 section 3.20). */
41
42 class sarif_invocation : public sarif_object
43 {
44 public:
45 sarif_invocation ()
46 : m_notifications_arr (new json::array ()),
47 m_success (true)
48 {}
49
50 void add_notification_for_ice (diagnostic_context *context,
51 const diagnostic_info &diagnostic,
52 sarif_builder *builder);
53 void prepare_to_flush (diagnostic_context *context);
54
55 private:
56 json::array *m_notifications_arr;
57 bool m_success;
58 };
59
60 /* Subclass of sarif_object for SARIF result objects
61 (SARIF v2.1.0 section 3.27). */
62
63 class sarif_result : public sarif_object
64 {
65 public:
66 sarif_result () : m_related_locations_arr (NULL) {}
67
68 void
69 on_nested_diagnostic (diagnostic_context *context,
70 const diagnostic_info &diagnostic,
71 diagnostic_t orig_diag_kind,
72 sarif_builder *builder);
73 void on_diagram (diagnostic_context *context,
74 const diagnostic_diagram &diagram,
75 sarif_builder *builder);
76
77 private:
78 void add_related_location (json::object *location_obj);
79
80 json::array *m_related_locations_arr;
81 };
82
83 /* Subclass of sarif_object for SARIF notification objects
84 (SARIF v2.1.0 section 3.58).
85
86 This subclass is specifically for notifying when an
87 internal compiler error occurs. */
88
89 class sarif_ice_notification : public sarif_object
90 {
91 public:
92 sarif_ice_notification (diagnostic_context *context,
93 const diagnostic_info &diagnostic,
94 sarif_builder *builder);
95 };
96
97 /* Subclass of sarif_object for SARIF threadFlow objects
98 (SARIF v2.1.0 section 3.37) for PATH. */
99
100 class sarif_thread_flow : public sarif_object
101 {
102 public:
103 sarif_thread_flow (const diagnostic_thread &thread);
104
105 void add_location (json::object *thread_flow_loc_obj)
106 {
107 m_locations_arr->append (thread_flow_loc_obj);
108 }
109
110 private:
111 json::array *m_locations_arr;
112 };
113
114 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
115 and -fdiagnostics-format=sarif-file).
116
117 As diagnostics occur, we build "result" JSON objects, and
118 accumulate state:
119 - which source files are referenced
120 - which warnings are emitted
121 - which CWEs are used
122
123 At the end of the compile, we use the above to build the full SARIF
124 object tree, adding the result objects to the correct place, and
125 creating objects for the various source files, warnings and CWEs
126 referenced.
127
128 Implemented:
129 - fix-it hints
130 - CWE metadata
131 - diagnostic groups (see limitations below)
132 - logical locations (e.g. cfun)
133
134 Known limitations:
135 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
136 but we only capture location and message information from such nested
137 diagnostics (e.g. we ignore fix-it hints on them)
138 - doesn't yet capture command-line arguments: would be run.invocations
139 property (SARIF v2.1.0 section 3.14.11), as invocation objects
140 (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
141 toplev::main, and the response files.
142 - doesn't capture escape_on_output_p
143 - doesn't capture secondary locations within a rich_location
144 (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
145 section 3.27.22)
146 - doesn't capture "artifact.encoding" property
147 (SARIF v2.1.0 section 3.24.9).
148 - doesn't capture hashes of the source files
149 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
150 - doesn't capture the "analysisTarget" property
151 (SARIF v2.1.0 section 3.27.13).
152 - doesn't capture labelled ranges
153 - doesn't capture -Werror cleanly
154 - doesn't capture inlining information (can SARIF handle this?)
155 - doesn't capture macro expansion information (can SARIF handle this?). */
156
157 class sarif_builder
158 {
159 public:
160 sarif_builder (diagnostic_context *context,
161 bool formatted);
162
163 void end_diagnostic (diagnostic_context *context,
164 const diagnostic_info &diagnostic,
165 diagnostic_t orig_diag_kind);
166 void emit_diagram (diagnostic_context *context,
167 const diagnostic_diagram &diagram);
168 void end_group ();
169
170 void flush_to_file (FILE *outf);
171
172 json::array *make_locations_arr (const diagnostic_info &diagnostic);
173 json::object *make_location_object (const rich_location &rich_loc,
174 const logical_location *logical_loc);
175 json::object *make_message_object (const char *msg) const;
176 json::object *
177 make_message_object_for_diagram (diagnostic_context *context,
178 const diagnostic_diagram &diagram);
179
180 private:
181 sarif_result *make_result_object (diagnostic_context *context,
182 const diagnostic_info &diagnostic,
183 diagnostic_t orig_diag_kind);
184 void set_any_logical_locs_arr (json::object *location_obj,
185 const logical_location *logical_loc);
186 json::object *make_location_object (const diagnostic_event &event);
187 json::object *
188 make_logical_location_object (const logical_location &logical_loc) const;
189 json::object *make_code_flow_object (const diagnostic_path &path);
190 json::object *
191 make_thread_flow_location_object (const diagnostic_event &event,
192 int path_event_idx);
193 json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
194 json::object *maybe_make_physical_location_object (location_t loc);
195 json::object *make_artifact_location_object (location_t loc);
196 json::object *make_artifact_location_object (const char *filename);
197 json::object *make_artifact_location_object_for_pwd () const;
198 json::object *maybe_make_region_object (location_t loc) const;
199 json::object *maybe_make_region_object_for_context (location_t loc) const;
200 json::object *make_region_object_for_hint (const fixit_hint &hint) const;
201 json::object *make_multiformat_message_string (const char *msg) const;
202 json::object *make_top_level_object (sarif_invocation *invocation_obj,
203 json::array *results);
204 json::object *make_run_object (sarif_invocation *invocation_obj,
205 json::array *results);
206 json::object *make_tool_object () const;
207 json::object *make_driver_tool_component_object () const;
208 json::array *maybe_make_taxonomies_array () const;
209 json::object *maybe_make_cwe_taxonomy_object () const;
210 json::object *make_tool_component_reference_object_for_cwe () const;
211 json::object *
212 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
213 const diagnostic_info &diagnostic,
214 diagnostic_t orig_diag_kind,
215 const char *option_text);
216 json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
217 json::object *
218 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
219 json::object *make_artifact_object (const char *filename);
220 char *get_source_lines (const char *filename,
221 int start_line,
222 int end_line) const;
223 json::object *maybe_make_artifact_content_object (const char *filename) const;
224 json::object *maybe_make_artifact_content_object (const char *filename,
225 int start_line,
226 int end_line) const;
227 json::object *make_fix_object (const rich_location &rich_loc);
228 json::object *make_artifact_change_object (const rich_location &richloc);
229 json::object *make_replacement_object (const fixit_hint &hint) const;
230 json::object *make_artifact_content_object (const char *text) const;
231 int get_sarif_column (expanded_location exploc) const;
232
233 diagnostic_context *m_context;
234
235 /* The JSON object for the invocation object. */
236 sarif_invocation *m_invocation_obj;
237
238 /* The JSON array of pending diagnostics. */
239 json::array *m_results_array;
240
241 /* The JSON object for the result object (if any) in the current
242 diagnostic group. */
243 sarif_result *m_cur_group_result;
244
245 hash_set <const char *> m_filenames;
246 bool m_seen_any_relative_paths;
247 hash_set <free_string_hash> m_rule_id_set;
248 json::array *m_rules_arr;
249
250 /* The set of all CWE IDs we've seen, if any. */
251 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
252
253 int m_tabstop;
254
255 bool m_formatted;
256 };
257
258 /* class sarif_object : public json::object. */
259
260 sarif_property_bag &
261 sarif_object::get_or_create_properties ()
262 {
263 json::value *properties_val = get ("properties");
264 if (properties_val)
265 {
266 if (properties_val->get_kind () == json::JSON_OBJECT)
267 return *static_cast <sarif_property_bag *> (properties_val);
268 }
269
270 sarif_property_bag *bag = new sarif_property_bag ();
271 set ("properties", bag);
272 return *bag;
273 }
274
275 /* class sarif_invocation : public sarif_object. */
276
277 /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
278 Add an object representing the ICE to the notifications array. */
279
280 void
281 sarif_invocation::add_notification_for_ice (diagnostic_context *context,
282 const diagnostic_info &diagnostic,
283 sarif_builder *builder)
284 {
285 m_success = false;
286
287 sarif_ice_notification *notification_obj
288 = new sarif_ice_notification (context, diagnostic, builder);
289 m_notifications_arr->append (notification_obj);
290 }
291
292 void
293 sarif_invocation::prepare_to_flush (diagnostic_context *context)
294 {
295 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
296 set_bool ("executionSuccessful", m_success);
297
298 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
299 set ("toolExecutionNotifications", m_notifications_arr);
300
301 /* Call client hook, allowing it to create a custom property bag for
302 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
303 if (auto client_data_hooks = context->get_client_data_hooks ())
304 client_data_hooks->add_sarif_invocation_properties (*this);
305 }
306
307 /* class sarif_result : public sarif_object. */
308
309 /* Handle secondary diagnostics that occur within a diagnostic group.
310 The closest SARIF seems to have to nested diagnostics is the
311 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
312 so we lazily set this property and populate the array if and when
313 secondary diagnostics occur (such as notes to a warning). */
314
315 void
316 sarif_result::on_nested_diagnostic (diagnostic_context *context,
317 const diagnostic_info &diagnostic,
318 diagnostic_t /*orig_diag_kind*/,
319 sarif_builder *builder)
320 {
321 /* We don't yet generate meaningful logical locations for notes;
322 sometimes these will related to current_function_decl, but
323 often they won't. */
324 json::object *location_obj
325 = builder->make_location_object (*diagnostic.richloc, NULL);
326 json::object *message_obj
327 = builder->make_message_object (pp_formatted_text (context->printer));
328 pp_clear_output_area (context->printer);
329 location_obj->set ("message", message_obj);
330
331 add_related_location (location_obj);
332 }
333
334 /* Handle diagrams that occur within a diagnostic group.
335 The closest thing in SARIF seems to be to add a location to the
336 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
337 and to put the diagram into the "message" property of that location
338 (SARIF v2.1.0 section 3.28.5). */
339
340 void
341 sarif_result::on_diagram (diagnostic_context *context,
342 const diagnostic_diagram &diagram,
343 sarif_builder *builder)
344 {
345 json::object *location_obj = new json::object ();
346 json::object *message_obj
347 = builder->make_message_object_for_diagram (context, diagram);
348 location_obj->set ("message", message_obj);
349
350 add_related_location (location_obj);
351 }
352
353 /* Add LOCATION_OBJ to this result's "relatedLocations" array,
354 creating it if it doesn't yet exist. */
355
356 void
357 sarif_result::add_related_location (json::object *location_obj)
358 {
359 if (!m_related_locations_arr)
360 {
361 m_related_locations_arr = new json::array ();
362 set ("relatedLocations", m_related_locations_arr);
363 }
364 m_related_locations_arr->append (location_obj);
365 }
366
367 /* class sarif_ice_notification : public sarif_object. */
368
369 /* sarif_ice_notification's ctor.
370 DIAGNOSTIC is an internal compiler error. */
371
372 sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
373 const diagnostic_info &diagnostic,
374 sarif_builder *builder)
375 {
376 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
377 json::array *locations_arr = builder->make_locations_arr (diagnostic);
378 set ("locations", locations_arr);
379
380 /* "message" property (SARIF v2.1.0 section 3.85.5). */
381 json::object *message_obj
382 = builder->make_message_object (pp_formatted_text (context->printer));
383 pp_clear_output_area (context->printer);
384 set ("message", message_obj);
385
386 /* "level" property (SARIF v2.1.0 section 3.58.6). */
387 set_string ("level", "error");
388 }
389
390 /* class sarif_thread_flow : public sarif_object. */
391
392 sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
393 {
394 /* "id" property (SARIF v2.1.0 section 3.37.2). */
395 label_text name (thread.get_name (false));
396 set_string ("id", name.get ());
397
398 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
399 m_locations_arr = new json::array ();
400 set ("locations", m_locations_arr);
401 }
402
403 /* class sarif_builder. */
404
405 /* sarif_builder's ctor. */
406
407 sarif_builder::sarif_builder (diagnostic_context *context,
408 bool formatted)
409 : m_context (context),
410 m_invocation_obj (new sarif_invocation ()),
411 m_results_array (new json::array ()),
412 m_cur_group_result (NULL),
413 m_seen_any_relative_paths (false),
414 m_rule_id_set (),
415 m_rules_arr (new json::array ()),
416 m_tabstop (context->m_tabstop),
417 m_formatted (formatted)
418 {
419 }
420
421 /* Implementation of "end_diagnostic" for SARIF output. */
422
423 void
424 sarif_builder::end_diagnostic (diagnostic_context *context,
425 const diagnostic_info &diagnostic,
426 diagnostic_t orig_diag_kind)
427 {
428 if (diagnostic.kind == DK_ICE || diagnostic.kind == DK_ICE_NOBT)
429 {
430 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
431 return;
432 }
433
434 if (m_cur_group_result)
435 /* Nested diagnostic. */
436 m_cur_group_result->on_nested_diagnostic (context,
437 diagnostic,
438 orig_diag_kind,
439 this);
440 else
441 {
442 /* Top-level diagnostic. */
443 sarif_result *result_obj
444 = make_result_object (context, diagnostic, orig_diag_kind);
445 m_results_array->append (result_obj);
446 m_cur_group_result = result_obj;
447 }
448 }
449
450 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
451 for SARIF output. */
452
453 void
454 sarif_builder::emit_diagram (diagnostic_context *context,
455 const diagnostic_diagram &diagram)
456 {
457 /* We must be within the emission of a top-level diagnostic. */
458 gcc_assert (m_cur_group_result);
459 m_cur_group_result->on_diagram (context, diagram, this);
460 }
461
462 /* Implementation of "end_group_cb" for SARIF output. */
463
464 void
465 sarif_builder::end_group ()
466 {
467 m_cur_group_result = NULL;
468 }
469
470 /* Create a top-level object, and add it to all the results
471 (and other entities) we've seen so far.
472
473 Flush it all to OUTF. */
474
475 void
476 sarif_builder::flush_to_file (FILE *outf)
477 {
478 m_invocation_obj->prepare_to_flush (m_context);
479 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
480 top->dump (outf, m_formatted);
481 m_invocation_obj = NULL;
482 m_results_array = NULL;
483 fprintf (outf, "\n");
484 delete top;
485 }
486
487 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
488 property (SARIF v2.1.0 section 3.27.10).
489
490 Return NULL if there isn't one. */
491
492 static const char *
493 maybe_get_sarif_level (diagnostic_t diag_kind)
494 {
495 switch (diag_kind)
496 {
497 case DK_WARNING:
498 return "warning";
499 case DK_ERROR:
500 return "error";
501 case DK_NOTE:
502 case DK_ANACHRONISM:
503 return "note";
504 default:
505 return NULL;
506 }
507 }
508
509 /* Make a string for DIAG_KIND suitable for use a ruleId
510 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
511 have anything better to use. */
512
513 static char *
514 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
515 {
516 static const char *const diagnostic_kind_text[] = {
517 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
518 #include "diagnostic.def"
519 #undef DEFINE_DIAGNOSTIC_KIND
520 "must-not-happen"
521 };
522 /* Lose the trailing ": ". */
523 const char *kind_text = diagnostic_kind_text[diag_kind];
524 size_t len = strlen (kind_text);
525 gcc_assert (len > 2);
526 gcc_assert (kind_text[len - 2] == ':');
527 gcc_assert (kind_text[len - 1] == ' ');
528 char *rstrip = xstrdup (kind_text);
529 rstrip[len - 2] = '\0';
530 return rstrip;
531 }
532
533 /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
534
535 sarif_result *
536 sarif_builder::make_result_object (diagnostic_context *context,
537 const diagnostic_info &diagnostic,
538 diagnostic_t orig_diag_kind)
539 {
540 sarif_result *result_obj = new sarif_result ();
541
542 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
543 /* Ideally we'd have an option_name for these. */
544 if (char *option_text
545 = context->make_option_name (diagnostic.option_index,
546 orig_diag_kind, diagnostic.kind))
547 {
548 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
549 Set ruleId referencing them. */
550 result_obj->set_string ("ruleId", option_text);
551 if (m_rule_id_set.contains (option_text))
552 free (option_text);
553 else
554 {
555 /* This is the first time we've seen this ruleId. */
556 /* Add to set, taking ownership. */
557 m_rule_id_set.add (option_text);
558
559 json::object *reporting_desc_obj
560 = make_reporting_descriptor_object_for_warning (context,
561 diagnostic,
562 orig_diag_kind,
563 option_text);
564 m_rules_arr->append (reporting_desc_obj);
565 }
566 }
567 else
568 {
569 /* Otherwise, we have an "error" or a stray "note"; use the
570 diagnostic kind as the ruleId, so that the result object at least
571 has a ruleId.
572 We don't bother creating reportingDescriptor objects for these. */
573 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
574 result_obj->set_string ("ruleId", rule_id);
575 free (rule_id);
576 }
577
578 if (diagnostic.metadata)
579 {
580 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
581 if (int cwe_id = diagnostic.metadata->get_cwe ())
582 {
583 json::array *taxa_arr = new json::array ();
584 json::object *cwe_id_obj
585 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
586 taxa_arr->append (cwe_id_obj);
587 result_obj->set ("taxa", taxa_arr);
588 }
589
590 diagnostic.metadata->maybe_add_sarif_properties (*result_obj);
591 }
592
593 /* "level" property (SARIF v2.1.0 section 3.27.10). */
594 if (const char *sarif_level = maybe_get_sarif_level (diagnostic.kind))
595 result_obj->set_string ("level", sarif_level);
596
597 /* "message" property (SARIF v2.1.0 section 3.27.11). */
598 json::object *message_obj
599 = make_message_object (pp_formatted_text (context->printer));
600 pp_clear_output_area (context->printer);
601 result_obj->set ("message", message_obj);
602
603 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
604 json::array *locations_arr = make_locations_arr (diagnostic);
605 result_obj->set ("locations", locations_arr);
606
607 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
608 if (const diagnostic_path *path = diagnostic.richloc->get_path ())
609 {
610 json::array *code_flows_arr = new json::array ();
611 json::object *code_flow_obj = make_code_flow_object (*path);
612 code_flows_arr->append (code_flow_obj);
613 result_obj->set ("codeFlows", code_flows_arr);
614 }
615
616 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
617 set up later, if any nested diagnostics occur within this diagnostic
618 group. */
619
620 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
621 const rich_location *richloc = diagnostic.richloc;
622 if (richloc->get_num_fixit_hints ())
623 {
624 json::array *fix_arr = new json::array ();
625 json::object *fix_obj = make_fix_object (*richloc);
626 fix_arr->append (fix_obj);
627 result_obj->set ("fixes", fix_arr);
628 }
629
630 return result_obj;
631 }
632
633 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
634 for a GCC warning. */
635
636 json::object *
637 sarif_builder::
638 make_reporting_descriptor_object_for_warning (diagnostic_context *context,
639 const diagnostic_info &diagnostic,
640 diagnostic_t /*orig_diag_kind*/,
641 const char *option_text)
642 {
643 json::object *reporting_desc = new json::object ();
644
645 /* "id" property (SARIF v2.1.0 section 3.49.3). */
646 reporting_desc->set_string ("id", option_text);
647
648 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
649 it seems redundant compared to "id". */
650
651 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
652 if (char *option_url = context->make_option_url (diagnostic.option_index))
653 {
654 reporting_desc->set_string ("helpUri", option_url);
655 free (option_url);
656 }
657
658 return reporting_desc;
659 }
660
661 /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
662 for CWE_ID, for use within the CWE taxa array. */
663
664 json::object *
665 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
666 {
667 json::object *reporting_desc = new json::object ();
668
669 /* "id" property (SARIF v2.1.0 section 3.49.3). */
670 {
671 pretty_printer pp;
672 pp_printf (&pp, "%i", cwe_id);
673 reporting_desc->set_string ("id", pp_formatted_text (&pp));
674 }
675
676 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
677 {
678 char *url = get_cwe_url (cwe_id);
679 reporting_desc->set_string ("helpUri", url);
680 free (url);
681 }
682
683 return reporting_desc;
684 }
685
686 /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
687 referencing CWE_ID, for use within a result object.
688 Also, add CWE_ID to m_cwe_id_set. */
689
690 json::object *
691 sarif_builder::
692 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
693 {
694 json::object *desc_ref_obj = new json::object ();
695
696 /* "id" property (SARIF v2.1.0 section 3.52.4). */
697 {
698 pretty_printer pp;
699 pp_printf (&pp, "%i", cwe_id);
700 desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
701 }
702
703 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
704 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
705 desc_ref_obj->set ("toolComponent", comp_ref_obj);
706
707 /* Add CWE_ID to our set. */
708 gcc_assert (cwe_id > 0);
709 m_cwe_id_set.add (cwe_id);
710
711 return desc_ref_obj;
712 }
713
714 /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
715 references the CWE taxonomy. */
716
717 json::object *
718 sarif_builder::
719 make_tool_component_reference_object_for_cwe () const
720 {
721 json::object *comp_ref_obj = new json::object ();
722
723 /* "name" property (SARIF v2.1.0 section 3.54.3). */
724 comp_ref_obj->set_string ("name", "cwe");
725
726 return comp_ref_obj;
727 }
728
729 /* Make an array suitable for use as the "locations" property of:
730 - a "result" object (SARIF v2.1.0 section 3.27.12), or
731 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
732
733 json::array *
734 sarif_builder::make_locations_arr (const diagnostic_info &diagnostic)
735 {
736 json::array *locations_arr = new json::array ();
737 const logical_location *logical_loc = NULL;
738 if (auto client_data_hooks = m_context->get_client_data_hooks ())
739 logical_loc = client_data_hooks->get_current_logical_location ();
740
741 json::object *location_obj
742 = make_location_object (*diagnostic.richloc, logical_loc);
743 locations_arr->append (location_obj);
744 return locations_arr;
745 }
746
747 /* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
748 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
749
750 void
751 sarif_builder::
752 set_any_logical_locs_arr (json::object *location_obj,
753 const logical_location *logical_loc)
754 {
755 if (!logical_loc)
756 return;
757 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
758 json::array *location_locs_arr = new json::array ();
759 location_locs_arr->append (logical_loc_obj);
760 location_obj->set ("logicalLocations", location_locs_arr);
761 }
762
763 /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
764 and LOGICAL_LOC. */
765
766 json::object *
767 sarif_builder::make_location_object (const rich_location &rich_loc,
768 const logical_location *logical_loc)
769 {
770 json::object *location_obj = new json::object ();
771
772 /* Get primary loc from RICH_LOC. */
773 location_t loc = rich_loc.get_loc ();
774
775 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
776 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
777 location_obj->set ("physicalLocation", phs_loc_obj);
778
779 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
780 set_any_logical_locs_arr (location_obj, logical_loc);
781
782 return location_obj;
783 }
784
785 /* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
786 within a diagnostic_path. */
787
788 json::object *
789 sarif_builder::make_location_object (const diagnostic_event &event)
790 {
791 json::object *location_obj = new json::object ();
792
793 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
794 location_t loc = event.get_location ();
795 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
796 location_obj->set ("physicalLocation", phs_loc_obj);
797
798 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
799 const logical_location *logical_loc = event.get_logical_location ();
800 set_any_logical_locs_arr (location_obj, logical_loc);
801
802 /* "message" property (SARIF v2.1.0 section 3.28.5). */
803 label_text ev_desc = event.get_desc (false);
804 json::object *message_obj = make_message_object (ev_desc.get ());
805 location_obj->set ("message", message_obj);
806
807 return location_obj;
808 }
809
810 /* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
811 or return NULL;
812 Add any filename to the m_artifacts. */
813
814 json::object *
815 sarif_builder::maybe_make_physical_location_object (location_t loc)
816 {
817 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
818 return NULL;
819
820 json::object *phys_loc_obj = new json::object ();
821
822 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
823 json::object *artifact_loc_obj = make_artifact_location_object (loc);
824 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
825 m_filenames.add (LOCATION_FILE (loc));
826
827 /* "region" property (SARIF v2.1.0 section 3.29.4). */
828 if (json::object *region_obj = maybe_make_region_object (loc))
829 phys_loc_obj->set ("region", region_obj);
830
831 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
832 if (json::object *context_region_obj
833 = maybe_make_region_object_for_context (loc))
834 phys_loc_obj->set ("contextRegion", context_region_obj);
835
836 /* Instead, we add artifacts to the run as a whole,
837 with artifact.contents.
838 Could do both, though. */
839
840 return phys_loc_obj;
841 }
842
843 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
844 or return NULL. */
845
846 json::object *
847 sarif_builder::make_artifact_location_object (location_t loc)
848 {
849 return make_artifact_location_object (LOCATION_FILE (loc));
850 }
851
852 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
853 for when we need to express paths relative to PWD. */
854
855 #define PWD_PROPERTY_NAME ("PWD")
856
857 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
858 or return NULL. */
859
860 json::object *
861 sarif_builder::make_artifact_location_object (const char *filename)
862 {
863 json::object *artifact_loc_obj = new json::object ();
864
865 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
866 artifact_loc_obj->set_string ("uri", filename);
867
868 if (filename[0] != '/')
869 {
870 /* If we have a relative path, set the "uriBaseId" property
871 (SARIF v2.1.0 section 3.4.4). */
872 artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
873 m_seen_any_relative_paths = true;
874 }
875
876 return artifact_loc_obj;
877 }
878
879 /* Get the PWD, or NULL, as an absolute file-based URI,
880 adding a trailing forward slash (as required by SARIF v2.1.0
881 section 3.14.14). */
882
883 static char *
884 make_pwd_uri_str ()
885 {
886 /* The prefix of a file-based URI, up to, but not including the path. */
887 #define FILE_PREFIX ("file://")
888
889 const char *pwd = getpwd ();
890 if (!pwd)
891 return NULL;
892 size_t len = strlen (pwd);
893 if (len == 0 || pwd[len - 1] != '/')
894 return concat (FILE_PREFIX, pwd, "/", NULL);
895 else
896 {
897 gcc_assert (pwd[len - 1] == '/');
898 return concat (FILE_PREFIX, pwd, NULL);
899 }
900 }
901
902 /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
903 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
904 section 3.14.14) when we have any relative paths. */
905
906 json::object *
907 sarif_builder::make_artifact_location_object_for_pwd () const
908 {
909 json::object *artifact_loc_obj = new json::object ();
910
911 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
912 if (char *pwd = make_pwd_uri_str ())
913 {
914 gcc_assert (strlen (pwd) > 0);
915 gcc_assert (pwd[strlen (pwd) - 1] == '/');
916 artifact_loc_obj->set_string ("uri", pwd);
917 free (pwd);
918 }
919
920 return artifact_loc_obj;
921 }
922
923 /* Get the column number within EXPLOC. */
924
925 int
926 sarif_builder::get_sarif_column (expanded_location exploc) const
927 {
928 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
929 return location_compute_display_column (m_context->get_file_cache (),
930 exploc, policy);
931 }
932
933 /* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
934 or return NULL. */
935
936 json::object *
937 sarif_builder::maybe_make_region_object (location_t loc) const
938 {
939 location_t caret_loc = get_pure_location (loc);
940
941 if (caret_loc <= BUILTINS_LOCATION)
942 return NULL;
943
944 location_t start_loc = get_start (loc);
945 location_t finish_loc = get_finish (loc);
946
947 expanded_location exploc_caret = expand_location (caret_loc);
948 expanded_location exploc_start = expand_location (start_loc);
949 expanded_location exploc_finish = expand_location (finish_loc);
950
951 if (exploc_start.file !=exploc_caret.file)
952 return NULL;
953 if (exploc_finish.file !=exploc_caret.file)
954 return NULL;
955
956 json::object *region_obj = new json::object ();
957
958 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
959 region_obj->set_integer ("startLine", exploc_start.line);
960
961 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
962 region_obj->set_integer ("startColumn", get_sarif_column (exploc_start));
963
964 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
965 if (exploc_finish.line != exploc_start.line)
966 region_obj->set_integer ("endLine", exploc_finish.line);
967
968 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
969 This expresses the column immediately beyond the range. */
970 {
971 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
972 region_obj->set_integer ("endColumn", next_column);
973 }
974
975 return region_obj;
976 }
977
978 /* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
979 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
980
981 This is similar to maybe_make_region_object, but ignores column numbers,
982 covering the line(s) as a whole, and including a "snippet" property
983 embedding those source lines, making it easier for consumers to show
984 the pertinent source. */
985
986 json::object *
987 sarif_builder::maybe_make_region_object_for_context (location_t loc) const
988 {
989 location_t caret_loc = get_pure_location (loc);
990
991 if (caret_loc <= BUILTINS_LOCATION)
992 return NULL;
993
994 location_t start_loc = get_start (loc);
995 location_t finish_loc = get_finish (loc);
996
997 expanded_location exploc_caret = expand_location (caret_loc);
998 expanded_location exploc_start = expand_location (start_loc);
999 expanded_location exploc_finish = expand_location (finish_loc);
1000
1001 if (exploc_start.file !=exploc_caret.file)
1002 return NULL;
1003 if (exploc_finish.file !=exploc_caret.file)
1004 return NULL;
1005
1006 json::object *region_obj = new json::object ();
1007
1008 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1009 region_obj->set_integer ("startLine", exploc_start.line);
1010
1011 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1012 if (exploc_finish.line != exploc_start.line)
1013 region_obj->set_integer ("endLine", exploc_finish.line);
1014
1015 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1016 if (json::object *artifact_content_obj
1017 = maybe_make_artifact_content_object (exploc_start.file,
1018 exploc_start.line,
1019 exploc_finish.line))
1020 region_obj->set ("snippet", artifact_content_obj);
1021
1022 return region_obj;
1023 }
1024
1025 /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1026 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1027
1028 json::object *
1029 sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1030 {
1031 location_t start_loc = hint.get_start_loc ();
1032 location_t next_loc = hint.get_next_loc ();
1033
1034 expanded_location exploc_start = expand_location (start_loc);
1035 expanded_location exploc_next = expand_location (next_loc);
1036
1037 json::object *region_obj = new json::object ();
1038
1039 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1040 region_obj->set_integer ("startLine", exploc_start.line);
1041
1042 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1043 int start_col = get_sarif_column (exploc_start);
1044 region_obj->set_integer ("startColumn", start_col);
1045
1046 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1047 if (exploc_next.line != exploc_start.line)
1048 region_obj->set_integer ("endLine", exploc_next.line);
1049
1050 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1051 This expresses the column immediately beyond the range. */
1052 int next_col = get_sarif_column (exploc_next);
1053 region_obj->set_integer ("endColumn", next_col);
1054
1055 return region_obj;
1056 }
1057
1058 /* Attempt to get a string for a logicalLocation's "kind" property
1059 (SARIF v2.1.0 section 3.33.7).
1060 Return NULL if unknown. */
1061
1062 static const char *
1063 maybe_get_sarif_kind (enum logical_location_kind kind)
1064 {
1065 switch (kind)
1066 {
1067 default:
1068 gcc_unreachable ();
1069 case LOGICAL_LOCATION_KIND_UNKNOWN:
1070 return NULL;
1071
1072 case LOGICAL_LOCATION_KIND_FUNCTION:
1073 return "function";
1074 case LOGICAL_LOCATION_KIND_MEMBER:
1075 return "member";
1076 case LOGICAL_LOCATION_KIND_MODULE:
1077 return "module";
1078 case LOGICAL_LOCATION_KIND_NAMESPACE:
1079 return "namespace";
1080 case LOGICAL_LOCATION_KIND_TYPE:
1081 return "type";
1082 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1083 return "returnType";
1084 case LOGICAL_LOCATION_KIND_PARAMETER:
1085 return "parameter";
1086 case LOGICAL_LOCATION_KIND_VARIABLE:
1087 return "variable";
1088 }
1089 }
1090
1091 /* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1092 or return NULL. */
1093
1094 json::object *
1095 sarif_builder::
1096 make_logical_location_object (const logical_location &logical_loc) const
1097 {
1098 json::object *logical_loc_obj = new json::object ();
1099
1100 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1101 if (const char *short_name = logical_loc.get_short_name ())
1102 logical_loc_obj->set_string ("name", short_name);
1103
1104 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1105 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1106 logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope);
1107
1108 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1109 if (const char *internal_name = logical_loc.get_internal_name ())
1110 logical_loc_obj->set_string ("decoratedName", internal_name);
1111
1112 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1113 enum logical_location_kind kind = logical_loc.get_kind ();
1114 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
1115 logical_loc_obj->set_string ("kind", sarif_kind_str);
1116
1117 return logical_loc_obj;
1118 }
1119
1120 /* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1121
1122 json::object *
1123 sarif_builder::make_code_flow_object (const diagnostic_path &path)
1124 {
1125 json::object *code_flow_obj = new json::object ();
1126
1127 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1128 json::array *thread_flows_arr = new json::array ();
1129
1130 /* Walk the events, consolidating into per-thread threadFlow objects,
1131 using the index with PATH as the overall executionOrder. */
1132 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1133 sarif_thread_flow *> thread_id_map;
1134 for (unsigned i = 0; i < path.num_events (); i++)
1135 {
1136 const diagnostic_event &event = path.get_event (i);
1137 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1138 sarif_thread_flow *thread_flow_obj;
1139
1140 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1141 thread_flow_obj = *slot;
1142 else
1143 {
1144 const diagnostic_thread &thread = path.get_thread (thread_id);
1145 thread_flow_obj = new sarif_thread_flow (thread);
1146 thread_flows_arr->append (thread_flow_obj);
1147 thread_id_map.put (thread_id, thread_flow_obj);
1148 }
1149
1150 /* Add event to thread's threadFlow object. */
1151 json::object *thread_flow_loc_obj
1152 = make_thread_flow_location_object (event, i);
1153 thread_flow_obj->add_location (thread_flow_loc_obj);
1154 }
1155 code_flow_obj->set ("threadFlows", thread_flows_arr);
1156
1157 return code_flow_obj;
1158 }
1159
1160 /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1161
1162 json::object *
1163 sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1164 int path_event_idx)
1165 {
1166 json::object *thread_flow_loc_obj = new json::object ();
1167
1168 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1169 json::object *location_obj = make_location_object (ev);
1170 thread_flow_loc_obj->set ("location", location_obj);
1171
1172 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1173 diagnostic_event::meaning m = ev.get_meaning ();
1174 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1175 thread_flow_loc_obj->set ("kinds", kinds_arr);
1176
1177 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1178 thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
1179
1180 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1181 Offset by 1 to match the human-readable values emitted by %@. */
1182 thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
1183
1184 /* It might be nice to eventually implement the following for -fanalyzer:
1185 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1186 - the "state" property (SARIF v2.1.0 section 3.38.9)
1187 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1188
1189 return thread_flow_loc_obj;
1190 }
1191
1192 /* If M has any known meaning, make a json array suitable for the "kinds"
1193 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1194
1195 Otherwise, return NULL. */
1196
1197 json::array *
1198 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1199 {
1200 if (m.m_verb == diagnostic_event::VERB_unknown
1201 && m.m_noun == diagnostic_event::NOUN_unknown
1202 && m.m_property == diagnostic_event::PROPERTY_unknown)
1203 return NULL;
1204
1205 json::array *kinds_arr = new json::array ();
1206 if (const char *verb_str
1207 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1208 kinds_arr->append (new json::string (verb_str));
1209 if (const char *noun_str
1210 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1211 kinds_arr->append (new json::string (noun_str));
1212 if (const char *property_str
1213 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1214 kinds_arr->append (new json::string (property_str));
1215 return kinds_arr;
1216 }
1217
1218 /* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1219
1220 json::object *
1221 sarif_builder::make_message_object (const char *msg) const
1222 {
1223 json::object *message_obj = new json::object ();
1224
1225 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1226 message_obj->set_string ("text", msg);
1227
1228 return message_obj;
1229 }
1230
1231 /* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1232 We emit the diagram as a code block within the Markdown part
1233 of the message. */
1234
1235 json::object *
1236 sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1237 const diagnostic_diagram &diagram)
1238 {
1239 json::object *message_obj = new json::object ();
1240
1241 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1242 message_obj->set_string ("text", diagram.get_alt_text ());
1243
1244 char *saved_prefix = pp_take_prefix (context->printer);
1245 pp_set_prefix (context->printer, NULL);
1246
1247 /* "To produce a code block in Markdown, simply indent every line of
1248 the block by at least 4 spaces or 1 tab."
1249 Here we use 4 spaces. */
1250 diagram.get_canvas ().print_to_pp (context->printer, " ");
1251 pp_set_prefix (context->printer, saved_prefix);
1252
1253 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1254 message_obj->set_string ("markdown", pp_formatted_text (context->printer));
1255
1256 pp_clear_output_area (context->printer);
1257
1258 return message_obj;
1259 }
1260
1261 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1262 for MSG. */
1263
1264 json::object *
1265 sarif_builder::make_multiformat_message_string (const char *msg) const
1266 {
1267 json::object *message_obj = new json::object ();
1268
1269 /* "text" property (SARIF v2.1.0 section 3.12.3). */
1270 message_obj->set_string ("text", msg);
1271
1272 return message_obj;
1273 }
1274
1275 #define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1276 #define SARIF_VERSION "2.1.0"
1277
1278 /* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
1279 Take ownership of INVOCATION_OBJ and RESULTS. */
1280
1281 json::object *
1282 sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1283 json::array *results)
1284 {
1285 json::object *log_obj = new json::object ();
1286
1287 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
1288 log_obj->set_string ("$schema", SARIF_SCHEMA);
1289
1290 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1291 log_obj->set_string ("version", SARIF_VERSION);
1292
1293 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1294 json::array *run_arr = new json::array ();
1295 json::object *run_obj = make_run_object (invocation_obj, results);
1296 run_arr->append (run_obj);
1297 log_obj->set ("runs", run_arr);
1298
1299 return log_obj;
1300 }
1301
1302 /* Make a run object (SARIF v2.1.0 section 3.14).
1303 Take ownership of INVOCATION_OBJ and RESULTS. */
1304
1305 json::object *
1306 sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1307 json::array *results)
1308 {
1309 json::object *run_obj = new json::object ();
1310
1311 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1312 json::object *tool_obj = make_tool_object ();
1313 run_obj->set ("tool", tool_obj);
1314
1315 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1316 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1317 run_obj->set ("taxonomies", taxonomies_arr);
1318
1319 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1320 {
1321 json::array *invocations_arr = new json::array ();
1322 invocations_arr->append (invocation_obj);
1323 run_obj->set ("invocations", invocations_arr);
1324 }
1325
1326 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1327 if (m_seen_any_relative_paths)
1328 {
1329 json::object *orig_uri_base_ids = new json::object ();
1330 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1331 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1332 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1333 }
1334
1335 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1336 json::array *artifacts_arr = new json::array ();
1337 for (auto iter : m_filenames)
1338 {
1339 json::object *artifact_obj = make_artifact_object (iter);
1340 artifacts_arr->append (artifact_obj);
1341 }
1342 run_obj->set ("artifacts", artifacts_arr);
1343
1344 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1345 run_obj->set ("results", results);
1346
1347 return run_obj;
1348 }
1349
1350 /* Make a tool object (SARIF v2.1.0 section 3.18). */
1351
1352 json::object *
1353 sarif_builder::make_tool_object () const
1354 {
1355 json::object *tool_obj = new json::object ();
1356
1357 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1358 json::object *driver_obj = make_driver_tool_component_object ();
1359 tool_obj->set ("driver", driver_obj);
1360
1361 /* Report plugins via the "extensions" property
1362 (SARIF v2.1.0 section 3.18.3). */
1363 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1364 if (const client_version_info *vinfo
1365 = client_data_hooks->get_any_version_info ())
1366 {
1367 class my_plugin_visitor : public client_version_info :: plugin_visitor
1368 {
1369 public:
1370 void on_plugin (const diagnostic_client_plugin_info &p) final override
1371 {
1372 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1373 for the plugin. */
1374 json::object *plugin_obj = new json::object ();
1375 m_plugin_objs.safe_push (plugin_obj);
1376
1377 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1378 if (const char *short_name = p.get_short_name ())
1379 plugin_obj->set_string ("name", short_name);
1380
1381 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1382 if (const char *full_name = p.get_full_name ())
1383 plugin_obj->set_string ("fullName", full_name);
1384
1385 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1386 if (const char *version = p.get_version ())
1387 plugin_obj->set_string ("version", version);
1388 }
1389 auto_vec <json::object *> m_plugin_objs;
1390 };
1391 my_plugin_visitor v;
1392 vinfo->for_each_plugin (v);
1393 if (v.m_plugin_objs.length () > 0)
1394 {
1395 json::array *extensions_arr = new json::array ();
1396 tool_obj->set ("extensions", extensions_arr);
1397 for (auto iter : v.m_plugin_objs)
1398 extensions_arr->append (iter);
1399 }
1400 }
1401
1402 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1403 "extensions" (see toplev.cc: print_version). */
1404
1405 return tool_obj;
1406 }
1407
1408 /* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1409 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1410
1411 json::object *
1412 sarif_builder::make_driver_tool_component_object () const
1413 {
1414 json::object *driver_obj = new json::object ();
1415
1416 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1417 if (const client_version_info *vinfo
1418 = client_data_hooks->get_any_version_info ())
1419 {
1420 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1421 if (const char *name = vinfo->get_tool_name ())
1422 driver_obj->set_string ("name", name);
1423
1424 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1425 if (char *full_name = vinfo->maybe_make_full_name ())
1426 {
1427 driver_obj->set_string ("fullName", full_name);
1428 free (full_name);
1429 }
1430
1431 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1432 if (const char *version = vinfo->get_version_string ())
1433 driver_obj->set_string ("version", version);
1434
1435 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1436 if (char *version_url = vinfo->maybe_make_version_url ())
1437 {
1438 driver_obj->set_string ("informationUri", version_url);
1439 free (version_url);
1440 }
1441 }
1442
1443 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1444 driver_obj->set ("rules", m_rules_arr);
1445
1446 return driver_obj;
1447 }
1448
1449 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
1450 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1451 toolComponent (3.19) as per 3.19.3, representing the CWE.
1452
1453 Otherwise return NULL. */
1454
1455 json::array *
1456 sarif_builder::maybe_make_taxonomies_array () const
1457 {
1458 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1459 if (!cwe_obj)
1460 return NULL;
1461
1462 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1463 json::array *taxonomies_arr = new json::array ();
1464 taxonomies_arr->append (cwe_obj);
1465 return taxonomies_arr;
1466 }
1467
1468 /* If we've seen any CWE IDs, make a toolComponent object
1469 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1470 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1471
1472 Otherwise return NULL. */
1473
1474 json::object *
1475 sarif_builder::maybe_make_cwe_taxonomy_object () const
1476 {
1477 if (m_cwe_id_set.is_empty ())
1478 return NULL;
1479
1480 json::object *taxonomy_obj = new json::object ();
1481
1482 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1483 taxonomy_obj->set_string ("name", "CWE");
1484
1485 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1486 taxonomy_obj->set_string ("version", "4.7");
1487
1488 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1489 taxonomy_obj->set_string ("organization", "MITRE");
1490
1491 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1492 json::object *short_desc
1493 = make_multiformat_message_string ("The MITRE"
1494 " Common Weakness Enumeration");
1495 taxonomy_obj->set ("shortDescription", short_desc);
1496
1497 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1498 json::array *taxa_arr = new json::array ();
1499 for (auto cwe_id : m_cwe_id_set)
1500 {
1501 json::object *cwe_taxon
1502 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1503 taxa_arr->append (cwe_taxon);
1504 }
1505 taxonomy_obj->set ("taxa", taxa_arr);
1506
1507 return taxonomy_obj;
1508 }
1509
1510 /* Make an artifact object (SARIF v2.1.0 section 3.24). */
1511
1512 json::object *
1513 sarif_builder::make_artifact_object (const char *filename)
1514 {
1515 json::object *artifact_obj = new json::object ();
1516
1517 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1518 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1519 artifact_obj->set ("location", artifact_loc_obj);
1520
1521 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1522 if (json::object *artifact_content_obj
1523 = maybe_make_artifact_content_object (filename))
1524 artifact_obj->set ("contents", artifact_content_obj);
1525
1526 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1527 if (auto client_data_hooks = m_context->get_client_data_hooks ())
1528 if (const char *source_lang
1529 = client_data_hooks->maybe_get_sarif_source_language (filename))
1530 artifact_obj->set_string ("sourceLanguage", source_lang);
1531
1532 return artifact_obj;
1533 }
1534
1535 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1536 full contents of FILENAME. */
1537
1538 json::object *
1539 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1540 {
1541 /* Let input.cc handle any charset conversion. */
1542 char_span utf8_content
1543 = m_context->get_file_cache ().get_source_file_content (filename);
1544 if (!utf8_content)
1545 return NULL;
1546
1547 /* Don't add it if it's not valid UTF-8. */
1548 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1549 return NULL;
1550
1551 json::object *artifact_content_obj = new json::object ();
1552 artifact_content_obj->set ("text",
1553 new json::string (utf8_content.get_buffer (),
1554 utf8_content.length ()));
1555 return artifact_content_obj;
1556 }
1557
1558 /* Attempt to read the given range of lines from FILENAME; return
1559 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1560
1561 char *
1562 sarif_builder::get_source_lines (const char *filename,
1563 int start_line,
1564 int end_line) const
1565 {
1566 auto_vec<char> result;
1567
1568 for (int line = start_line; line <= end_line; line++)
1569 {
1570 char_span line_content
1571 = m_context->get_file_cache ().get_source_line (filename, line);
1572 if (!line_content.get_buffer ())
1573 return NULL;
1574 result.reserve (line_content.length () + 1);
1575 for (size_t i = 0; i < line_content.length (); i++)
1576 result.quick_push (line_content[i]);
1577 result.quick_push ('\n');
1578 }
1579 result.safe_push ('\0');
1580
1581 return xstrdup (result.address ());
1582 }
1583
1584 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1585 run of lines within FILENAME (including the endpoints). */
1586
1587 json::object *
1588 sarif_builder::maybe_make_artifact_content_object (const char *filename,
1589 int start_line,
1590 int end_line) const
1591 {
1592 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1593
1594 if (!text_utf8)
1595 return NULL;
1596
1597 /* Don't add it if it's not valid UTF-8. */
1598 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1599 {
1600 free (text_utf8);
1601 return NULL;
1602 }
1603
1604 json::object *artifact_content_obj = new json::object ();
1605 artifact_content_obj->set_string ("text", text_utf8);
1606 free (text_utf8);
1607
1608 return artifact_content_obj;
1609 }
1610
1611 /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1612
1613 json::object *
1614 sarif_builder::make_fix_object (const rich_location &richloc)
1615 {
1616 json::object *fix_obj = new json::object ();
1617
1618 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1619 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1620 json::array *artifact_change_arr = new json::array ();
1621 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1622 artifact_change_arr->append (artifact_change_obj);
1623 fix_obj->set ("artifactChanges", artifact_change_arr);
1624
1625 return fix_obj;
1626 }
1627
1628 /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1629
1630 json::object *
1631 sarif_builder::make_artifact_change_object (const rich_location &richloc)
1632 {
1633 json::object *artifact_change_obj = new json::object ();
1634
1635 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1636 json::object *artifact_location_obj
1637 = make_artifact_location_object (richloc.get_loc ());
1638 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1639
1640 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1641 json::array *replacement_arr = new json::array ();
1642 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1643 {
1644 const fixit_hint *hint = richloc.get_fixit_hint (i);
1645 json::object *replacement_obj = make_replacement_object (*hint);
1646 replacement_arr->append (replacement_obj);
1647 }
1648 artifact_change_obj->set ("replacements", replacement_arr);
1649
1650 return artifact_change_obj;
1651 }
1652
1653 /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1654
1655 json::object *
1656 sarif_builder::make_replacement_object (const fixit_hint &hint) const
1657 {
1658 json::object *replacement_obj = new json::object ();
1659
1660 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1661 json::object *region_obj = make_region_object_for_hint (hint);
1662 replacement_obj->set ("deletedRegion", region_obj);
1663
1664 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1665 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1666 replacement_obj->set ("insertedContent", content_obj);
1667
1668 return replacement_obj;
1669 }
1670
1671 /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1672
1673 json::object *
1674 sarif_builder::make_artifact_content_object (const char *text) const
1675 {
1676 json::object *content_obj = new json::object ();
1677
1678 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1679 content_obj->set_string ("text", text);
1680
1681 return content_obj;
1682 }
1683
1684 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
1685 occurs. */
1686
1687 static void
1688 sarif_ice_handler (diagnostic_context *context)
1689 {
1690 /* Attempt to ensure that a .sarif file is written out. */
1691 diagnostic_finish (context);
1692
1693 /* Print a header for the remaining output to stderr, and
1694 return, attempting to print the usual ICE messages to
1695 stderr. Hopefully this will be helpful to the user in
1696 indicating what's gone wrong (also for DejaGnu, for pruning
1697 those messages). */
1698 fnotice (stderr, "Internal compiler error:\n");
1699 }
1700
1701 class sarif_output_format : public diagnostic_output_format
1702 {
1703 public:
1704 void on_begin_group () final override
1705 {
1706 /* No-op, */
1707 }
1708 void on_end_group () final override
1709 {
1710 m_builder.end_group ();
1711 }
1712 void
1713 on_begin_diagnostic (const diagnostic_info &) final override
1714 {
1715 /* No-op, */
1716 }
1717 void
1718 on_end_diagnostic (const diagnostic_info &diagnostic,
1719 diagnostic_t orig_diag_kind) final override
1720 {
1721 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1722 }
1723 void on_diagram (const diagnostic_diagram &diagram) final override
1724 {
1725 m_builder.emit_diagram (&m_context, diagram);
1726 }
1727
1728 protected:
1729 sarif_output_format (diagnostic_context &context,
1730 bool formatted)
1731 : diagnostic_output_format (context),
1732 m_builder (&context, formatted)
1733 {}
1734
1735 sarif_builder m_builder;
1736 };
1737
1738 class sarif_stream_output_format : public sarif_output_format
1739 {
1740 public:
1741 sarif_stream_output_format (diagnostic_context &context,
1742 bool formatted,
1743 FILE *stream)
1744 : sarif_output_format (context, formatted),
1745 m_stream (stream)
1746 {
1747 }
1748 ~sarif_stream_output_format ()
1749 {
1750 m_builder.flush_to_file (m_stream);
1751 }
1752 private:
1753 FILE *m_stream;
1754 };
1755
1756 class sarif_file_output_format : public sarif_output_format
1757 {
1758 public:
1759 sarif_file_output_format (diagnostic_context &context,
1760 bool formatted,
1761 const char *base_file_name)
1762 : sarif_output_format (context, formatted),
1763 m_base_file_name (xstrdup (base_file_name))
1764 {
1765 }
1766 ~sarif_file_output_format ()
1767 {
1768 char *filename = concat (m_base_file_name, ".sarif", NULL);
1769 free (m_base_file_name);
1770 m_base_file_name = nullptr;
1771 FILE *outf = fopen (filename, "w");
1772 if (!outf)
1773 {
1774 const char *errstr = xstrerror (errno);
1775 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1776 filename, errstr);
1777 free (filename);
1778 return;
1779 }
1780 m_builder.flush_to_file (outf);
1781 fclose (outf);
1782 free (filename);
1783 }
1784
1785 private:
1786 char *m_base_file_name;
1787 };
1788
1789 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1790 to a file). */
1791
1792 static void
1793 diagnostic_output_format_init_sarif (diagnostic_context *context)
1794 {
1795 /* Override callbacks. */
1796 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1797 context->set_ice_handler_callback (sarif_ice_handler);
1798
1799 /* The metadata is handled in SARIF format, rather than as text. */
1800 context->set_show_cwe (false);
1801 context->set_show_rules (false);
1802
1803 /* The option is handled in SARIF format, rather than as text. */
1804 context->set_show_option_requested (false);
1805
1806 /* Don't colorize the text. */
1807 pp_show_color (context->printer) = false;
1808 }
1809
1810 /* Populate CONTEXT in preparation for SARIF output to stderr. */
1811
1812 void
1813 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context,
1814 bool formatted)
1815 {
1816 diagnostic_output_format_init_sarif (context);
1817 context->set_output_format (new sarif_stream_output_format (*context,
1818 formatted,
1819 stderr));
1820 }
1821
1822 /* Populate CONTEXT in preparation for SARIF output to a file named
1823 BASE_FILE_NAME.sarif. */
1824
1825 void
1826 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1827 bool formatted,
1828 const char *base_file_name)
1829 {
1830 diagnostic_output_format_init_sarif (context);
1831 context->set_output_format (new sarif_file_output_format (*context,
1832 formatted,
1833 base_file_name));
1834 }
1835
1836 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
1837
1838 void
1839 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1840 bool formatted,
1841 FILE *stream)
1842 {
1843 diagnostic_output_format_init_sarif (context);
1844 context->set_output_format (new sarif_stream_output_format (*context,
1845 formatted,
1846 stream));
1847 }