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