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

source code of gcc/diagnostic-format-sarif.cc