1/* SARIF output for diagnostics
2 Copyright (C) 2018-2023 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 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 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 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
162 void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
163 diagnostic_t orig_diag_kind);
164 void emit_diagram (diagnostic_context *context,
165 const diagnostic_diagram &diagram);
166 void end_group ();
167
168 void flush_to_file (FILE *outf);
169
170 json::array *make_locations_arr (diagnostic_info *diagnostic);
171 json::object *make_location_object (const rich_location &rich_loc,
172 const logical_location *logical_loc);
173 json::object *make_message_object (const char *msg) const;
174 json::object *
175 make_message_object_for_diagram (diagnostic_context *context,
176 const diagnostic_diagram &diagram);
177
178private:
179 sarif_result *make_result_object (diagnostic_context *context,
180 diagnostic_info *diagnostic,
181 diagnostic_t orig_diag_kind);
182 void set_any_logical_locs_arr (json::object *location_obj,
183 const logical_location *logical_loc);
184 json::object *make_location_object (const diagnostic_event &event);
185 json::object *
186 make_logical_location_object (const logical_location &logical_loc) const;
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 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
254/* class sarif_object : public json::object. */
255
256sarif_property_bag &
257sarif_object::get_or_create_properties ()
258{
259 json::value *properties_val = get (key: "properties");
260 if (properties_val)
261 {
262 if (properties_val->get_kind () == json::JSON_OBJECT)
263 return *static_cast <sarif_property_bag *> (properties_val);
264 }
265
266 sarif_property_bag *bag = new sarif_property_bag ();
267 set (key: "properties", v: bag);
268 return *bag;
269}
270
271/* class sarif_invocation : public sarif_object. */
272
273/* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
274 Add an object representing the ICE to the notifications array. */
275
276void
277sarif_invocation::add_notification_for_ice (diagnostic_context *context,
278 diagnostic_info *diagnostic,
279 sarif_builder *builder)
280{
281 m_success = false;
282
283 sarif_ice_notification *notification_obj
284 = new sarif_ice_notification (context, diagnostic, builder);
285 m_notifications_arr->append (v: notification_obj);
286}
287
288void
289sarif_invocation::prepare_to_flush (diagnostic_context *context)
290{
291 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
292 set (key: "executionSuccessful", v: new json::literal (m_success));
293
294 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
295 set (key: "toolExecutionNotifications", v: m_notifications_arr);
296
297 /* Call client hook, allowing it to create a custom property bag for
298 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
299 if (auto client_data_hooks = context->get_client_data_hooks ())
300 client_data_hooks->add_sarif_invocation_properties (invocation_obj&: *this);
301}
302
303/* class sarif_result : public sarif_object. */
304
305/* Handle secondary diagnostics that occur within a diagnostic group.
306 The closest SARIF seems to have to nested diagnostics is the
307 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
308 so we lazily set this property and populate the array if and when
309 secondary diagnostics occur (such as notes to a warning). */
310
311void
312sarif_result::on_nested_diagnostic (diagnostic_context *context,
313 diagnostic_info *diagnostic,
314 diagnostic_t /*orig_diag_kind*/,
315 sarif_builder *builder)
316{
317 /* We don't yet generate meaningful logical locations for notes;
318 sometimes these will related to current_function_decl, but
319 often they won't. */
320 json::object *location_obj
321 = builder->make_location_object (rich_loc: *diagnostic->richloc, NULL);
322 json::object *message_obj
323 = builder->make_message_object (msg: pp_formatted_text (context->printer));
324 pp_clear_output_area (context->printer);
325 location_obj->set (key: "message", v: message_obj);
326
327 add_related_location (location_obj);
328}
329
330/* Handle diagrams that occur within a diagnostic group.
331 The closest thing in SARIF seems to be to add a location to the
332 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
333 and to put the diagram into the "message" property of that location
334 (SARIF v2.1.0 section 3.28.5). */
335
336void
337sarif_result::on_diagram (diagnostic_context *context,
338 const diagnostic_diagram &diagram,
339 sarif_builder *builder)
340{
341 json::object *location_obj = new json::object ();
342 json::object *message_obj
343 = builder->make_message_object_for_diagram (context, diagram);
344 location_obj->set (key: "message", v: message_obj);
345
346 add_related_location (location_obj);
347}
348
349/* Add LOCATION_OBJ to this result's "relatedLocations" array,
350 creating it if it doesn't yet exist. */
351
352void
353sarif_result::add_related_location (json::object *location_obj)
354{
355 if (!m_related_locations_arr)
356 {
357 m_related_locations_arr = new json::array ();
358 set (key: "relatedLocations", v: m_related_locations_arr);
359 }
360 m_related_locations_arr->append (v: location_obj);
361}
362
363/* class sarif_ice_notification : public sarif_object. */
364
365/* sarif_ice_notification's ctor.
366 DIAGNOSTIC is an internal compiler error. */
367
368sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
369 diagnostic_info *diagnostic,
370 sarif_builder *builder)
371{
372 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
373 json::array *locations_arr = builder->make_locations_arr (diagnostic);
374 set (key: "locations", v: locations_arr);
375
376 /* "message" property (SARIF v2.1.0 section 3.85.5). */
377 json::object *message_obj
378 = builder->make_message_object (msg: pp_formatted_text (context->printer));
379 pp_clear_output_area (context->printer);
380 set (key: "message", v: message_obj);
381
382 /* "level" property (SARIF v2.1.0 section 3.58.6). */
383 set (key: "level", v: new json::string ("error"));
384}
385
386/* class sarif_thread_flow : public sarif_object. */
387
388sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
389{
390 /* "id" property (SARIF v2.1.0 section 3.37.2). */
391 label_text name (thread.get_name (can_colorize: false));
392 set (key: "id", v: new json::string (name.get ()));
393
394 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
395 m_locations_arr = new json::array ();
396 set (key: "locations", v: m_locations_arr);
397}
398
399/* class sarif_builder. */
400
401/* sarif_builder's ctor. */
402
403sarif_builder::sarif_builder (diagnostic_context *context)
404: m_context (context),
405 m_invocation_obj (new sarif_invocation ()),
406 m_results_array (new json::array ()),
407 m_cur_group_result (NULL),
408 m_seen_any_relative_paths (false),
409 m_rule_id_set (),
410 m_rules_arr (new json::array ()),
411 m_tabstop (context->m_tabstop)
412{
413}
414
415/* Implementation of "end_diagnostic" for SARIF output. */
416
417void
418sarif_builder::end_diagnostic (diagnostic_context *context,
419 diagnostic_info *diagnostic,
420 diagnostic_t orig_diag_kind)
421{
422 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
423 {
424 m_invocation_obj->add_notification_for_ice (context, diagnostic, builder: this);
425 return;
426 }
427
428 if (m_cur_group_result)
429 /* Nested diagnostic. */
430 m_cur_group_result->on_nested_diagnostic (context,
431 diagnostic,
432 orig_diag_kind,
433 builder: this);
434 else
435 {
436 /* Top-level diagnostic. */
437 sarif_result *result_obj
438 = make_result_object (context, diagnostic, orig_diag_kind);
439 m_results_array->append (v: result_obj);
440 m_cur_group_result = result_obj;
441 }
442}
443
444/* Implementation of diagnostic_context::m_diagrams.m_emission_cb
445 for SARIF output. */
446
447void
448sarif_builder::emit_diagram (diagnostic_context *context,
449 const diagnostic_diagram &diagram)
450{
451 /* We must be within the emission of a top-level diagnostic. */
452 gcc_assert (m_cur_group_result);
453 m_cur_group_result->on_diagram (context, diagram, builder: this);
454}
455
456/* Implementation of "end_group_cb" for SARIF output. */
457
458void
459sarif_builder::end_group ()
460{
461 m_cur_group_result = NULL;
462}
463
464/* Create a top-level object, and add it to all the results
465 (and other entities) we've seen so far.
466
467 Flush it all to OUTF. */
468
469void
470sarif_builder::flush_to_file (FILE *outf)
471{
472 m_invocation_obj->prepare_to_flush (context: m_context);
473 json::object *top = make_top_level_object (invocation_obj: m_invocation_obj, results: m_results_array);
474 top->dump (outf);
475 m_invocation_obj = NULL;
476 m_results_array = NULL;
477 fprintf (stream: outf, format: "\n");
478 delete top;
479}
480
481/* Attempt to convert DIAG_KIND to a suitable value for the "level"
482 property (SARIF v2.1.0 section 3.27.10).
483
484 Return NULL if there isn't one. */
485
486static const char *
487maybe_get_sarif_level (diagnostic_t diag_kind)
488{
489 switch (diag_kind)
490 {
491 case DK_WARNING:
492 return "warning";
493 case DK_ERROR:
494 return "error";
495 case DK_NOTE:
496 case DK_ANACHRONISM:
497 return "note";
498 default:
499 return NULL;
500 }
501}
502
503/* Make a string for DIAG_KIND suitable for use a ruleId
504 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
505 have anything better to use. */
506
507static char *
508make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
509{
510 static const char *const diagnostic_kind_text[] = {
511#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
512#include "diagnostic.def"
513#undef DEFINE_DIAGNOSTIC_KIND
514 "must-not-happen"
515 };
516 /* Lose the trailing ": ". */
517 const char *kind_text = diagnostic_kind_text[diag_kind];
518 size_t len = strlen (s: kind_text);
519 gcc_assert (len > 2);
520 gcc_assert (kind_text[len - 2] == ':');
521 gcc_assert (kind_text[len - 1] == ' ');
522 char *rstrip = xstrdup (kind_text);
523 rstrip[len - 2] = '\0';
524 return rstrip;
525}
526
527/* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
528
529sarif_result *
530sarif_builder::make_result_object (diagnostic_context *context,
531 diagnostic_info *diagnostic,
532 diagnostic_t orig_diag_kind)
533{
534 sarif_result *result_obj = new sarif_result ();
535
536 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
537 /* Ideally we'd have an option_name for these. */
538 if (char *option_text
539 = context->m_option_name (context, diagnostic->option_index,
540 orig_diag_kind, diagnostic->kind))
541 {
542 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
543 Set ruleId referencing them. */
544 result_obj->set (key: "ruleId", v: new json::string (option_text));
545 if (m_rule_id_set.contains (k: option_text))
546 free (ptr: option_text);
547 else
548 {
549 /* This is the first time we've seen this ruleId. */
550 /* Add to set, taking ownership. */
551 m_rule_id_set.add (k: option_text);
552
553 json::object *reporting_desc_obj
554 = make_reporting_descriptor_object_for_warning (context,
555 diagnostic,
556 orig_diag_kind,
557 option_text);
558 m_rules_arr->append (v: reporting_desc_obj);
559 }
560 }
561 else
562 {
563 /* Otherwise, we have an "error" or a stray "note"; use the
564 diagnostic kind as the ruleId, so that the result object at least
565 has a ruleId.
566 We don't bother creating reportingDescriptor objects for these. */
567 char *rule_id = make_rule_id_for_diagnostic_kind (diag_kind: orig_diag_kind);
568 result_obj->set (key: "ruleId", v: new json::string (rule_id));
569 free (ptr: rule_id);
570 }
571
572 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
573 if (diagnostic->metadata)
574 if (int cwe_id = diagnostic->metadata->get_cwe ())
575 {
576 json::array *taxa_arr = new json::array ();
577 json::object *cwe_id_obj
578 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
579 taxa_arr->append (v: cwe_id_obj);
580 result_obj->set (key: "taxa", v: taxa_arr);
581 }
582
583 /* "level" property (SARIF v2.1.0 section 3.27.10). */
584 if (const char *sarif_level = maybe_get_sarif_level (diag_kind: diagnostic->kind))
585 result_obj->set (key: "level", v: new json::string (sarif_level));
586
587 /* "message" property (SARIF v2.1.0 section 3.27.11). */
588 json::object *message_obj
589 = make_message_object (msg: pp_formatted_text (context->printer));
590 pp_clear_output_area (context->printer);
591 result_obj->set (key: "message", v: message_obj);
592
593 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
594 json::array *locations_arr = make_locations_arr (diagnostic);
595 result_obj->set (key: "locations", v: locations_arr);
596
597 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
598 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
599 {
600 json::array *code_flows_arr = new json::array ();
601 json::object *code_flow_obj = make_code_flow_object (path: *path);
602 code_flows_arr->append (v: code_flow_obj);
603 result_obj->set (key: "codeFlows", v: code_flows_arr);
604 }
605
606 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
607 set up later, if any nested diagnostics occur within this diagnostic
608 group. */
609
610 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
611 const rich_location *richloc = diagnostic->richloc;
612 if (richloc->get_num_fixit_hints ())
613 {
614 json::array *fix_arr = new json::array ();
615 json::object *fix_obj = make_fix_object (rich_loc: *richloc);
616 fix_arr->append (v: fix_obj);
617 result_obj->set (key: "fixes", v: fix_arr);
618 }
619
620 return result_obj;
621}
622
623/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
624 for a GCC warning. */
625
626json::object *
627sarif_builder::
628make_reporting_descriptor_object_for_warning (diagnostic_context *context,
629 diagnostic_info *diagnostic,
630 diagnostic_t /*orig_diag_kind*/,
631 const char *option_text)
632{
633 json::object *reporting_desc = new json::object ();
634
635 /* "id" property (SARIF v2.1.0 section 3.49.3). */
636 reporting_desc->set (key: "id", v: new json::string (option_text));
637
638 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
639 it seems redundant compared to "id". */
640
641 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
642 if (context->m_get_option_url)
643 {
644 char *option_url
645 = context->m_get_option_url (context, diagnostic->option_index);
646 if (option_url)
647 {
648 reporting_desc->set (key: "helpUri", v: new json::string (option_url));
649 free (ptr: option_url);
650 }
651 }
652
653 return reporting_desc;
654}
655
656/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
657 for CWE_ID, for use within the CWE taxa array. */
658
659json::object *
660sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
661{
662 json::object *reporting_desc = new json::object ();
663
664 /* "id" property (SARIF v2.1.0 section 3.49.3). */
665 {
666 pretty_printer pp;
667 pp_printf (&pp, "%i", cwe_id);
668 reporting_desc->set (key: "id", v: new json::string (pp_formatted_text (&pp)));
669 }
670
671 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
672 {
673 char *url = get_cwe_url (cwe: cwe_id);
674 reporting_desc->set (key: "helpUri", v: new json::string (url));
675 free (ptr: url);
676 }
677
678 return reporting_desc;
679}
680
681/* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
682 referencing CWE_ID, for use within a result object.
683 Also, add CWE_ID to m_cwe_id_set. */
684
685json::object *
686sarif_builder::
687make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
688{
689 json::object *desc_ref_obj = new json::object ();
690
691 /* "id" property (SARIF v2.1.0 section 3.52.4). */
692 {
693 pretty_printer pp;
694 pp_printf (&pp, "%i", cwe_id);
695 desc_ref_obj->set (key: "id", v: new json::string (pp_formatted_text (&pp)));
696 }
697
698 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
699 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
700 desc_ref_obj->set (key: "toolComponent", v: comp_ref_obj);
701
702 /* Add CWE_ID to our set. */
703 gcc_assert (cwe_id > 0);
704 m_cwe_id_set.add (k: cwe_id);
705
706 return desc_ref_obj;
707}
708
709/* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
710 references the CWE taxonomy. */
711
712json::object *
713sarif_builder::
714make_tool_component_reference_object_for_cwe () const
715{
716 json::object *comp_ref_obj = new json::object ();
717
718 /* "name" property (SARIF v2.1.0 section 3.54.3). */
719 comp_ref_obj->set (key: "name", v: new json::string ("cwe"));
720
721 return comp_ref_obj;
722}
723
724/* Make an array suitable for use as the "locations" property of:
725 - a "result" object (SARIF v2.1.0 section 3.27.12), or
726 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
727
728json::array *
729sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
730{
731 json::array *locations_arr = new json::array ();
732 const logical_location *logical_loc = NULL;
733 if (auto client_data_hooks = m_context->get_client_data_hooks ())
734 logical_loc = client_data_hooks->get_current_logical_location ();
735
736 json::object *location_obj
737 = make_location_object (rich_loc: *diagnostic->richloc, logical_loc);
738 locations_arr->append (v: location_obj);
739 return locations_arr;
740}
741
742/* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
743 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
744
745void
746sarif_builder::
747set_any_logical_locs_arr (json::object *location_obj,
748 const logical_location *logical_loc)
749{
750 if (!logical_loc)
751 return;
752 json::object *logical_loc_obj = make_logical_location_object (logical_loc: *logical_loc);
753 json::array *location_locs_arr = new json::array ();
754 location_locs_arr->append (v: logical_loc_obj);
755 location_obj->set (key: "logicalLocations", v: location_locs_arr);
756}
757
758/* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
759 and LOGICAL_LOC. */
760
761json::object *
762sarif_builder::make_location_object (const rich_location &rich_loc,
763 const logical_location *logical_loc)
764{
765 json::object *location_obj = new json::object ();
766
767 /* Get primary loc from RICH_LOC. */
768 location_t loc = rich_loc.get_loc ();
769
770 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
771 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
772 location_obj->set (key: "physicalLocation", v: phs_loc_obj);
773
774 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
775 set_any_logical_locs_arr (location_obj, logical_loc);
776
777 return location_obj;
778}
779
780/* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
781 within a diagnostic_path. */
782
783json::object *
784sarif_builder::make_location_object (const diagnostic_event &event)
785{
786 json::object *location_obj = new json::object ();
787
788 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
789 location_t loc = event.get_location ();
790 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
791 location_obj->set (key: "physicalLocation", v: phs_loc_obj);
792
793 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
794 const logical_location *logical_loc = event.get_logical_location ();
795 set_any_logical_locs_arr (location_obj, logical_loc);
796
797 /* "message" property (SARIF v2.1.0 section 3.28.5). */
798 label_text ev_desc = event.get_desc (can_colorize: false);
799 json::object *message_obj = make_message_object (msg: ev_desc.get ());
800 location_obj->set (key: "message", v: message_obj);
801
802 return location_obj;
803}
804
805/* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
806 or return NULL;
807 Add any filename to the m_artifacts. */
808
809json::object *
810sarif_builder::maybe_make_physical_location_object (location_t loc)
811{
812 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
813 return NULL;
814
815 json::object *phys_loc_obj = new json::object ();
816
817 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
818 json::object *artifact_loc_obj = make_artifact_location_object (loc);
819 phys_loc_obj->set (key: "artifactLocation", v: artifact_loc_obj);
820 m_filenames.add (LOCATION_FILE (loc));
821
822 /* "region" property (SARIF v2.1.0 section 3.29.4). */
823 if (json::object *region_obj = maybe_make_region_object (loc))
824 phys_loc_obj->set (key: "region", v: region_obj);
825
826 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
827 if (json::object *context_region_obj
828 = maybe_make_region_object_for_context (loc))
829 phys_loc_obj->set (key: "contextRegion", v: context_region_obj);
830
831 /* Instead, we add artifacts to the run as a whole,
832 with artifact.contents.
833 Could do both, though. */
834
835 return phys_loc_obj;
836}
837
838/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
839 or return NULL. */
840
841json::object *
842sarif_builder::make_artifact_location_object (location_t loc)
843{
844 return make_artifact_location_object (LOCATION_FILE (loc));
845}
846
847/* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
848 for when we need to express paths relative to PWD. */
849
850#define PWD_PROPERTY_NAME ("PWD")
851
852/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
853 or return NULL. */
854
855json::object *
856sarif_builder::make_artifact_location_object (const char *filename)
857{
858 json::object *artifact_loc_obj = new json::object ();
859
860 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
861 artifact_loc_obj->set (key: "uri", v: new json::string (filename));
862
863 if (filename[0] != '/')
864 {
865 /* If we have a relative path, set the "uriBaseId" property
866 (SARIF v2.1.0 section 3.4.4). */
867 artifact_loc_obj->set (key: "uriBaseId", v: new json::string (PWD_PROPERTY_NAME));
868 m_seen_any_relative_paths = true;
869 }
870
871 return artifact_loc_obj;
872}
873
874/* Get the PWD, or NULL, as an absolute file-based URI,
875 adding a trailing forward slash (as required by SARIF v2.1.0
876 section 3.14.14). */
877
878static char *
879make_pwd_uri_str ()
880{
881 /* The prefix of a file-based URI, up to, but not including the path. */
882#define FILE_PREFIX ("file://")
883
884 const char *pwd = getpwd ();
885 if (!pwd)
886 return NULL;
887 size_t len = strlen (s: pwd);
888 if (len == 0 || pwd[len - 1] != '/')
889 return concat (FILE_PREFIX, pwd, "/", NULL);
890 else
891 {
892 gcc_assert (pwd[len - 1] == '/');
893 return concat (FILE_PREFIX, pwd, NULL);
894 }
895}
896
897/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
898 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
899 section 3.14.14) when we have any relative paths. */
900
901json::object *
902sarif_builder::make_artifact_location_object_for_pwd () const
903{
904 json::object *artifact_loc_obj = new json::object ();
905
906 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
907 if (char *pwd = make_pwd_uri_str ())
908 {
909 gcc_assert (strlen (pwd) > 0);
910 gcc_assert (pwd[strlen (pwd) - 1] == '/');
911 artifact_loc_obj->set (key: "uri", v: new json::string (pwd));
912 free (ptr: pwd);
913 }
914
915 return artifact_loc_obj;
916}
917
918/* Get the column number within EXPLOC. */
919
920int
921sarif_builder::get_sarif_column (expanded_location exploc) const
922{
923 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
924 return location_compute_display_column (exploc, policy);
925}
926
927/* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
928 or return NULL. */
929
930json::object *
931sarif_builder::maybe_make_region_object (location_t loc) const
932{
933 location_t caret_loc = get_pure_location (loc);
934
935 if (caret_loc <= BUILTINS_LOCATION)
936 return NULL;
937
938 location_t start_loc = get_start (loc);
939 location_t finish_loc = get_finish (loc);
940
941 expanded_location exploc_caret = expand_location (caret_loc);
942 expanded_location exploc_start = expand_location (start_loc);
943 expanded_location exploc_finish = expand_location (finish_loc);
944
945 if (exploc_start.file !=exploc_caret.file)
946 return NULL;
947 if (exploc_finish.file !=exploc_caret.file)
948 return NULL;
949
950 json::object *region_obj = new json::object ();
951
952 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
953 region_obj->set (key: "startLine", v: new json::integer_number (exploc_start.line));
954
955 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
956 region_obj->set (key: "startColumn",
957 v: new json::integer_number (get_sarif_column (exploc: exploc_start)));
958
959 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
960 if (exploc_finish.line != exploc_start.line)
961 region_obj->set (key: "endLine", v: new json::integer_number (exploc_finish.line));
962
963 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
964 This expresses the column immediately beyond the range. */
965 {
966 int next_column = sarif_builder::get_sarif_column (exploc: exploc_finish) + 1;
967 region_obj->set (key: "endColumn", v: new json::integer_number (next_column));
968 }
969
970 return region_obj;
971}
972
973/* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
974 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
975
976 This is similar to maybe_make_region_object, but ignores column numbers,
977 covering the line(s) as a whole, and including a "snippet" property
978 embedding those source lines, making it easier for consumers to show
979 the pertinent source. */
980
981json::object *
982sarif_builder::maybe_make_region_object_for_context (location_t loc) const
983{
984 location_t caret_loc = get_pure_location (loc);
985
986 if (caret_loc <= BUILTINS_LOCATION)
987 return NULL;
988
989 location_t start_loc = get_start (loc);
990 location_t finish_loc = get_finish (loc);
991
992 expanded_location exploc_caret = expand_location (caret_loc);
993 expanded_location exploc_start = expand_location (start_loc);
994 expanded_location exploc_finish = expand_location (finish_loc);
995
996 if (exploc_start.file !=exploc_caret.file)
997 return NULL;
998 if (exploc_finish.file !=exploc_caret.file)
999 return NULL;
1000
1001 json::object *region_obj = new json::object ();
1002
1003 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1004 region_obj->set (key: "startLine", v: new json::integer_number (exploc_start.line));
1005
1006 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1007 if (exploc_finish.line != exploc_start.line)
1008 region_obj->set (key: "endLine", v: new json::integer_number (exploc_finish.line));
1009
1010 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1011 if (json::object *artifact_content_obj
1012 = maybe_make_artifact_content_object (filename: exploc_start.file,
1013 start_line: exploc_start.line,
1014 end_line: exploc_finish.line))
1015 region_obj->set (key: "snippet", v: artifact_content_obj);
1016
1017 return region_obj;
1018}
1019
1020/* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1021 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1022
1023json::object *
1024sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1025{
1026 location_t start_loc = hint.get_start_loc ();
1027 location_t next_loc = hint.get_next_loc ();
1028
1029 expanded_location exploc_start = expand_location (start_loc);
1030 expanded_location exploc_next = expand_location (next_loc);
1031
1032 json::object *region_obj = new json::object ();
1033
1034 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
1035 region_obj->set (key: "startLine", v: new json::integer_number (exploc_start.line));
1036
1037 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1038 int start_col = get_sarif_column (exploc: exploc_start);
1039 region_obj->set (key: "startColumn",
1040 v: new json::integer_number (start_col));
1041
1042 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1043 if (exploc_next.line != exploc_start.line)
1044 region_obj->set (key: "endLine", v: new json::integer_number (exploc_next.line));
1045
1046 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1047 This expresses the column immediately beyond the range. */
1048 int next_col = get_sarif_column (exploc: exploc_next);
1049 region_obj->set (key: "endColumn", v: new json::integer_number (next_col));
1050
1051 return region_obj;
1052}
1053
1054/* Attempt to get a string for a logicalLocation's "kind" property
1055 (SARIF v2.1.0 section 3.33.7).
1056 Return NULL if unknown. */
1057
1058static const char *
1059maybe_get_sarif_kind (enum logical_location_kind kind)
1060{
1061 switch (kind)
1062 {
1063 default:
1064 gcc_unreachable ();
1065 case LOGICAL_LOCATION_KIND_UNKNOWN:
1066 return NULL;
1067
1068 case LOGICAL_LOCATION_KIND_FUNCTION:
1069 return "function";
1070 case LOGICAL_LOCATION_KIND_MEMBER:
1071 return "member";
1072 case LOGICAL_LOCATION_KIND_MODULE:
1073 return "module";
1074 case LOGICAL_LOCATION_KIND_NAMESPACE:
1075 return "namespace";
1076 case LOGICAL_LOCATION_KIND_TYPE:
1077 return "type";
1078 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1079 return "returnType";
1080 case LOGICAL_LOCATION_KIND_PARAMETER:
1081 return "parameter";
1082 case LOGICAL_LOCATION_KIND_VARIABLE:
1083 return "variable";
1084 }
1085}
1086
1087/* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1088 or return NULL. */
1089
1090json::object *
1091sarif_builder::
1092make_logical_location_object (const logical_location &logical_loc) const
1093{
1094 json::object *logical_loc_obj = new json::object ();
1095
1096 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1097 if (const char *short_name = logical_loc.get_short_name ())
1098 logical_loc_obj->set (key: "name", v: new json::string (short_name));
1099
1100 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1101 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
1102 logical_loc_obj->set (key: "fullyQualifiedName",
1103 v: new json::string (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 (key: "decoratedName", v: new json::string (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 (key: "kind", v: new json::string (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 json::object *thread_flow_loc_obj = new json::object ();
1164
1165 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1166 json::object *location_obj = make_location_object (event: ev);
1167 thread_flow_loc_obj->set (key: "location", v: location_obj);
1168
1169 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1170 diagnostic_event::meaning m = ev.get_meaning ();
1171 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1172 thread_flow_loc_obj->set (key: "kinds", v: kinds_arr);
1173
1174 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1175 thread_flow_loc_obj->set (key: "nestingLevel",
1176 v: new json::integer_number (ev.get_stack_depth ()));
1177
1178 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1179 Offset by 1 to match the human-readable values emitted by %@. */
1180 thread_flow_loc_obj->set (key: "executionOrder",
1181 v: new json::integer_number (path_event_idx + 1));
1182
1183 /* It might be nice to eventually implement the following for -fanalyzer:
1184 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1185 - the "state" property (SARIF v2.1.0 section 3.38.9)
1186 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1187
1188 return thread_flow_loc_obj;
1189}
1190
1191/* If M has any known meaning, make a json array suitable for the "kinds"
1192 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1193
1194 Otherwise, return NULL. */
1195
1196json::array *
1197sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1198{
1199 if (m.m_verb == diagnostic_event::VERB_unknown
1200 && m.m_noun == diagnostic_event::NOUN_unknown
1201 && m.m_property == diagnostic_event::PROPERTY_unknown)
1202 return NULL;
1203
1204 json::array *kinds_arr = new json::array ();
1205 if (const char *verb_str
1206 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1207 kinds_arr->append (v: new json::string (verb_str));
1208 if (const char *noun_str
1209 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1210 kinds_arr->append (v: new json::string (noun_str));
1211 if (const char *property_str
1212 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1213 kinds_arr->append (v: new json::string (property_str));
1214 return kinds_arr;
1215}
1216
1217/* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1218
1219json::object *
1220sarif_builder::make_message_object (const char *msg) const
1221{
1222 json::object *message_obj = new json::object ();
1223
1224 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1225 message_obj->set (key: "text", v: new json::string (msg));
1226
1227 return message_obj;
1228}
1229
1230/* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1231 We emit the diagram as a code block within the Markdown part
1232 of the message. */
1233
1234json::object *
1235sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1236 const diagnostic_diagram &diagram)
1237{
1238 json::object *message_obj = new json::object ();
1239
1240 /* "text" property (SARIF v2.1.0 section 3.11.8). */
1241 message_obj->set (key: "text", v: new json::string (diagram.get_alt_text ()));
1242
1243 char *saved_prefix = pp_take_prefix (context->printer);
1244 pp_set_prefix (context->printer, NULL);
1245
1246 /* "To produce a code block in Markdown, simply indent every line of
1247 the block by at least 4 spaces or 1 tab."
1248 Here we use 4 spaces. */
1249 diagram.get_canvas ().print_to_pp (pp: context->printer, per_line_prefix: " ");
1250 pp_set_prefix (context->printer, saved_prefix);
1251
1252 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1253 message_obj->set (key: "markdown",
1254 v: new json::string (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
1264json::object *
1265sarif_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 (key: "text", v: new json::string (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
1281json::object *
1282sarif_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 (key: "$schema", v: new json::string (SARIF_SCHEMA));
1289
1290 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1291 log_obj->set (key: "version", v: new json::string (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 (v: run_obj);
1297 log_obj->set (key: "runs", v: 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
1305json::object *
1306sarif_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 (key: "tool", v: 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 (key: "taxonomies", v: 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 (v: invocation_obj);
1323 run_obj->set (key: "invocations", v: 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 (key: "originalUriBaseIds", v: 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, v: 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 (filename: iter);
1340 artifacts_arr->append (v: artifact_obj);
1341 }
1342 run_obj->set (key: "artifacts", v: artifacts_arr);
1343
1344 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1345 run_obj->set (key: "results", v: results);
1346
1347 return run_obj;
1348}
1349
1350/* Make a tool object (SARIF v2.1.0 section 3.18). */
1351
1352json::object *
1353sarif_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 (key: "driver", v: 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 (obj: 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 (key: "name", v: new json::string (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 (key: "fullName", v: new json::string (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 (key: "version", v: new json::string (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 (key: "extensions", v: extensions_arr);
1397 for (auto iter : v.m_plugin_objs)
1398 extensions_arr->append (v: 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
1411json::object *
1412sarif_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 (key: "name", v: new json::string (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 (key: "fullName", v: new json::string (full_name));
1428 free (ptr: 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 (key: "version", v: new json::string (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 (key: "informationUri", v: new json::string (version_url));
1439 free (ptr: version_url);
1440 }
1441 }
1442
1443 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1444 driver_obj->set (key: "rules", v: 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
1455json::array *
1456sarif_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 (v: 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
1474json::object *
1475sarif_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 (key: "name", v: new json::string ("CWE"));
1484
1485 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1486 taxonomy_obj->set (key: "version", v: new json::string ("4.7"));
1487
1488 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1489 taxonomy_obj->set (key: "organization", v: new json::string ("MITRE"));
1490
1491 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1492 json::object *short_desc
1493 = make_multiformat_message_string (msg: "The MITRE"
1494 " Common Weakness Enumeration");
1495 taxonomy_obj->set (key: "shortDescription", v: 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 (v: cwe_taxon);
1504 }
1505 taxonomy_obj->set (key: "taxa", v: taxa_arr);
1506
1507 return taxonomy_obj;
1508}
1509
1510/* Make an artifact object (SARIF v2.1.0 section 3.24). */
1511
1512json::object *
1513sarif_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 (key: "location", v: 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 (key: "contents", v: 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 (key: "sourceLanguage", v: new json::string (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
1538json::object *
1539sarif_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 (file_path: 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(data: utf8_content.get_buffer (), num_bytes: utf8_content.length ()))
1549 return NULL;
1550
1551 json::object *artifact_content_obj = new json::object ();
1552 artifact_content_obj->set (key: "text",
1553 v: 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
1561char *
1562sarif_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 (file_path: filename, line);
1572 if (!line_content.get_buffer ())
1573 return NULL;
1574 result.reserve (nelems: line_content.length () + 1);
1575 for (size_t i = 0; i < line_content.length (); i++)
1576 result.quick_push (obj: line_content[i]);
1577 result.quick_push (obj: '\n');
1578 }
1579 result.safe_push (obj: '\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
1587json::object *
1588sarif_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(data: text_utf8, num_bytes: strlen(s: text_utf8)))
1599 {
1600 free (ptr: text_utf8);
1601 return NULL;
1602 }
1603
1604 json::object *artifact_content_obj = new json::object ();
1605 artifact_content_obj->set (key: "text", v: new json::string (text_utf8));
1606 free (ptr: 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
1613json::object *
1614sarif_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 (v: artifact_change_obj);
1623 fix_obj->set (key: "artifactChanges", v: 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
1630json::object *
1631sarif_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 (loc: richloc.get_loc ());
1638 artifact_change_obj->set (key: "artifactLocation", v: 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 (idx: i);
1645 json::object *replacement_obj = make_replacement_object (hint: *hint);
1646 replacement_arr->append (v: replacement_obj);
1647 }
1648 artifact_change_obj->set (key: "replacements", v: 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
1655json::object *
1656sarif_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 (key: "deletedRegion", v: region_obj);
1663
1664 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1665 json::object *content_obj = make_artifact_content_object (text: hint.get_string ());
1666 replacement_obj->set (key: "insertedContent", v: content_obj);
1667
1668 return replacement_obj;
1669}
1670
1671/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1672
1673json::object *
1674sarif_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 (key: "text", v: new json::string (text));
1680
1681 return content_obj;
1682}
1683
1684/* Callback for diagnostic_context::ice_handler_cb for when an ICE
1685 occurs. */
1686
1687static void
1688sarif_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
1701class sarif_output_format : public diagnostic_output_format
1702{
1703public:
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 (diagnostic_info *) final override
1714 {
1715 /* No-op, */
1716 }
1717 void
1718 on_end_diagnostic (diagnostic_info *diagnostic,
1719 diagnostic_t orig_diag_kind) final override
1720 {
1721 m_builder.end_diagnostic (context: &m_context, diagnostic, orig_diag_kind);
1722 }
1723 void on_diagram (const diagnostic_diagram &diagram) final override
1724 {
1725 m_builder.emit_diagram (context: &m_context, diagram);
1726 }
1727
1728protected:
1729 sarif_output_format (diagnostic_context &context)
1730 : diagnostic_output_format (context),
1731 m_builder (&context)
1732 {}
1733
1734 sarif_builder m_builder;
1735};
1736
1737class sarif_stream_output_format : public sarif_output_format
1738{
1739public:
1740 sarif_stream_output_format (diagnostic_context &context, FILE *stream)
1741 : sarif_output_format (context),
1742 m_stream (stream)
1743 {
1744 }
1745 ~sarif_stream_output_format ()
1746 {
1747 m_builder.flush_to_file (outf: m_stream);
1748 }
1749private:
1750 FILE *m_stream;
1751};
1752
1753class sarif_file_output_format : public sarif_output_format
1754{
1755public:
1756 sarif_file_output_format (diagnostic_context &context,
1757 const char *base_file_name)
1758 : sarif_output_format (context),
1759 m_base_file_name (xstrdup (base_file_name))
1760 {
1761 }
1762 ~sarif_file_output_format ()
1763 {
1764 char *filename = concat (m_base_file_name, ".sarif", NULL);
1765 free (ptr: m_base_file_name);
1766 m_base_file_name = nullptr;
1767 FILE *outf = fopen (filename: filename, modes: "w");
1768 if (!outf)
1769 {
1770 const char *errstr = xstrerror (errno);
1771 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1772 filename, errstr);
1773 free (ptr: filename);
1774 return;
1775 }
1776 m_builder.flush_to_file (outf);
1777 fclose (stream: outf);
1778 free (ptr: filename);
1779 }
1780
1781private:
1782 char *m_base_file_name;
1783};
1784
1785/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1786 to a file). */
1787
1788static void
1789diagnostic_output_format_init_sarif (diagnostic_context *context)
1790{
1791 /* Override callbacks. */
1792 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1793 context->set_ice_handler_callback (sarif_ice_handler);
1794
1795 /* The metadata is handled in SARIF format, rather than as text. */
1796 context->set_show_cwe (false);
1797 context->set_show_rules (false);
1798
1799 /* The option is handled in SARIF format, rather than as text. */
1800 context->set_show_option_requested (false);
1801
1802 /* Don't colorize the text. */
1803 pp_show_color (context->printer) = false;
1804}
1805
1806/* Populate CONTEXT in preparation for SARIF output to stderr. */
1807
1808void
1809diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1810{
1811 diagnostic_output_format_init_sarif (context);
1812 context->set_output_format (new sarif_stream_output_format (*context,
1813 stderr));
1814}
1815
1816/* Populate CONTEXT in preparation for SARIF output to a file named
1817 BASE_FILE_NAME.sarif. */
1818
1819void
1820diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1821 const char *base_file_name)
1822{
1823 diagnostic_output_format_init_sarif (context);
1824 context->set_output_format (new sarif_file_output_format (*context,
1825 base_file_name));
1826}
1827
1828/* Populate CONTEXT in preparation for SARIF output to STREAM. */
1829
1830void
1831diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1832 FILE *stream)
1833{
1834 diagnostic_output_format_init_sarif (context);
1835 context->set_output_format (new sarif_stream_output_format (*context,
1836 stream));
1837}
1838

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