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