]> gcc.gnu.org Git - gcc.git/blame - gcc/diagnostic-format-sarif.cc
ada: Another couple of cleanups in the finalization machinery
[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);
14082026
DM
218 char *get_source_lines (const char *filename,
219 int start_line,
220 int end_line) const;
6cf276dd
DM
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
79aaba0a
DM
233 /* The JSON object for the invocation object. */
234 sarif_invocation *m_invocation_obj;
235
6cf276dd
DM
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
75d62394
DM
254/* class sarif_object : public json::object. */
255
256sarif_property_bag &
257sarif_object::get_or_create_properties ()
258{
259 json::value *properties_val = get ("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 ("properties", bag);
268 return *bag;
269}
270
271/* class sarif_invocation : public sarif_object. */
79aaba0a
DM
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 (notification_obj);
286}
287
288void
75d62394 289sarif_invocation::prepare_to_flush (diagnostic_context *context)
79aaba0a
DM
290{
291 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
070944fd 292 set_bool ("executionSuccessful", m_success);
79aaba0a
DM
293
294 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
295 set ("toolExecutionNotifications", m_notifications_arr);
75d62394
DM
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. */
8200cd97
DM
299 if (auto client_data_hooks = context->get_client_data_hooks ())
300 client_data_hooks->add_sarif_invocation_properties (*this);
79aaba0a
DM
301}
302
75d62394 303/* class sarif_result : public sarif_object. */
6cf276dd
DM
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{
6cf276dd
DM
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 (*diagnostic->richloc, NULL);
322 json::object *message_obj
323 = builder->make_message_object (pp_formatted_text (context->printer));
324 pp_clear_output_area (context->printer);
325 location_obj->set ("message", message_obj);
326
4f01ae37
DM
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 ("message", 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 ("relatedLocations", m_related_locations_arr);
359 }
6cf276dd
DM
360 m_related_locations_arr->append (location_obj);
361}
362
75d62394 363/* class sarif_ice_notification : public sarif_object. */
79aaba0a
DM
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 ("locations", locations_arr);
375
376 /* "message" property (SARIF v2.1.0 section 3.85.5). */
377 json::object *message_obj
378 = builder->make_message_object (pp_formatted_text (context->printer));
379 pp_clear_output_area (context->printer);
380 set ("message", message_obj);
381
382 /* "level" property (SARIF v2.1.0 section 3.58.6). */
070944fd 383 set_string ("level", "error");
79aaba0a
DM
384}
385
3a1e9f3e
DM
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 (false));
070944fd 392 set_string ("id", name.get ());
3a1e9f3e
DM
393
394 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
395 m_locations_arr = new json::array ();
396 set ("locations", m_locations_arr);
397}
398
6cf276dd
DM
399/* class sarif_builder. */
400
401/* sarif_builder's ctor. */
402
403sarif_builder::sarif_builder (diagnostic_context *context)
404: m_context (context),
79aaba0a 405 m_invocation_obj (new sarif_invocation ()),
6cf276dd
DM
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 ()),
8200cd97 411 m_tabstop (context->m_tabstop)
6cf276dd
DM
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{
79aaba0a
DM
422 if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
423 {
424 m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
425 return;
426 }
6cf276dd
DM
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 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 (result_obj);
440 m_cur_group_result = result_obj;
441 }
442}
443
4f01ae37
DM
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, this);
454}
455
6cf276dd
DM
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{
75d62394 472 m_invocation_obj->prepare_to_flush (m_context);
79aaba0a 473 json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
6cf276dd 474 top->dump (outf);
79aaba0a 475 m_invocation_obj = NULL;
6cf276dd
DM
476 m_results_array = NULL;
477 fprintf (outf, "\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 (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
353f146c
DM
539 = context->make_option_name (diagnostic->option_index,
540 orig_diag_kind, diagnostic->kind))
6cf276dd
DM
541 {
542 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
543 Set ruleId referencing them. */
070944fd 544 result_obj->set_string ("ruleId", option_text);
6cf276dd
DM
545 if (m_rule_id_set.contains (option_text))
546 free (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 (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 (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 (orig_diag_kind);
070944fd 568 result_obj->set_string ("ruleId", rule_id);
6cf276dd
DM
569 free (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 (cwe_id_obj);
580 result_obj->set ("taxa", 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 (diagnostic->kind))
070944fd 585 result_obj->set_string ("level", sarif_level);
6cf276dd
DM
586
587 /* "message" property (SARIF v2.1.0 section 3.27.11). */
588 json::object *message_obj
589 = make_message_object (pp_formatted_text (context->printer));
590 pp_clear_output_area (context->printer);
591 result_obj->set ("message", message_obj);
592
593 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
79aaba0a 594 json::array *locations_arr = make_locations_arr (diagnostic);
6cf276dd
DM
595 result_obj->set ("locations", 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);
602 code_flows_arr->append (code_flow_obj);
603 result_obj->set ("codeFlows", 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 (*richloc);
616 fix_arr->append (fix_obj);
617 result_obj->set ("fixes", 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). */
070944fd 636 reporting_desc->set_string ("id", option_text);
6cf276dd
DM
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). */
353f146c 642 if (char *option_url = context->make_option_url (diagnostic->option_index))
6cf276dd 643 {
353f146c
DM
644 reporting_desc->set_string ("helpUri", option_url);
645 free (option_url);
6cf276dd
DM
646 }
647
648 return reporting_desc;
649}
650
651/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
652 for CWE_ID, for use within the CWE taxa array. */
653
654json::object *
655sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
656{
657 json::object *reporting_desc = new json::object ();
658
659 /* "id" property (SARIF v2.1.0 section 3.49.3). */
660 {
661 pretty_printer pp;
662 pp_printf (&pp, "%i", cwe_id);
070944fd 663 reporting_desc->set_string ("id", pp_formatted_text (&pp));
6cf276dd
DM
664 }
665
666 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
667 {
668 char *url = get_cwe_url (cwe_id);
070944fd 669 reporting_desc->set_string ("helpUri", url);
6cf276dd
DM
670 free (url);
671 }
672
673 return reporting_desc;
674}
675
676/* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
677 referencing CWE_ID, for use within a result object.
678 Also, add CWE_ID to m_cwe_id_set. */
679
680json::object *
681sarif_builder::
682make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
683{
684 json::object *desc_ref_obj = new json::object ();
685
686 /* "id" property (SARIF v2.1.0 section 3.52.4). */
687 {
688 pretty_printer pp;
689 pp_printf (&pp, "%i", cwe_id);
070944fd 690 desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
6cf276dd
DM
691 }
692
693 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
694 json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
695 desc_ref_obj->set ("toolComponent", comp_ref_obj);
696
697 /* Add CWE_ID to our set. */
698 gcc_assert (cwe_id > 0);
699 m_cwe_id_set.add (cwe_id);
700
701 return desc_ref_obj;
702}
703
704/* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
705 references the CWE taxonomy. */
706
707json::object *
708sarif_builder::
709make_tool_component_reference_object_for_cwe () const
710{
711 json::object *comp_ref_obj = new json::object ();
712
713 /* "name" property (SARIF v2.1.0 section 3.54.3). */
070944fd 714 comp_ref_obj->set_string ("name", "cwe");
6cf276dd
DM
715
716 return comp_ref_obj;
717}
718
79aaba0a
DM
719/* Make an array suitable for use as the "locations" property of:
720 - a "result" object (SARIF v2.1.0 section 3.27.12), or
721 - a "notification" object (SARIF v2.1.0 section 3.58.4). */
722
723json::array *
724sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
725{
726 json::array *locations_arr = new json::array ();
727 const logical_location *logical_loc = NULL;
8200cd97
DM
728 if (auto client_data_hooks = m_context->get_client_data_hooks ())
729 logical_loc = client_data_hooks->get_current_logical_location ();
79aaba0a
DM
730
731 json::object *location_obj
732 = make_location_object (*diagnostic->richloc, logical_loc);
733 locations_arr->append (location_obj);
734 return locations_arr;
735}
736
6cf276dd
DM
737/* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
738 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
739
740void
741sarif_builder::
742set_any_logical_locs_arr (json::object *location_obj,
743 const logical_location *logical_loc)
744{
745 if (!logical_loc)
746 return;
747 json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
748 json::array *location_locs_arr = new json::array ();
749 location_locs_arr->append (logical_loc_obj);
750 location_obj->set ("logicalLocations", location_locs_arr);
751}
752
753/* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
754 and LOGICAL_LOC. */
755
756json::object *
757sarif_builder::make_location_object (const rich_location &rich_loc,
758 const logical_location *logical_loc)
759{
760 json::object *location_obj = new json::object ();
761
762 /* Get primary loc from RICH_LOC. */
763 location_t loc = rich_loc.get_loc ();
764
765 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
766 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
767 location_obj->set ("physicalLocation", phs_loc_obj);
768
769 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
770 set_any_logical_locs_arr (location_obj, logical_loc);
771
772 return location_obj;
773}
774
775/* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
776 within a diagnostic_path. */
777
778json::object *
779sarif_builder::make_location_object (const diagnostic_event &event)
780{
781 json::object *location_obj = new json::object ();
782
783 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
784 location_t loc = event.get_location ();
785 if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
786 location_obj->set ("physicalLocation", phs_loc_obj);
787
788 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
789 const logical_location *logical_loc = event.get_logical_location ();
790 set_any_logical_locs_arr (location_obj, logical_loc);
791
792 /* "message" property (SARIF v2.1.0 section 3.28.5). */
793 label_text ev_desc = event.get_desc (false);
f858fe7a 794 json::object *message_obj = make_message_object (ev_desc.get ());
6cf276dd 795 location_obj->set ("message", message_obj);
6cf276dd
DM
796
797 return location_obj;
798}
799
800/* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
801 or return NULL;
802 Add any filename to the m_artifacts. */
803
804json::object *
805sarif_builder::maybe_make_physical_location_object (location_t loc)
806{
2e8a0553 807 if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL)
6cf276dd
DM
808 return NULL;
809
810 json::object *phys_loc_obj = new json::object ();
811
812 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
813 json::object *artifact_loc_obj = make_artifact_location_object (loc);
814 phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
815 m_filenames.add (LOCATION_FILE (loc));
816
817 /* "region" property (SARIF v2.1.0 section 3.29.4). */
818 if (json::object *region_obj = maybe_make_region_object (loc))
819 phys_loc_obj->set ("region", region_obj);
820
821 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
822 if (json::object *context_region_obj
823 = maybe_make_region_object_for_context (loc))
824 phys_loc_obj->set ("contextRegion", context_region_obj);
825
826 /* Instead, we add artifacts to the run as a whole,
827 with artifact.contents.
828 Could do both, though. */
829
830 return phys_loc_obj;
831}
832
833/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
834 or return NULL. */
835
836json::object *
837sarif_builder::make_artifact_location_object (location_t loc)
838{
839 return make_artifact_location_object (LOCATION_FILE (loc));
840}
841
842/* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
843 for when we need to express paths relative to PWD. */
844
845#define PWD_PROPERTY_NAME ("PWD")
846
847/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
848 or return NULL. */
849
850json::object *
851sarif_builder::make_artifact_location_object (const char *filename)
852{
853 json::object *artifact_loc_obj = new json::object ();
854
855 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
070944fd 856 artifact_loc_obj->set_string ("uri", filename);
6cf276dd
DM
857
858 if (filename[0] != '/')
859 {
860 /* If we have a relative path, set the "uriBaseId" property
861 (SARIF v2.1.0 section 3.4.4). */
070944fd 862 artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
6cf276dd
DM
863 m_seen_any_relative_paths = true;
864 }
865
866 return artifact_loc_obj;
867}
868
869/* Get the PWD, or NULL, as an absolute file-based URI,
870 adding a trailing forward slash (as required by SARIF v2.1.0
871 section 3.14.14). */
872
873static char *
874make_pwd_uri_str ()
875{
876 /* The prefix of a file-based URI, up to, but not including the path. */
877#define FILE_PREFIX ("file://")
878
879 const char *pwd = getpwd ();
880 if (!pwd)
881 return NULL;
882 size_t len = strlen (pwd);
883 if (len == 0 || pwd[len - 1] != '/')
884 return concat (FILE_PREFIX, pwd, "/", NULL);
885 else
886 {
887 gcc_assert (pwd[len - 1] == '/');
888 return concat (FILE_PREFIX, pwd, NULL);
889 }
890}
891
892/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
893 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
894 section 3.14.14) when we have any relative paths. */
895
896json::object *
897sarif_builder::make_artifact_location_object_for_pwd () const
898{
899 json::object *artifact_loc_obj = new json::object ();
900
901 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
902 if (char *pwd = make_pwd_uri_str ())
903 {
904 gcc_assert (strlen (pwd) > 0);
905 gcc_assert (pwd[strlen (pwd) - 1] == '/');
070944fd 906 artifact_loc_obj->set_string ("uri", pwd);
6cf276dd
DM
907 free (pwd);
908 }
909
910 return artifact_loc_obj;
911}
912
913/* Get the column number within EXPLOC. */
914
915int
916sarif_builder::get_sarif_column (expanded_location exploc) const
917{
918 cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
1bdd665a
DM
919 return location_compute_display_column (m_context->get_file_cache (),
920 exploc, policy);
6cf276dd
DM
921}
922
923/* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
924 or return NULL. */
925
926json::object *
927sarif_builder::maybe_make_region_object (location_t loc) const
928{
929 location_t caret_loc = get_pure_location (loc);
930
931 if (caret_loc <= BUILTINS_LOCATION)
932 return NULL;
933
934 location_t start_loc = get_start (loc);
935 location_t finish_loc = get_finish (loc);
936
937 expanded_location exploc_caret = expand_location (caret_loc);
938 expanded_location exploc_start = expand_location (start_loc);
939 expanded_location exploc_finish = expand_location (finish_loc);
940
941 if (exploc_start.file !=exploc_caret.file)
942 return NULL;
943 if (exploc_finish.file !=exploc_caret.file)
944 return NULL;
945
946 json::object *region_obj = new json::object ();
947
948 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
070944fd 949 region_obj->set_integer ("startLine", exploc_start.line);
6cf276dd
DM
950
951 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
070944fd 952 region_obj->set_integer ("startColumn", get_sarif_column (exploc_start));
6cf276dd
DM
953
954 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
955 if (exploc_finish.line != exploc_start.line)
070944fd 956 region_obj->set_integer ("endLine", exploc_finish.line);
6cf276dd
DM
957
958 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
959 This expresses the column immediately beyond the range. */
960 {
961 int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
070944fd 962 region_obj->set_integer ("endColumn", next_column);
6cf276dd
DM
963 }
964
965 return region_obj;
966}
967
968/* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
969 property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
970
971 This is similar to maybe_make_region_object, but ignores column numbers,
972 covering the line(s) as a whole, and including a "snippet" property
973 embedding those source lines, making it easier for consumers to show
974 the pertinent source. */
975
976json::object *
977sarif_builder::maybe_make_region_object_for_context (location_t loc) const
978{
979 location_t caret_loc = get_pure_location (loc);
980
981 if (caret_loc <= BUILTINS_LOCATION)
982 return NULL;
983
984 location_t start_loc = get_start (loc);
985 location_t finish_loc = get_finish (loc);
986
987 expanded_location exploc_caret = expand_location (caret_loc);
988 expanded_location exploc_start = expand_location (start_loc);
989 expanded_location exploc_finish = expand_location (finish_loc);
990
991 if (exploc_start.file !=exploc_caret.file)
992 return NULL;
993 if (exploc_finish.file !=exploc_caret.file)
994 return NULL;
995
996 json::object *region_obj = new json::object ();
997
998 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
070944fd 999 region_obj->set_integer ("startLine", exploc_start.line);
6cf276dd
DM
1000
1001 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1002 if (exploc_finish.line != exploc_start.line)
070944fd 1003 region_obj->set_integer ("endLine", exploc_finish.line);
6cf276dd
DM
1004
1005 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
1006 if (json::object *artifact_content_obj
1007 = maybe_make_artifact_content_object (exploc_start.file,
1008 exploc_start.line,
1009 exploc_finish.line))
1010 region_obj->set ("snippet", artifact_content_obj);
1011
1012 return region_obj;
1013}
1014
1015/* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
1016 of HINT (as per SARIF v2.1.0 section 3.57.3). */
1017
1018json::object *
1019sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
1020{
1021 location_t start_loc = hint.get_start_loc ();
1022 location_t next_loc = hint.get_next_loc ();
1023
1024 expanded_location exploc_start = expand_location (start_loc);
1025 expanded_location exploc_next = expand_location (next_loc);
1026
1027 json::object *region_obj = new json::object ();
1028
1029 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
070944fd 1030 region_obj->set_integer ("startLine", exploc_start.line);
6cf276dd
DM
1031
1032 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
1033 int start_col = get_sarif_column (exploc_start);
070944fd 1034 region_obj->set_integer ("startColumn", start_col);
6cf276dd
DM
1035
1036 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
1037 if (exploc_next.line != exploc_start.line)
070944fd 1038 region_obj->set_integer ("endLine", exploc_next.line);
6cf276dd
DM
1039
1040 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
1041 This expresses the column immediately beyond the range. */
1042 int next_col = get_sarif_column (exploc_next);
070944fd 1043 region_obj->set_integer ("endColumn", next_col);
6cf276dd
DM
1044
1045 return region_obj;
1046}
1047
1048/* Attempt to get a string for a logicalLocation's "kind" property
1049 (SARIF v2.1.0 section 3.33.7).
1050 Return NULL if unknown. */
1051
1052static const char *
1053maybe_get_sarif_kind (enum logical_location_kind kind)
1054{
1055 switch (kind)
1056 {
1057 default:
1058 gcc_unreachable ();
1059 case LOGICAL_LOCATION_KIND_UNKNOWN:
1060 return NULL;
1061
1062 case LOGICAL_LOCATION_KIND_FUNCTION:
1063 return "function";
1064 case LOGICAL_LOCATION_KIND_MEMBER:
1065 return "member";
1066 case LOGICAL_LOCATION_KIND_MODULE:
1067 return "module";
1068 case LOGICAL_LOCATION_KIND_NAMESPACE:
1069 return "namespace";
1070 case LOGICAL_LOCATION_KIND_TYPE:
1071 return "type";
1072 case LOGICAL_LOCATION_KIND_RETURN_TYPE:
1073 return "returnType";
1074 case LOGICAL_LOCATION_KIND_PARAMETER:
1075 return "parameter";
1076 case LOGICAL_LOCATION_KIND_VARIABLE:
1077 return "variable";
1078 }
1079}
1080
1081/* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
1082 or return NULL. */
1083
1084json::object *
1085sarif_builder::
1086make_logical_location_object (const logical_location &logical_loc) const
1087{
1088 json::object *logical_loc_obj = new json::object ();
1089
1090 /* "name" property (SARIF v2.1.0 section 3.33.4). */
1091 if (const char *short_name = logical_loc.get_short_name ())
070944fd 1092 logical_loc_obj->set_string ("name", short_name);
6cf276dd
DM
1093
1094 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
1095 if (const char *name_with_scope = logical_loc.get_name_with_scope ())
070944fd 1096 logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope);
6cf276dd
DM
1097
1098 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
1099 if (const char *internal_name = logical_loc.get_internal_name ())
070944fd 1100 logical_loc_obj->set_string ("decoratedName", internal_name);
6cf276dd
DM
1101
1102 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
1103 enum logical_location_kind kind = logical_loc.get_kind ();
1104 if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
070944fd 1105 logical_loc_obj->set_string ("kind", sarif_kind_str);
6cf276dd
DM
1106
1107 return logical_loc_obj;
1108}
1109
1110/* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
1111
1112json::object *
1113sarif_builder::make_code_flow_object (const diagnostic_path &path)
1114{
1115 json::object *code_flow_obj = new json::object ();
1116
3a1e9f3e 1117 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
6cf276dd 1118 json::array *thread_flows_arr = new json::array ();
6cf276dd 1119
3a1e9f3e
DM
1120 /* Walk the events, consolidating into per-thread threadFlow objects,
1121 using the index with PATH as the overall executionOrder. */
1122 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1123 sarif_thread_flow *> thread_id_map;
6cf276dd
DM
1124 for (unsigned i = 0; i < path.num_events (); i++)
1125 {
1126 const diagnostic_event &event = path.get_event (i);
3a1e9f3e
DM
1127 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1128 sarif_thread_flow *thread_flow_obj;
1129
1130 if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1131 thread_flow_obj = *slot;
1132 else
1133 {
1134 const diagnostic_thread &thread = path.get_thread (thread_id);
1135 thread_flow_obj = new sarif_thread_flow (thread);
1136 thread_flows_arr->append (thread_flow_obj);
1137 thread_id_map.put (thread_id, thread_flow_obj);
1138 }
1139
1140 /* Add event to thread's threadFlow object. */
6cf276dd 1141 json::object *thread_flow_loc_obj
3a1e9f3e
DM
1142 = make_thread_flow_location_object (event, i);
1143 thread_flow_obj->add_location (thread_flow_loc_obj);
6cf276dd 1144 }
3a1e9f3e 1145 code_flow_obj->set ("threadFlows", thread_flows_arr);
6cf276dd 1146
3a1e9f3e 1147 return code_flow_obj;
6cf276dd
DM
1148}
1149
1150/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
1151
1152json::object *
3a1e9f3e
DM
1153sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1154 int path_event_idx)
6cf276dd
DM
1155{
1156 json::object *thread_flow_loc_obj = new json::object ();
1157
1158 /* "location" property (SARIF v2.1.0 section 3.38.3). */
1159 json::object *location_obj = make_location_object (ev);
1160 thread_flow_loc_obj->set ("location", location_obj);
1161
1162 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
1163 diagnostic_event::meaning m = ev.get_meaning ();
1164 if (json::array *kinds_arr = maybe_make_kinds_array (m))
1165 thread_flow_loc_obj->set ("kinds", kinds_arr);
1166
1167 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
070944fd 1168 thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ());
6cf276dd 1169
3a1e9f3e
DM
1170 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
1171 Offset by 1 to match the human-readable values emitted by %@. */
070944fd 1172 thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1);
3a1e9f3e 1173
6cf276dd
DM
1174 /* It might be nice to eventually implement the following for -fanalyzer:
1175 - the "stack" property (SARIF v2.1.0 section 3.38.5)
1176 - the "state" property (SARIF v2.1.0 section 3.38.9)
1177 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
1178
1179 return thread_flow_loc_obj;
1180}
1181
1182/* If M has any known meaning, make a json array suitable for the "kinds"
1183 property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
1184
1185 Otherwise, return NULL. */
1186
1187json::array *
1188sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
1189{
1190 if (m.m_verb == diagnostic_event::VERB_unknown
1191 && m.m_noun == diagnostic_event::NOUN_unknown
1192 && m.m_property == diagnostic_event::PROPERTY_unknown)
1193 return NULL;
1194
1195 json::array *kinds_arr = new json::array ();
1196 if (const char *verb_str
1197 = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
1198 kinds_arr->append (new json::string (verb_str));
1199 if (const char *noun_str
1200 = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
1201 kinds_arr->append (new json::string (noun_str));
1202 if (const char *property_str
1203 = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
1204 kinds_arr->append (new json::string (property_str));
1205 return kinds_arr;
1206}
1207
1208/* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
1209
1210json::object *
1211sarif_builder::make_message_object (const char *msg) const
1212{
1213 json::object *message_obj = new json::object ();
1214
1215 /* "text" property (SARIF v2.1.0 section 3.11.8). */
070944fd 1216 message_obj->set_string ("text", msg);
6cf276dd
DM
1217
1218 return message_obj;
1219}
1220
4f01ae37
DM
1221/* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
1222 We emit the diagram as a code block within the Markdown part
1223 of the message. */
1224
1225json::object *
1226sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
1227 const diagnostic_diagram &diagram)
1228{
1229 json::object *message_obj = new json::object ();
1230
1231 /* "text" property (SARIF v2.1.0 section 3.11.8). */
070944fd 1232 message_obj->set_string ("text", diagram.get_alt_text ());
4f01ae37
DM
1233
1234 char *saved_prefix = pp_take_prefix (context->printer);
1235 pp_set_prefix (context->printer, NULL);
1236
1237 /* "To produce a code block in Markdown, simply indent every line of
1238 the block by at least 4 spaces or 1 tab."
1239 Here we use 4 spaces. */
1240 diagram.get_canvas ().print_to_pp (context->printer, " ");
1241 pp_set_prefix (context->printer, saved_prefix);
1242
1243 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
070944fd 1244 message_obj->set_string ("markdown", pp_formatted_text (context->printer));
4f01ae37
DM
1245
1246 pp_clear_output_area (context->printer);
1247
1248 return message_obj;
1249}
1250
6cf276dd
DM
1251/* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
1252 for MSG. */
1253
1254json::object *
1255sarif_builder::make_multiformat_message_string (const char *msg) const
1256{
1257 json::object *message_obj = new json::object ();
1258
1259 /* "text" property (SARIF v2.1.0 section 3.12.3). */
070944fd 1260 message_obj->set_string ("text", msg);
6cf276dd
DM
1261
1262 return message_obj;
1263}
1264
1265#define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
1266#define SARIF_VERSION "2.1.0"
1267
1268/* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
79aaba0a 1269 Take ownership of INVOCATION_OBJ and RESULTS. */
6cf276dd
DM
1270
1271json::object *
79aaba0a
DM
1272sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
1273 json::array *results)
6cf276dd
DM
1274{
1275 json::object *log_obj = new json::object ();
1276
1277 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
070944fd 1278 log_obj->set_string ("$schema", SARIF_SCHEMA);
6cf276dd
DM
1279
1280 /* "version" property (SARIF v2.1.0 section 3.13.2). */
070944fd 1281 log_obj->set_string ("version", SARIF_VERSION);
6cf276dd
DM
1282
1283 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
1284 json::array *run_arr = new json::array ();
79aaba0a 1285 json::object *run_obj = make_run_object (invocation_obj, results);
6cf276dd
DM
1286 run_arr->append (run_obj);
1287 log_obj->set ("runs", run_arr);
1288
1289 return log_obj;
1290}
1291
1292/* Make a run object (SARIF v2.1.0 section 3.14).
79aaba0a 1293 Take ownership of INVOCATION_OBJ and RESULTS. */
6cf276dd
DM
1294
1295json::object *
79aaba0a
DM
1296sarif_builder::make_run_object (sarif_invocation *invocation_obj,
1297 json::array *results)
6cf276dd
DM
1298{
1299 json::object *run_obj = new json::object ();
1300
1301 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
1302 json::object *tool_obj = make_tool_object ();
1303 run_obj->set ("tool", tool_obj);
1304
1305 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1306 if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
1307 run_obj->set ("taxonomies", taxonomies_arr);
1308
79aaba0a
DM
1309 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
1310 {
1311 json::array *invocations_arr = new json::array ();
1312 invocations_arr->append (invocation_obj);
1313 run_obj->set ("invocations", invocations_arr);
1314 }
1315
6cf276dd
DM
1316 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
1317 if (m_seen_any_relative_paths)
1318 {
1319 json::object *orig_uri_base_ids = new json::object ();
1320 run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
1321 json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
1322 orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
1323 }
1324
1325 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
1326 json::array *artifacts_arr = new json::array ();
1327 for (auto iter : m_filenames)
1328 {
1329 json::object *artifact_obj = make_artifact_object (iter);
1330 artifacts_arr->append (artifact_obj);
1331 }
1332 run_obj->set ("artifacts", artifacts_arr);
1333
1334 /* "results" property (SARIF v2.1.0 section 3.14.23). */
1335 run_obj->set ("results", results);
1336
1337 return run_obj;
1338}
1339
1340/* Make a tool object (SARIF v2.1.0 section 3.18). */
1341
1342json::object *
1343sarif_builder::make_tool_object () const
1344{
1345 json::object *tool_obj = new json::object ();
1346
1347 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
1348 json::object *driver_obj = make_driver_tool_component_object ();
1349 tool_obj->set ("driver", driver_obj);
1350
1351 /* Report plugins via the "extensions" property
1352 (SARIF v2.1.0 section 3.18.3). */
8200cd97 1353 if (auto client_data_hooks = m_context->get_client_data_hooks ())
6cf276dd 1354 if (const client_version_info *vinfo
8200cd97 1355 = client_data_hooks->get_any_version_info ())
6cf276dd
DM
1356 {
1357 class my_plugin_visitor : public client_version_info :: plugin_visitor
1358 {
1359 public:
1360 void on_plugin (const diagnostic_client_plugin_info &p) final override
1361 {
1362 /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
1363 for the plugin. */
1364 json::object *plugin_obj = new json::object ();
1365 m_plugin_objs.safe_push (plugin_obj);
1366
1367 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1368 if (const char *short_name = p.get_short_name ())
070944fd 1369 plugin_obj->set_string ("name", short_name);
6cf276dd
DM
1370
1371 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1372 if (const char *full_name = p.get_full_name ())
070944fd 1373 plugin_obj->set_string ("fullName", full_name);
6cf276dd
DM
1374
1375 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1376 if (const char *version = p.get_version ())
070944fd 1377 plugin_obj->set_string ("version", version);
6cf276dd
DM
1378 }
1379 auto_vec <json::object *> m_plugin_objs;
1380 };
1381 my_plugin_visitor v;
1382 vinfo->for_each_plugin (v);
1383 if (v.m_plugin_objs.length () > 0)
1384 {
1385 json::array *extensions_arr = new json::array ();
1386 tool_obj->set ("extensions", extensions_arr);
1387 for (auto iter : v.m_plugin_objs)
1388 extensions_arr->append (iter);
1389 }
1390 }
1391
1392 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
1393 "extensions" (see toplev.cc: print_version). */
1394
1395 return tool_obj;
1396}
1397
1398/* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
1399 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
1400
1401json::object *
1402sarif_builder::make_driver_tool_component_object () const
1403{
1404 json::object *driver_obj = new json::object ();
1405
8200cd97 1406 if (auto client_data_hooks = m_context->get_client_data_hooks ())
6cf276dd 1407 if (const client_version_info *vinfo
8200cd97 1408 = client_data_hooks->get_any_version_info ())
6cf276dd
DM
1409 {
1410 /* "name" property (SARIF v2.1.0 section 3.19.8). */
1411 if (const char *name = vinfo->get_tool_name ())
070944fd 1412 driver_obj->set_string ("name", name);
6cf276dd
DM
1413
1414 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
1415 if (char *full_name = vinfo->maybe_make_full_name ())
1416 {
070944fd 1417 driver_obj->set_string ("fullName", full_name);
6cf276dd
DM
1418 free (full_name);
1419 }
1420
1421 /* "version" property (SARIF v2.1.0 section 3.19.13). */
1422 if (const char *version = vinfo->get_version_string ())
070944fd 1423 driver_obj->set_string ("version", version);
6cf276dd
DM
1424
1425 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
1426 if (char *version_url = vinfo->maybe_make_version_url ())
1427 {
070944fd 1428 driver_obj->set_string ("informationUri", version_url);
6cf276dd
DM
1429 free (version_url);
1430 }
1431 }
1432
1433 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
1434 driver_obj->set ("rules", m_rules_arr);
1435
1436 return driver_obj;
1437}
1438
1439/* If we've seen any CWE IDs, make an array for the "taxonomies" property
1440 (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
1441 toolComponent (3.19) as per 3.19.3, representing the CWE.
1442
1443 Otherwise return NULL. */
1444
1445json::array *
1446sarif_builder::maybe_make_taxonomies_array () const
1447{
1448 json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
1449 if (!cwe_obj)
1450 return NULL;
1451
1452 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
1453 json::array *taxonomies_arr = new json::array ();
1454 taxonomies_arr->append (cwe_obj);
1455 return taxonomies_arr;
1456}
1457
1458/* If we've seen any CWE IDs, make a toolComponent object
1459 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
1460 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
1461
1462 Otherwise return NULL. */
1463
1464json::object *
1465sarif_builder::maybe_make_cwe_taxonomy_object () const
1466{
1467 if (m_cwe_id_set.is_empty ())
1468 return NULL;
1469
1470 json::object *taxonomy_obj = new json::object ();
1471
1472 /* "name" property (SARIF v2.1.0 section 3.19.8). */
070944fd 1473 taxonomy_obj->set_string ("name", "CWE");
6cf276dd
DM
1474
1475 /* "version" property (SARIF v2.1.0 section 3.19.13). */
070944fd 1476 taxonomy_obj->set_string ("version", "4.7");
6cf276dd
DM
1477
1478 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
070944fd 1479 taxonomy_obj->set_string ("organization", "MITRE");
6cf276dd
DM
1480
1481 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
1482 json::object *short_desc
1483 = make_multiformat_message_string ("The MITRE"
1484 " Common Weakness Enumeration");
1485 taxonomy_obj->set ("shortDescription", short_desc);
1486
1487 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
1488 json::array *taxa_arr = new json::array ();
1489 for (auto cwe_id : m_cwe_id_set)
1490 {
1491 json::object *cwe_taxon
1492 = make_reporting_descriptor_object_for_cwe_id (cwe_id);
1493 taxa_arr->append (cwe_taxon);
1494 }
1495 taxonomy_obj->set ("taxa", taxa_arr);
1496
1497 return taxonomy_obj;
1498}
1499
1500/* Make an artifact object (SARIF v2.1.0 section 3.24). */
1501
1502json::object *
1503sarif_builder::make_artifact_object (const char *filename)
1504{
1505 json::object *artifact_obj = new json::object ();
1506
1507 /* "location" property (SARIF v2.1.0 section 3.24.2). */
1508 json::object *artifact_loc_obj = make_artifact_location_object (filename);
1509 artifact_obj->set ("location", artifact_loc_obj);
1510
1511 /* "contents" property (SARIF v2.1.0 section 3.24.8). */
1512 if (json::object *artifact_content_obj
1513 = maybe_make_artifact_content_object (filename))
1514 artifact_obj->set ("contents", artifact_content_obj);
1515
1516 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
8200cd97 1517 if (auto client_data_hooks = m_context->get_client_data_hooks ())
6cf276dd 1518 if (const char *source_lang
8200cd97 1519 = client_data_hooks->maybe_get_sarif_source_language (filename))
070944fd 1520 artifact_obj->set_string ("sourceLanguage", source_lang);
6cf276dd
DM
1521
1522 return artifact_obj;
1523}
1524
6cf276dd
DM
1525/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
1526 full contents of FILENAME. */
1527
1528json::object *
1529sarif_builder::maybe_make_artifact_content_object (const char *filename) const
1530{
d495ea2b 1531 /* Let input.cc handle any charset conversion. */
14082026 1532 char_span utf8_content
1bdd665a 1533 = m_context->get_file_cache ().get_source_file_content (filename);
d495ea2b 1534 if (!utf8_content)
6cf276dd
DM
1535 return NULL;
1536
d495ea2b
DM
1537 /* Don't add it if it's not valid UTF-8. */
1538 if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
1539 return NULL;
6cf276dd 1540
d495ea2b
DM
1541 json::object *artifact_content_obj = new json::object ();
1542 artifact_content_obj->set ("text",
1543 new json::string (utf8_content.get_buffer (),
1544 utf8_content.length ()));
6cf276dd
DM
1545 return artifact_content_obj;
1546}
1547
1548/* Attempt to read the given range of lines from FILENAME; return
1549 a freshly-allocated 0-terminated buffer containing them, or NULL. */
1550
14082026
DM
1551char *
1552sarif_builder::get_source_lines (const char *filename,
1553 int start_line,
1554 int end_line) const
6cf276dd
DM
1555{
1556 auto_vec<char> result;
1557
1558 for (int line = start_line; line <= end_line; line++)
1559 {
14082026 1560 char_span line_content
1bdd665a 1561 = m_context->get_file_cache ().get_source_line (filename, line);
6cf276dd
DM
1562 if (!line_content.get_buffer ())
1563 return NULL;
1564 result.reserve (line_content.length () + 1);
1565 for (size_t i = 0; i < line_content.length (); i++)
1566 result.quick_push (line_content[i]);
1567 result.quick_push ('\n');
1568 }
1569 result.safe_push ('\0');
1570
1571 return xstrdup (result.address ());
1572}
1573
1574/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
1575 run of lines within FILENAME (including the endpoints). */
1576
1577json::object *
1578sarif_builder::maybe_make_artifact_content_object (const char *filename,
1579 int start_line,
1580 int end_line) const
1581{
1582 char *text_utf8 = get_source_lines (filename, start_line, end_line);
1583
1584 if (!text_utf8)
1585 return NULL;
1586
d495ea2b
DM
1587 /* Don't add it if it's not valid UTF-8. */
1588 if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
1589 {
1590 free (text_utf8);
1591 return NULL;
1592 }
1593
6cf276dd 1594 json::object *artifact_content_obj = new json::object ();
070944fd 1595 artifact_content_obj->set_string ("text", text_utf8);
6cf276dd
DM
1596 free (text_utf8);
1597
1598 return artifact_content_obj;
1599}
1600
1601/* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
1602
1603json::object *
1604sarif_builder::make_fix_object (const rich_location &richloc)
1605{
1606 json::object *fix_obj = new json::object ();
1607
1608 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
1609 /* We assume that all fix-it hints in RICHLOC affect the same file. */
1610 json::array *artifact_change_arr = new json::array ();
1611 json::object *artifact_change_obj = make_artifact_change_object (richloc);
1612 artifact_change_arr->append (artifact_change_obj);
1613 fix_obj->set ("artifactChanges", artifact_change_arr);
1614
1615 return fix_obj;
1616}
1617
1618/* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
1619
1620json::object *
1621sarif_builder::make_artifact_change_object (const rich_location &richloc)
1622{
1623 json::object *artifact_change_obj = new json::object ();
1624
1625 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
1626 json::object *artifact_location_obj
1627 = make_artifact_location_object (richloc.get_loc ());
1628 artifact_change_obj->set ("artifactLocation", artifact_location_obj);
1629
1630 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
1631 json::array *replacement_arr = new json::array ();
1632 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1633 {
1634 const fixit_hint *hint = richloc.get_fixit_hint (i);
1635 json::object *replacement_obj = make_replacement_object (*hint);
1636 replacement_arr->append (replacement_obj);
1637 }
1638 artifact_change_obj->set ("replacements", replacement_arr);
1639
1640 return artifact_change_obj;
1641}
1642
1643/* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
1644
1645json::object *
1646sarif_builder::make_replacement_object (const fixit_hint &hint) const
1647{
1648 json::object *replacement_obj = new json::object ();
1649
1650 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
1651 json::object *region_obj = make_region_object_for_hint (hint);
1652 replacement_obj->set ("deletedRegion", region_obj);
1653
1654 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
1655 json::object *content_obj = make_artifact_content_object (hint.get_string ());
1656 replacement_obj->set ("insertedContent", content_obj);
1657
1658 return replacement_obj;
1659}
1660
1661/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
1662
1663json::object *
1664sarif_builder::make_artifact_content_object (const char *text) const
1665{
1666 json::object *content_obj = new json::object ();
1667
1668 /* "text" property (SARIF v2.1.0 section 3.3.2). */
070944fd 1669 content_obj->set_string ("text", text);
6cf276dd
DM
1670
1671 return content_obj;
1672}
1673
79aaba0a
DM
1674/* Callback for diagnostic_context::ice_handler_cb for when an ICE
1675 occurs. */
1676
1677static void
1678sarif_ice_handler (diagnostic_context *context)
1679{
1680 /* Attempt to ensure that a .sarif file is written out. */
1681 diagnostic_finish (context);
1682
1683 /* Print a header for the remaining output to stderr, and
1684 return, attempting to print the usual ICE messages to
1685 stderr. Hopefully this will be helpful to the user in
1686 indicating what's gone wrong (also for DejaGnu, for pruning
1687 those messages). */
1688 fnotice (stderr, "Internal compiler error:\n");
1689}
1690
14082026
DM
1691class sarif_output_format : public diagnostic_output_format
1692{
1693public:
1694 void on_begin_group () final override
1695 {
1696 /* No-op, */
1697 }
1698 void on_end_group () final override
1699 {
1700 m_builder.end_group ();
1701 }
1702 void
1703 on_begin_diagnostic (diagnostic_info *) final override
1704 {
1705 /* No-op, */
1706 }
1707 void
1708 on_end_diagnostic (diagnostic_info *diagnostic,
1709 diagnostic_t orig_diag_kind) final override
1710 {
1711 m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
1712 }
1713 void on_diagram (const diagnostic_diagram &diagram) final override
1714 {
1715 m_builder.emit_diagram (&m_context, diagram);
1716 }
4f01ae37 1717
14082026
DM
1718protected:
1719 sarif_output_format (diagnostic_context &context)
1720 : diagnostic_output_format (context),
1721 m_builder (&context)
1722 {}
1723
1724 sarif_builder m_builder;
1725};
1726
1727class sarif_stream_output_format : public sarif_output_format
4f01ae37 1728{
14082026
DM
1729public:
1730 sarif_stream_output_format (diagnostic_context &context, FILE *stream)
1731 : sarif_output_format (context),
1732 m_stream (stream)
1733 {
1734 }
1735 ~sarif_stream_output_format ()
1736 {
1737 m_builder.flush_to_file (m_stream);
1738 }
1739private:
1740 FILE *m_stream;
1741};
1742
1743class sarif_file_output_format : public sarif_output_format
1744{
1745public:
1746 sarif_file_output_format (diagnostic_context &context,
1747 const char *base_file_name)
1748 : sarif_output_format (context),
1749 m_base_file_name (xstrdup (base_file_name))
1750 {
1751 }
1752 ~sarif_file_output_format ()
1753 {
1754 char *filename = concat (m_base_file_name, ".sarif", NULL);
1755 free (m_base_file_name);
1756 m_base_file_name = nullptr;
1757 FILE *outf = fopen (filename, "w");
1758 if (!outf)
1759 {
1760 const char *errstr = xstrerror (errno);
1761 fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
1762 filename, errstr);
1763 free (filename);
1764 return;
1765 }
1766 m_builder.flush_to_file (outf);
1767 fclose (outf);
1768 free (filename);
1769 }
1770
1771private:
1772 char *m_base_file_name;
1773};
4f01ae37 1774
6cf276dd
DM
1775/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
1776 to a file). */
1777
1778static void
1779diagnostic_output_format_init_sarif (diagnostic_context *context)
1780{
6cf276dd 1781 /* Override callbacks. */
8200cd97
DM
1782 context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */
1783 context->set_ice_handler_callback (sarif_ice_handler);
6cf276dd
DM
1784
1785 /* The metadata is handled in SARIF format, rather than as text. */
8200cd97
DM
1786 context->set_show_cwe (false);
1787 context->set_show_rules (false);
6cf276dd
DM
1788
1789 /* The option is handled in SARIF format, rather than as text. */
8200cd97 1790 context->set_show_option_requested (false);
6cf276dd
DM
1791
1792 /* Don't colorize the text. */
1793 pp_show_color (context->printer) = false;
1794}
1795
1796/* Populate CONTEXT in preparation for SARIF output to stderr. */
1797
1798void
1799diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
1800{
1801 diagnostic_output_format_init_sarif (context);
8200cd97
DM
1802 context->set_output_format (new sarif_stream_output_format (*context,
1803 stderr));
6cf276dd
DM
1804}
1805
1806/* Populate CONTEXT in preparation for SARIF output to a file named
1807 BASE_FILE_NAME.sarif. */
1808
1809void
1810diagnostic_output_format_init_sarif_file (diagnostic_context *context,
14082026
DM
1811 const char *base_file_name)
1812{
1813 diagnostic_output_format_init_sarif (context);
8200cd97
DM
1814 context->set_output_format (new sarif_file_output_format (*context,
1815 base_file_name));
14082026
DM
1816}
1817
1818/* Populate CONTEXT in preparation for SARIF output to STREAM. */
1819
1820void
1821diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
1822 FILE *stream)
6cf276dd
DM
1823{
1824 diagnostic_output_format_init_sarif (context);
8200cd97
DM
1825 context->set_output_format (new sarif_stream_output_format (*context,
1826 stream));
6cf276dd 1827}
This page took 0.818926 seconds and 5 git commands to generate.