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