]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/diagnostic-format-sarif.cc
check undefine_p for one more vr
[thirdparty/gcc.git] / gcc / diagnostic-format-sarif.cc
CommitLineData
6cf276dd 1/* SARIF output for diagnostics
aeee4812 2 Copyright (C) 2018-2023 Free Software Foundation, Inc.
6cf276dd
DM
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"
b2e075a5 23#define INCLUDE_VECTOR
6cf276dd
DM
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"
4f01ae37
DM
33#include "diagnostic-diagram.h"
34#include "text-art/canvas.h"
75d62394 35#include "diagnostic-format-sarif.h"
6cf276dd
DM
36
37class sarif_builder;
38
79aaba0a
DM
39/* Subclass of json::object for SARIF invocation objects
40 (SARIF v2.1.0 section 3.20). */
41
75d62394 42class sarif_invocation : public sarif_object
79aaba0a
DM
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);
75d62394 53 void prepare_to_flush (diagnostic_context *context);
79aaba0a
DM
54
55private:
56 json::array *m_notifications_arr;
57 bool m_success;
58};
59
75d62394 60/* Subclass of sarif_object for SARIF result objects
79aaba0a 61 (SARIF v2.1.0 section 3.27). */
6cf276dd 62
75d62394 63class sarif_result : public sarif_object
6cf276dd
DM
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);
4f01ae37
DM
73 void on_diagram (diagnostic_context *context,
74 const diagnostic_diagram &diagram,
75 sarif_builder *builder);
6cf276dd
DM
76
77private:
4f01ae37
DM
78 void add_related_location (json::object *location_obj);
79
6cf276dd
DM
80 json::array *m_related_locations_arr;
81};
82
75d62394 83/* Subclass of sarif_object for SARIF notification objects
79aaba0a
DM
84 (SARIF v2.1.0 section 3.58).
85
86 This subclass is specifically for notifying when an
87 internal compiler error occurs. */
88
75d62394 89class sarif_ice_notification : public sarif_object
79aaba0a
DM
90{
91public:
92 sarif_ice_notification (diagnostic_context *context,
93 diagnostic_info *diagnostic,
94 sarif_builder *builder);
95};
96
3a1e9f3e
DM
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 (thread_flow_loc_obj);
108 }
109
110private:
111 json::array *m_locations_arr;
112};
113
6cf276dd
DM
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);
4f01ae37
DM
164 void emit_diagram (diagnostic_context *context,
165 const diagnostic_diagram &diagram);
6cf276dd
DM
166 void end_group ();
167
168 void flush_to_file (FILE *outf);
169
79aaba0a 170 json::array *make_locations_arr (diagnostic_info *diagnostic);
6cf276dd
DM
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;
4f01ae37
DM
174 json::object *
175 make_message_object_for_diagram (diagnostic_context *context,
176 const diagnostic_diagram &diagram);
6cf276dd
DM
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);
6cf276dd 188 json::object *
3a1e9f3e
DM
189 make_thread_flow_location_object (const diagnostic_event &event,
190 int path_event_idx);
6cf276dd
DM
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;
79aaba0a
DM
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);
6cf276dd
DM
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 json::object *maybe_make_artifact_content_object (const char *filename) const;
219 json::object *maybe_make_artifact_content_object (const char *filename,
220 int start_line,
221 int end_line) const;
222 json::object *make_fix_object (const rich_location &rich_loc);
223 json::object *make_artifact_change_object (const rich_location &richloc);
224 json::object *make_replacement_object (const fixit_hint &hint) const;
225 json::object *make_artifact_content_object (const char *text) const;
226 int get_sarif_column (expanded_location exploc) const;
227
228 diagnostic_context *m_context;
229
79aaba0a
DM
230 /* The JSON object for the invocation object. */
231 sarif_invocation *m_invocation_obj;
232
6cf276dd
DM
233 /* The JSON array of pending diagnostics. */
234 json::array *m_results_array;
235
236 /* The JSON object for the result object (if any) in the current
237 diagnostic group. */
238 sarif_result *m_cur_group_result;
239
240 hash_set <const char *> m_filenames;
241 bool m_seen_any_relative_paths;
242 hash_set <free_string_hash> m_rule_id_set;
243 json::array *m_rules_arr;
244
245 /* The set of all CWE IDs we've seen, if any. */
246 hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
247
248 int m_tabstop;
249};
250
251static sarif_builder *the_builder;
252
75d62394
DM
253/* class sarif_object : public json::object. */
254
255sarif_property_bag &
256sarif_object::get_or_create_properties ()
257{
258 json::value *properties_val = get ("properties");
259 if (properties_val)
260 {
261 if (properties_val->get_kind () == json::JSON_OBJECT)
262 return *static_cast <sarif_property_bag *> (properties_val);
263 }
264
265 sarif_property_bag *bag = new sarif_property_bag ();
266 set ("properties", bag);
267 return *bag;
268}
269
270/* class sarif_invocation : public sarif_object. */
79aaba0a
DM
271
272/* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
273 Add an object representing the ICE to the notifications array. */
274
275void
276sarif_invocation::add_notification_for_ice (diagnostic_context *context,
277 diagnostic_info *diagnostic,
278 sarif_builder *builder)
279{
280 m_success = false;
281
282 sarif_ice_notification *notification_obj
283 = new sarif_ice_notification (context, diagnostic, builder);
284 m_notifications_arr->append (notification_obj);
285}
286
287void
75d62394 288sarif_invocation::prepare_to_flush (diagnostic_context *context)
79aaba0a
DM
289{
290 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
291 set ("executionSuccessful", new json::literal (m_success));
292
293 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
294 set ("toolExecutionNotifications", m_notifications_arr);
75d62394
DM
295
296 /* Call client hook, allowing it to create a custom property bag for
297 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
298 if (context->m_client_data_hooks)
299 context->m_client_data_hooks->add_sarif_invocation_properties (*this);
79aaba0a
DM
300}
301
75d62394 302/* class sarif_result : public sarif_object. */
6cf276dd
DM
303
304/* Handle secondary diagnostics that occur within a diagnostic group.
305 The closest SARIF seems to have to nested diagnostics is the
306 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
307 so we lazily set this property and populate the array if and when
308 secondary diagnostics occur (such as notes to a warning). */
309
310void
311sarif_result::on_nested_diagnostic (diagnostic_context *context,
312 diagnostic_info *diagnostic,
313 diagnostic_t /*orig_diag_kind*/,
314 sarif_builder *builder)
315{
6cf276dd
DM
316 /* We don't yet generate meaningful logical locations for notes;
317 sometimes these will related to current_function_decl, but
318 often they won't. */
319 json::object *location_obj
320 = builder->make_location_object (*diagnostic->richloc, NULL);
321 json::object *message_obj
322 = builder->make_message_object (pp_formatted_text (context->printer));
323 pp_clear_output_area (context->printer);
324 location_obj->set ("message", message_obj);
325
4f01ae37
DM
326 add_related_location (location_obj);
327}
328
329/* Handle diagrams that occur within a diagnostic group.
330 The closest thing in SARIF seems to be to add a location to the
331 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
332 and to put the diagram into the "message" property of that location
333 (SARIF v2.1.0 section 3.28.5). */
334
335void
336sarif_result::on_diagram (diagnostic_context *context,
337 const diagnostic_diagram &diagram,
338 sarif_builder *builder)
339{
340 json::object *location_obj = new json::object ();
341 json::object *message_obj
342 = builder->make_message_object_for_diagram (context, diagram);
343 location_obj->set ("message", message_obj);
344
345 add_related_location (location_obj);
346}
347
348/* Add LOCATION_OBJ to this result's "relatedLocations" array,
349 creating it if it doesn't yet exist. */
350
351void
352sarif_result::add_related_location (json::object *location_obj)
353{
354 if (!m_related_locations_arr)
355 {
356 m_related_locations_arr = new json::array ();
357 set ("relatedLocations", m_related_locations_arr);
358 }
6cf276dd
DM
359 m_related_locations_arr->append (location_obj);
360}
361
75d62394 362/* class sarif_ice_notification : public sarif_object. */
79aaba0a
DM
363
364/* sarif_ice_notification's ctor.
365 DIAGNOSTIC is an internal compiler error. */
366
367sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
368 diagnostic_info *diagnostic,
369 sarif_builder *builder)
370{
371 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
372 json::array *locations_arr = builder->make_locations_arr (diagnostic);
373 set ("locations", locations_arr);
374
375 /* "message" property (SARIF v2.1.0 section 3.85.5). */
376 json::object *message_obj
377 = builder->make_message_object (pp_formatted_text (context->printer));
378 pp_clear_output_area (context->printer);
379 set ("message", message_obj);
380
381 /* "level" property (SARIF v2.1.0 section 3.58.6). */
382 set ("level", new json::string ("error"));
383}
384
3a1e9f3e
DM
385/* class sarif_thread_flow : public sarif_object. */
386
387sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
388{
389 /* "id" property (SARIF v2.1.0 section 3.37.2). */
390 label_text name (thread.get_name (false));
391 set ("id", new json::string (name.get ()));
392
393 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
394 m_locations_arr = new json::array ();
395 set ("locations", m_locations_arr);
396}
397
6cf276dd
DM
398/* class sarif_builder. */
399
400/* sarif_builder's ctor. */
401
402sarif_builder::sarif_builder (diagnostic_context *context)
403: m_context (context),
79aaba0a 404 m_invocation_obj (new sarif_invocation ()),
6cf276dd
DM
405 m_results_array (new json::array ()),
406 m_cur_group_result (NULL),
407 m_seen_any_relative_paths (false),
408 m_rule_id_set (),
409 m_rules_arr (new json::array ()),
410 m_tabstop (context->tabstop)
411{
412}
413
414/* Implementation of "end_diagnostic" for SARIF output. */
415
416void
417sarif_builder::end_diagnostic (diagnostic_context *context,
418 diagnostic_info *diagnostic,
419 diagnostic_t orig_diag_kind)
420{
79aaba0a
DM
421 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
422 {
423 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
424 return;
425 }
6cf276dd
DM
426
427 if (m_cur_group_result)
428 /* Nested diagnostic. */
429 m_cur_group_result->on_nested_diagnostic (context,
430 diagnostic,
431 orig_diag_kind,
432 this);
433 else
434 {
435 /* Top-level diagnostic. */
436 sarif_result *result_obj
437 = make_result_object (context, diagnostic, orig_diag_kind);
438 m_results_array->append (result_obj);
439 m_cur_group_result = result_obj;
440 }
441}
442
4f01ae37
DM
443/* Implementation of diagnostic_context::m_diagrams.m_emission_cb
444 for SARIF output. */
445
446void
447sarif_builder::emit_diagram (diagnostic_context *context,
448 const diagnostic_diagram &diagram)
449{
450 /* We must be within the emission of a top-level diagnostic. */
451 gcc_assert (m_cur_group_result);
452 m_cur_group_result->on_diagram (context, diagram, this);
453}
454
6cf276dd
DM
455/* Implementation of "end_group_cb" for SARIF output. */
456
457void
458sarif_builder::end_group ()
459{
460 m_cur_group_result = NULL;
461}
462
463/* Create a top-level object, and add it to all the results
464 (and other entities) we've seen so far.
465
466 Flush it all to OUTF. */
467
468void
469sarif_builder::flush_to_file (FILE *outf)
470{
75d62394 471 m_invocation_obj->prepare_to_flush (m_context);
79aaba0a 472 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
6cf276dd 473 top->dump (outf);
79aaba0a 474 m_invocation_obj = NULL;
6cf276dd
DM
475 m_results_array = NULL;
476 fprintf (outf, "\n");
477 delete top;
478}
479
480/* Attempt to convert DIAG_KIND to a suitable value for the "level"
481 property (SARIF v2.1.0 section 3.27.10).
482
483 Return NULL if there isn't one. */
484
485static const char *
486maybe_get_sarif_level (diagnostic_t diag_kind)
487{
488 switch (diag_kind)
489 {
490 case DK_WARNING:
491 return "warning";
492 case DK_ERROR:
493 return "error";
494 case DK_NOTE:
495 case DK_ANACHRONISM:
496 return "note";
497 default:
498 return NULL;
499 }
500}
501
502/* Make a string for DIAG_KIND suitable for use a ruleId
503 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
504 have anything better to use. */
505
506static char *
507make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
508{
509 static const char *const diagnostic_kind_text[] = {
510#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
511#include "diagnostic.def"
512#undef DEFINE_DIAGNOSTIC_KIND
513 "must-not-happen"
514 };
515 /* Lose the trailing ": ". */
516 const char *kind_text = diagnostic_kind_text[diag_kind];
517 size_t len = strlen (kind_text);
518 gcc_assert (len > 2);
519 gcc_assert (kind_text[len - 2] == ':');
520 gcc_assert (kind_text[len - 1] == ' ');
521 char *rstrip = xstrdup (kind_text);
522 rstrip[len - 2] = '\0';
523 return rstrip;
524}
525
526/* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
527
528sarif_result *
529sarif_builder::make_result_object (diagnostic_context *context,
530 diagnostic_info *diagnostic,
531 diagnostic_t orig_diag_kind)
532{
533 sarif_result *result_obj = new sarif_result ();
534
535 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
536 /* Ideally we'd have an option_name for these. */
537 if (char *option_text
538 = context->option_name (context, diagnostic->option_index,
539 orig_diag_kind, diagnostic->kind))
540 {
541 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
542 Set ruleId referencing them. */
543 result_obj->set ("ruleId", new json::string (option_text));
544 if (m_rule_id_set.contains (option_text))
545 free (option_text);
546 else
547 {
548 /* This is the first time we've seen this ruleId. */
549 /* Add to set, taking ownership. */
550 m_rule_id_set.add (option_text);
551
552 json::object *reporting_desc_obj
553 = make_reporting_descriptor_object_for_warning (context,
554 diagnostic,
555 orig_diag_kind,
556 option_text);
557 m_rules_arr->append (reporting_desc_obj);
558 }
559 }
560 else
561 {
562 /* Otherwise, we have an "error" or a stray "note"; use the
563 diagnostic kind as the ruleId, so that the result object at least
564 has a ruleId.
565 We don't bother creating reportingDescriptor objects for these. */
566 char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
567 result_obj->set ("ruleId", new json::string (rule_id));
568 free (rule_id);
569 }
570
571 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
572 if (diagnostic->metadata)
573 if (int cwe_id = diagnostic->metadata->get_cwe ())
574 {
575 json::array *taxa_arr = new json::array ();
576 json::object *cwe_id_obj
577 = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
578 taxa_arr->append (cwe_id_obj);
579 result_obj->set ("taxa", taxa_arr);
580 }
581
582 /* "level" property (SARIF v2.1.0 section 3.27.10). */
583 if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
584 result_obj->set ("level", new json::string (sarif_level));
585
586 /* "message" property (SARIF v2.1.0 section 3.27.11). */
587 json::object *message_obj
588 = make_message_object (pp_formatted_text (context->printer));
589 pp_clear_output_area (context->printer);
590 result_obj->set ("message", message_obj);
591
592 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
79aaba0a 593 json::array *locations_arr = make_locations_arr (diagnostic);
6cf276dd
DM
594 result_obj->set ("locations", locations_arr);
595
596 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
597 if (const diagnostic_path *path = diagnostic->richloc->get_path ())
598 {
599 json::array *code_flows_arr = new json::array ();
600 json::object *code_flow_obj = make_code_flow_object (*path);
601 code_flows_arr->append (code_flow_obj);
602 result_obj->set ("codeFlows", code_flows_arr);
603 }
604
605 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
606 set up later, if any nested diagnostics occur within this diagnostic
607 group. */
608
609 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
610 const rich_location *richloc = diagnostic->richloc;
611 if (richloc->get_num_fixit_hints ())
612 {
613 json::array *fix_arr = new json::array ();
614 json::object *fix_obj = make_fix_object (*richloc);
615 fix_arr->append (fix_obj);
616 result_obj->set ("fixes", fix_arr);
617 }
618
619 return result_obj;
620}
621
622/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
623 for a GCC warning. */
624
625json::object *
626sarif_builder::
627make_reporting_descriptor_object_for_warning (diagnostic_context *context,
628 diagnostic_info *diagnostic,
629 diagnostic_t /*orig_diag_kind*/,
630 const char *option_text)
631{
632 json::object *reporting_desc = new json::object ();
633
634 /* "id" property (SARIF v2.1.0 section 3.49.3). */
635 reporting_desc->set ("id", new json::string (option_text));
636
637 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
638 it seems redundant compared to "id". */
639
640 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
641 if (context->get_option_url)
642 {
643 char *option_url
644 = context->get_option_url (context, diagnostic->option_index);
645 if (option_url)
646 {
647 reporting_desc->set ("helpUri", new json::string (option_url));
648 free (option_url);
649 }
650 }
651
652 return reporting_desc;
653}
654
655/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
656 for CWE_ID, for use within the CWE taxa array. */
657
658json::object *
659sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
660{
661 json::object *reporting_desc = new json::object ();
662
663 /* "id" property (SARIF v2.1.0 section 3.49.3). */
664 {
665 pretty_printer pp;
666 pp_printf (&pp, "%i", cwe_id);
667 reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
668 }
669
670 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
671 {
672 char *url = get_cwe_url (cwe_id);
673 reporting_desc->set ("helpUri", new json::string (url));
674 free (url);
675 }
676
677 return reporting_desc;
678}
679
680/* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
681 referencing CWE_ID, for use within a result object.
682 Also, add CWE_ID to m_cwe_id_set. */
683
684json::object *
685sarif_builder::
686make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
687{
688 json::object *desc_ref_obj = new json::object ();
689
690 /* "id" property (SARIF v2.1.0 section 3.52.4). */
691 {
692 pretty_printer pp;
693 pp_printf (&pp, "%i", cwe_id);
694 desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
695 }
696
697 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
698 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
699 desc_ref_obj->set ("toolComponent", comp_ref_obj);
700
701 /* Add CWE_ID to our set. */
702 gcc_assert (cwe_id > 0);
703 m_cwe_id_set.add (cwe_id);
704
705 return desc_ref_obj;
706}
707
708/* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
709 references the CWE taxonomy. */
710
711json::object *
712sarif_builder::
713make_tool_component_reference_object_for_cwe () const
714{
715 json::object *comp_ref_obj = new json::object ();
716
717 /* "name" property (SARIF v2.1.0 section 3.54.3). */
718 comp_ref_obj->set ("name", new json::string ("cwe"));
719
720 return comp_ref_obj;
721}
722
79aaba0a
DM
723/* Make an array suitable for use as the "locations" property of:
724 - a "result" object (SARIF v2.1.0 section 3.27.12), or
725 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
726
727json::array *
728sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
729{
730 json::array *locations_arr = new json::array ();
731 const logical_location *logical_loc = NULL;
732 if (m_context->m_client_data_hooks)
733 logical_loc
734 = m_context->m_client_data_hooks->get_current_logical_location ();
735
736 json::object *location_obj
737 = make_location_object (*diagnostic->richloc, logical_loc);
738 locations_arr->append (location_obj);
739 return locations_arr;
740}
741
6cf276dd
DM
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);
753 json::array *location_locs_arr = new json::array ();
754 location_locs_arr->append (logical_loc_obj);
755 location_obj->set ("logicalLocations", 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 ("physicalLocation", 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 ("physicalLocation", 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 (false);
f858fe7a 799 json::object *message_obj = make_message_object (ev_desc.get ());
6cf276dd 800 location_obj->set ("message", message_obj);
6cf276dd
DM
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{
2e8a0553 812 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
6cf276dd
DM
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 ("artifactLocation", 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 ("region", 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 ("contextRegion", 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 ("uri", 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 ("uriBaseId", 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 (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 ("uri", new json::string (pwd));
912 free (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 ("startLine", new json::integer_number (exploc_start.line));
954
955 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
956 region_obj->set ("startColumn",
957 new json::integer_number (get_sarif_column (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 ("endLine", 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_finish) + 1;
967 region_obj->set ("endColumn", 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 ("startLine", 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 ("endLine", 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 (exploc_start.file,
1013 exploc_start.line,
1014 exploc_finish.line))
1015 region_obj->set ("snippet", 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 ("startLine", 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_start);
1039 region_obj->set ("startColumn",
1040 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 ("endLine", 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_next);
1049 region_obj->set ("endColumn", 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 ("name", 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 ("fullyQualifiedName",
1103 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 ("decoratedName", 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 ("kind", 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
3a1e9f3e 1124 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
6cf276dd 1125 json::array *thread_flows_arr = new json::array ();
6cf276dd 1126
3a1e9f3e
DM
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;
6cf276dd
DM
1131 for (unsigned i = 0; i < path.num_events (); i++)
1132 {
1133 const diagnostic_event &event = path.get_event (i);
3a1e9f3e
DM
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 (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 (thread_flow_obj);
1144 thread_id_map.put (thread_id, thread_flow_obj);
1145 }
1146
1147 /* Add event to thread's threadFlow object. */
6cf276dd 1148 json::object *thread_flow_loc_obj
3a1e9f3e
DM
1149 = make_thread_flow_location_object (event, i);
1150 thread_flow_obj->add_location (thread_flow_loc_obj);
6cf276dd 1151 }
3a1e9f3e 1152 code_flow_obj->set ("threadFlows", thread_flows_arr);
6cf276dd 1153
3a1e9f3e 1154 return code_flow_obj;
6cf276dd
DM
1155}
1156
1157/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1158
1159json::object *
3a1e9f3e
DM
1160sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1161 int path_event_idx)
6cf276dd
DM
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 (ev);
1167 thread_flow_loc_obj->set ("location", 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 ("kinds", kinds_arr);
1173
1174 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
1175 thread_flow_loc_obj->set ("nestingLevel",
1176 new json::integer_number (ev.get_stack_depth ()));
1177
3a1e9f3e
DM
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 ("executionOrder",
1181 new json::integer_number (path_event_idx + 1));
1182
6cf276dd
DM
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 (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 (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 (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 ("text", new json::string (msg));
1226
1227 return message_obj;
1228}
1229
4f01ae37
DM
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 ("text", 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 (context->printer, " ");
1250 pp_set_prefix (context->printer, saved_prefix);
1251
1252 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
1253 message_obj->set ("markdown",
1254 new json::string (pp_formatted_text (context->printer)));
1255
1256 pp_clear_output_area (context->printer);
1257
1258 return message_obj;
1259}
1260
6cf276dd
DM
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 ("text", 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).
79aaba0a 1279 Take ownership of INVOCATION_OBJ and RESULTS. */
6cf276dd
DM
1280
1281json::object *
79aaba0a
DM
1282sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1283 json::array *results)
6cf276dd
DM
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 ("$schema", new json::string (SARIF_SCHEMA));
1289
1290 /* "version" property (SARIF v2.1.0 section 3.13.2). */
1291 log_obj->set ("version", 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 ();
79aaba0a 1295 json::object *run_obj = make_run_object (invocation_obj, results);
6cf276dd
DM
1296 run_arr->append (run_obj);
1297 log_obj->set ("runs", run_arr);
1298
1299 return log_obj;
1300}
1301
1302/* Make a run object (SARIF v2.1.0 section 3.14).
79aaba0a 1303 Take ownership of INVOCATION_OBJ and RESULTS. */
6cf276dd
DM
1304
1305json::object *
79aaba0a
DM
1306sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1307 json::array *results)
6cf276dd
DM
1308{
1309 json::object *run_obj = new json::object ();
1310
1311 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1312 json::object *tool_obj = make_tool_object ();
1313 run_obj->set ("tool", tool_obj);
1314
1315 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1316 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1317 run_obj->set ("taxonomies", taxonomies_arr);
1318
79aaba0a
DM
1319 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1320 {
1321 json::array *invocations_arr = new json::array ();
1322 invocations_arr->append (invocation_obj);
1323 run_obj->set ("invocations", invocations_arr);
1324 }
1325
6cf276dd
DM
1326 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1327 if (m_seen_any_relative_paths)
1328 {
1329 json::object *orig_uri_base_ids = new json::object ();
1330 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1331 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1332 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1333 }
1334
1335 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1336 json::array *artifacts_arr = new json::array ();
1337 for (auto iter : m_filenames)
1338 {
1339 json::object *artifact_obj = make_artifact_object (iter);
1340 artifacts_arr->append (artifact_obj);
1341 }
1342 run_obj->set ("artifacts", artifacts_arr);
1343
1344 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1345 run_obj->set ("results", results);
1346
1347 return run_obj;
1348}
1349
1350/* Make a tool object (SARIF v2.1.0 section 3.18). */
1351
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 ("driver", driver_obj);
1360
1361 /* Report plugins via the "extensions" property
1362 (SARIF v2.1.0 section 3.18.3). */
1363 if (m_context->m_client_data_hooks)
1364 if (const client_version_info *vinfo
1365 = m_context->m_client_data_hooks->get_any_version_info ())
1366 {
1367 class my_plugin_visitor : public client_version_info :: plugin_visitor
1368 {
1369 public:
1370 void on_plugin (const diagnostic_client_plugin_info &p) final override
1371 {
1372 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1373 for the plugin. */
1374 json::object *plugin_obj = new json::object ();
1375 m_plugin_objs.safe_push (plugin_obj);
1376
1377 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1378 if (const char *short_name = p.get_short_name ())
1379 plugin_obj->set ("name", 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 ("fullName", 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 ("version", 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 ("extensions", extensions_arr);
1397 for (auto iter : v.m_plugin_objs)
1398 extensions_arr->append (iter);
1399 }
1400 }
1401
1402 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1403 "extensions" (see toplev.cc: print_version). */
1404
1405 return tool_obj;
1406}
1407
1408/* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1409 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1410
1411json::object *
1412sarif_builder::make_driver_tool_component_object () const
1413{
1414 json::object *driver_obj = new json::object ();
1415
1416 if (m_context->m_client_data_hooks)
1417 if (const client_version_info *vinfo
1418 = m_context->m_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 ("name", 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 ("fullName", new json::string (full_name));
1428 free (full_name);
1429 }
1430
1431 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1432 if (const char *version = vinfo->get_version_string ())
1433 driver_obj->set ("version", 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 ("informationUri", new json::string (version_url));
1439 free (version_url);
1440 }
1441 }
1442
1443 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1444 driver_obj->set ("rules", m_rules_arr);
1445
1446 return driver_obj;
1447}
1448
1449/* If we've seen any CWE IDs, make an array for the "taxonomies" property
1450 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1451 toolComponent (3.19) as per 3.19.3, representing the CWE.
1452
1453 Otherwise return NULL. */
1454
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 (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 ("name", new json::string ("CWE"));
1484
1485 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1486 taxonomy_obj->set ("version", new json::string ("4.7"));
1487
1488 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
1489 taxonomy_obj->set ("organization", 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 ("The MITRE"
1494 " Common Weakness Enumeration");
1495 taxonomy_obj->set ("shortDescription", short_desc);
1496
1497 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1498 json::array *taxa_arr = new json::array ();
1499 for (auto cwe_id : m_cwe_id_set)
1500 {
1501 json::object *cwe_taxon
1502 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1503 taxa_arr->append (cwe_taxon);
1504 }
1505 taxonomy_obj->set ("taxa", taxa_arr);
1506
1507 return taxonomy_obj;
1508}
1509
1510/* Make an artifact object (SARIF v2.1.0 section 3.24). */
1511
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 ("location", artifact_loc_obj);
1520
1521 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1522 if (json::object *artifact_content_obj
1523 = maybe_make_artifact_content_object (filename))
1524 artifact_obj->set ("contents", artifact_content_obj);
1525
1526 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
1527 if (m_context->m_client_data_hooks)
1528 if (const char *source_lang
1529 = m_context->m_client_data_hooks->maybe_get_sarif_source_language
1530 (filename))
1531 artifact_obj->set ("sourceLanguage", new json::string (source_lang));
1532
1533 return artifact_obj;
1534}
1535
6cf276dd
DM
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{
d495ea2b
DM
1542 /* Let input.cc handle any charset conversion. */
1543 char_span utf8_content = get_source_file_content (filename);
1544 if (!utf8_content)
6cf276dd
DM
1545 return NULL;
1546
d495ea2b
DM
1547 /* Don't add it if it's not valid UTF-8. */
1548 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1549 return NULL;
6cf276dd 1550
d495ea2b
DM
1551 json::object *artifact_content_obj = new json::object ();
1552 artifact_content_obj->set ("text",
1553 new json::string (utf8_content.get_buffer (),
1554 utf8_content.length ()));
6cf276dd
DM
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
1561static char *
1562get_source_lines (const char *filename,
1563 int start_line,
1564 int end_line)
1565{
1566 auto_vec<char> result;
1567
1568 for (int line = start_line; line <= end_line; line++)
1569 {
1570 char_span line_content = location_get_source_line (filename, line);
1571 if (!line_content.get_buffer ())
1572 return NULL;
1573 result.reserve (line_content.length () + 1);
1574 for (size_t i = 0; i < line_content.length (); i++)
1575 result.quick_push (line_content[i]);
1576 result.quick_push ('\n');
1577 }
1578 result.safe_push ('\0');
1579
1580 return xstrdup (result.address ());
1581}
1582
1583/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1584 run of lines within FILENAME (including the endpoints). */
1585
1586json::object *
1587sarif_builder::maybe_make_artifact_content_object (const char *filename,
1588 int start_line,
1589 int end_line) const
1590{
1591 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1592
1593 if (!text_utf8)
1594 return NULL;
1595
d495ea2b
DM
1596 /* Don't add it if it's not valid UTF-8. */
1597 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1598 {
1599 free (text_utf8);
1600 return NULL;
1601 }
1602
6cf276dd
DM
1603 json::object *artifact_content_obj = new json::object ();
1604 artifact_content_obj->set ("text", new json::string (text_utf8));
1605 free (text_utf8);
1606
1607 return artifact_content_obj;
1608}
1609
1610/* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1611
1612json::object *
1613sarif_builder::make_fix_object (const rich_location &richloc)
1614{
1615 json::object *fix_obj = new json::object ();
1616
1617 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1618 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1619 json::array *artifact_change_arr = new json::array ();
1620 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1621 artifact_change_arr->append (artifact_change_obj);
1622 fix_obj->set ("artifactChanges", artifact_change_arr);
1623
1624 return fix_obj;
1625}
1626
1627/* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1628
1629json::object *
1630sarif_builder::make_artifact_change_object (const rich_location &richloc)
1631{
1632 json::object *artifact_change_obj = new json::object ();
1633
1634 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1635 json::object *artifact_location_obj
1636 = make_artifact_location_object (richloc.get_loc ());
1637 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1638
1639 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1640 json::array *replacement_arr = new json::array ();
1641 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1642 {
1643 const fixit_hint *hint = richloc.get_fixit_hint (i);
1644 json::object *replacement_obj = make_replacement_object (*hint);
1645 replacement_arr->append (replacement_obj);
1646 }
1647 artifact_change_obj->set ("replacements", replacement_arr);
1648
1649 return artifact_change_obj;
1650}
1651
1652/* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1653
1654json::object *
1655sarif_builder::make_replacement_object (const fixit_hint &hint) const
1656{
1657 json::object *replacement_obj = new json::object ();
1658
1659 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1660 json::object *region_obj = make_region_object_for_hint (hint);
1661 replacement_obj->set ("deletedRegion", region_obj);
1662
1663 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1664 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1665 replacement_obj->set ("insertedContent", content_obj);
1666
1667 return replacement_obj;
1668}
1669
1670/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1671
1672json::object *
1673sarif_builder::make_artifact_content_object (const char *text) const
1674{
1675 json::object *content_obj = new json::object ();
1676
1677 /* "text" property (SARIF v2.1.0 section 3.3.2). */
1678 content_obj->set ("text", new json::string (text));
1679
1680 return content_obj;
1681}
1682
1683/* No-op implementation of "begin_diagnostic" for SARIF output. */
1684
1685static void
1686sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
1687{
1688}
1689
1690/* Implementation of "end_diagnostic" for SARIF output. */
1691
1692static void
1693sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
1694 diagnostic_t orig_diag_kind)
1695{
1696 gcc_assert (the_builder);
1697 the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
1698}
1699
1700/* No-op implementation of "begin_group_cb" for SARIF output. */
1701
1702static void
1703sarif_begin_group (diagnostic_context *)
1704{
1705}
1706
1707/* Implementation of "end_group_cb" for SARIF output. */
1708
1709static void
1710sarif_end_group (diagnostic_context *)
1711{
1712 gcc_assert (the_builder);
1713 the_builder->end_group ();
1714}
1715
1716/* Flush the top-level array to OUTF. */
1717
1718static void
1719sarif_flush_to_file (FILE *outf)
1720{
1721 gcc_assert (the_builder);
1722 the_builder->flush_to_file (outf);
1723 delete the_builder;
1724 the_builder = NULL;
1725}
1726
1727/* Callback for final cleanup for SARIF output to stderr. */
1728
1729static void
1730sarif_stderr_final_cb (diagnostic_context *)
1731{
1732 gcc_assert (the_builder);
1733 sarif_flush_to_file (stderr);
1734}
1735
1736static char *sarif_output_base_file_name;
1737
1738/* Callback for final cleanup for SARIF output to a file. */
1739
1740static void
1741sarif_file_final_cb (diagnostic_context *)
1742{
1743 char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
1744 FILE *outf = fopen (filename, "w");
1745 if (!outf)
1746 {
1747 const char *errstr = xstrerror (errno);
1748 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1749 filename, errstr);
1750 free (filename);
1751 return;
1752 }
1753 gcc_assert (the_builder);
1754 sarif_flush_to_file (outf);
1755 fclose (outf);
1756 free (filename);
1757}
1758
79aaba0a
DM
1759/* Callback for diagnostic_context::ice_handler_cb for when an ICE
1760 occurs. */
1761
1762static void
1763sarif_ice_handler (diagnostic_context *context)
1764{
1765 /* Attempt to ensure that a .sarif file is written out. */
1766 diagnostic_finish (context);
1767
1768 /* Print a header for the remaining output to stderr, and
1769 return, attempting to print the usual ICE messages to
1770 stderr. Hopefully this will be helpful to the user in
1771 indicating what's gone wrong (also for DejaGnu, for pruning
1772 those messages). */
1773 fnotice (stderr, "Internal compiler error:\n");
1774}
1775
4f01ae37
DM
1776/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */
1777
1778static void
1779sarif_emit_diagram (diagnostic_context *context,
1780 const diagnostic_diagram &diagram)
1781{
1782 gcc_assert (the_builder);
1783 the_builder->emit_diagram (context, diagram);
1784}
1785
6cf276dd
DM
1786/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1787 to a file). */
1788
1789static void
1790diagnostic_output_format_init_sarif (diagnostic_context *context)
1791{
1792 the_builder = new sarif_builder (context);
1793
1794 /* Override callbacks. */
1795 context->begin_diagnostic = sarif_begin_diagnostic;
1796 context->end_diagnostic = sarif_end_diagnostic;
1797 context->begin_group_cb = sarif_begin_group;
1798 context->end_group_cb = sarif_end_group;
1799 context->print_path = NULL; /* handled in sarif_end_diagnostic. */
79aaba0a 1800 context->ice_handler_cb = sarif_ice_handler;
4f01ae37 1801 context->m_diagrams.m_emission_cb = sarif_emit_diagram;
6cf276dd
DM
1802
1803 /* The metadata is handled in SARIF format, rather than as text. */
1804 context->show_cwe = false;
0b14f590 1805 context->show_rules = false;
6cf276dd
DM
1806
1807 /* The option is handled in SARIF format, rather than as text. */
1808 context->show_option_requested = false;
1809
1810 /* Don't colorize the text. */
1811 pp_show_color (context->printer) = false;
1812}
1813
1814/* Populate CONTEXT in preparation for SARIF output to stderr. */
1815
1816void
1817diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1818{
1819 diagnostic_output_format_init_sarif (context);
1820 context->final_cb = sarif_stderr_final_cb;
1821}
1822
1823/* Populate CONTEXT in preparation for SARIF output to a file named
1824 BASE_FILE_NAME.sarif. */
1825
1826void
1827diagnostic_output_format_init_sarif_file (diagnostic_context *context,
1828 const char *base_file_name)
1829{
1830 diagnostic_output_format_init_sarif (context);
1831 context->final_cb = sarif_file_final_cb;
1832 sarif_output_base_file_name = xstrdup (base_file_name);
1833}