]>
Commit | Line | Data |
---|---|---|
6cf276dd | 1 | /* SARIF output for diagnostics |
a945c346 | 2 | Copyright (C) 2018-2024 Free Software Foundation, Inc. |
6cf276dd DM |
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" | |
b2e075a5 | 23 | #define INCLUDE_VECTOR |
6cf276dd DM |
24 | #include "system.h" |
25 | #include "coretypes.h" | |
26 | #include "diagnostic.h" | |
27 | #include "diagnostic-metadata.h" | |
28 | #include "diagnostic-path.h" | |
29 | #include "json.h" | |
30 | #include "cpplib.h" | |
31 | #include "logical-location.h" | |
32 | #include "diagnostic-client-data-hooks.h" | |
4f01ae37 DM |
33 | #include "diagnostic-diagram.h" |
34 | #include "text-art/canvas.h" | |
75d62394 | 35 | #include "diagnostic-format-sarif.h" |
6cf276dd DM |
36 | |
37 | class sarif_builder; | |
38 | ||
79aaba0a DM |
39 | /* Subclass of json::object for SARIF invocation objects |
40 | (SARIF v2.1.0 section 3.20). */ | |
41 | ||
75d62394 | 42 | class sarif_invocation : public sarif_object |
79aaba0a DM |
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, | |
8fc4e6c3 | 51 | const diagnostic_info &diagnostic, |
79aaba0a | 52 | sarif_builder *builder); |
75d62394 | 53 | void prepare_to_flush (diagnostic_context *context); |
79aaba0a DM |
54 | |
55 | private: | |
56 | json::array *m_notifications_arr; | |
57 | bool m_success; | |
58 | }; | |
59 | ||
75d62394 | 60 | /* Subclass of sarif_object for SARIF result objects |
79aaba0a | 61 | (SARIF v2.1.0 section 3.27). */ |
6cf276dd | 62 | |
75d62394 | 63 | class sarif_result : public sarif_object |
6cf276dd DM |
64 | { |
65 | public: | |
66 | sarif_result () : m_related_locations_arr (NULL) {} | |
67 | ||
68 | void | |
69 | on_nested_diagnostic (diagnostic_context *context, | |
8fc4e6c3 | 70 | const diagnostic_info &diagnostic, |
6cf276dd DM |
71 | diagnostic_t orig_diag_kind, |
72 | sarif_builder *builder); | |
4f01ae37 DM |
73 | void on_diagram (diagnostic_context *context, |
74 | const diagnostic_diagram &diagram, | |
75 | sarif_builder *builder); | |
6cf276dd DM |
76 | |
77 | private: | |
4f01ae37 DM |
78 | void add_related_location (json::object *location_obj); |
79 | ||
6cf276dd DM |
80 | json::array *m_related_locations_arr; |
81 | }; | |
82 | ||
75d62394 | 83 | /* Subclass of sarif_object for SARIF notification objects |
79aaba0a DM |
84 | (SARIF v2.1.0 section 3.58). |
85 | ||
86 | This subclass is specifically for notifying when an | |
87 | internal compiler error occurs. */ | |
88 | ||
75d62394 | 89 | class sarif_ice_notification : public sarif_object |
79aaba0a DM |
90 | { |
91 | public: | |
92 | sarif_ice_notification (diagnostic_context *context, | |
8fc4e6c3 | 93 | const diagnostic_info &diagnostic, |
79aaba0a DM |
94 | sarif_builder *builder); |
95 | }; | |
96 | ||
3a1e9f3e DM |
97 | /* Subclass of sarif_object for SARIF threadFlow objects |
98 | (SARIF v2.1.0 section 3.37) for PATH. */ | |
99 | ||
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 (thread_flow_loc_obj); | |
108 | } | |
109 | ||
110 | private: | |
111 | json::array *m_locations_arr; | |
112 | }; | |
113 | ||
6cf276dd DM |
114 | /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr |
115 | and -fdiagnostics-format=sarif-file). | |
116 | ||
117 | As diagnostics occur, we build "result" JSON objects, and | |
118 | accumulate state: | |
119 | - which source files are referenced | |
120 | - which warnings are emitted | |
121 | - which CWEs are used | |
122 | ||
123 | At the end of the compile, we use the above to build the full SARIF | |
124 | object tree, adding the result objects to the correct place, and | |
125 | creating objects for the various source files, warnings and CWEs | |
126 | referenced. | |
127 | ||
128 | Implemented: | |
129 | - fix-it hints | |
130 | - CWE metadata | |
131 | - diagnostic groups (see limitations below) | |
132 | - logical locations (e.g. cfun) | |
133 | ||
134 | Known limitations: | |
135 | - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group), | |
136 | but we only capture location and message information from such nested | |
137 | diagnostics (e.g. we ignore fix-it hints on them) | |
138 | - doesn't yet capture command-line arguments: would be run.invocations | |
139 | property (SARIF v2.1.0 section 3.14.11), as invocation objects | |
140 | (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to | |
141 | toplev::main, and the response files. | |
142 | - doesn't capture escape_on_output_p | |
143 | - doesn't capture secondary locations within a rich_location | |
144 | (perhaps we should use the "relatedLocations" property: SARIF v2.1.0 | |
145 | section 3.27.22) | |
146 | - doesn't capture "artifact.encoding" property | |
147 | (SARIF v2.1.0 section 3.24.9). | |
148 | - doesn't capture hashes of the source files | |
149 | ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11). | |
150 | - doesn't capture the "analysisTarget" property | |
151 | (SARIF v2.1.0 section 3.27.13). | |
152 | - doesn't capture labelled ranges | |
153 | - doesn't capture -Werror cleanly | |
154 | - doesn't capture inlining information (can SARIF handle this?) | |
155 | - doesn't capture macro expansion information (can SARIF handle this?). */ | |
156 | ||
157 | class sarif_builder | |
158 | { | |
159 | public: | |
3bd8241a DM |
160 | sarif_builder (diagnostic_context *context, |
161 | bool formatted); | |
6cf276dd | 162 | |
8fc4e6c3 DM |
163 | void end_diagnostic (diagnostic_context *context, |
164 | const diagnostic_info &diagnostic, | |
6cf276dd | 165 | diagnostic_t orig_diag_kind); |
4f01ae37 DM |
166 | void emit_diagram (diagnostic_context *context, |
167 | const diagnostic_diagram &diagram); | |
6cf276dd DM |
168 | void end_group (); |
169 | ||
170 | void flush_to_file (FILE *outf); | |
171 | ||
8fc4e6c3 | 172 | json::array *make_locations_arr (const diagnostic_info &diagnostic); |
6cf276dd DM |
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; | |
4f01ae37 DM |
176 | json::object * |
177 | make_message_object_for_diagram (diagnostic_context *context, | |
178 | const diagnostic_diagram &diagram); | |
6cf276dd DM |
179 | |
180 | private: | |
181 | sarif_result *make_result_object (diagnostic_context *context, | |
8fc4e6c3 | 182 | const diagnostic_info &diagnostic, |
6cf276dd DM |
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); | |
6cf276dd | 187 | json::object *make_code_flow_object (const diagnostic_path &path); |
6cf276dd | 188 | json::object * |
3a1e9f3e DM |
189 | make_thread_flow_location_object (const diagnostic_event &event, |
190 | int path_event_idx); | |
6cf276dd DM |
191 | json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const; |
192 | json::object *maybe_make_physical_location_object (location_t loc); | |
193 | json::object *make_artifact_location_object (location_t loc); | |
194 | json::object *make_artifact_location_object (const char *filename); | |
195 | json::object *make_artifact_location_object_for_pwd () const; | |
196 | json::object *maybe_make_region_object (location_t loc) const; | |
197 | json::object *maybe_make_region_object_for_context (location_t loc) const; | |
198 | json::object *make_region_object_for_hint (const fixit_hint &hint) const; | |
199 | json::object *make_multiformat_message_string (const char *msg) const; | |
79aaba0a DM |
200 | json::object *make_top_level_object (sarif_invocation *invocation_obj, |
201 | json::array *results); | |
202 | json::object *make_run_object (sarif_invocation *invocation_obj, | |
203 | json::array *results); | |
6cf276dd DM |
204 | json::object *make_tool_object () const; |
205 | json::object *make_driver_tool_component_object () const; | |
206 | json::array *maybe_make_taxonomies_array () const; | |
207 | json::object *maybe_make_cwe_taxonomy_object () const; | |
208 | json::object *make_tool_component_reference_object_for_cwe () const; | |
209 | json::object * | |
210 | make_reporting_descriptor_object_for_warning (diagnostic_context *context, | |
8fc4e6c3 | 211 | const diagnostic_info &diagnostic, |
6cf276dd DM |
212 | diagnostic_t orig_diag_kind, |
213 | const char *option_text); | |
214 | json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const; | |
215 | json::object * | |
216 | make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id); | |
217 | json::object *make_artifact_object (const char *filename); | |
14082026 DM |
218 | char *get_source_lines (const char *filename, |
219 | int start_line, | |
220 | int end_line) const; | |
6cf276dd DM |
221 | json::object *maybe_make_artifact_content_object (const char *filename) const; |
222 | json::object *maybe_make_artifact_content_object (const char *filename, | |
223 | int start_line, | |
224 | int end_line) const; | |
225 | json::object *make_fix_object (const rich_location &rich_loc); | |
226 | json::object *make_artifact_change_object (const rich_location &richloc); | |
227 | json::object *make_replacement_object (const fixit_hint &hint) const; | |
228 | json::object *make_artifact_content_object (const char *text) const; | |
229 | int get_sarif_column (expanded_location exploc) const; | |
230 | ||
231 | diagnostic_context *m_context; | |
232 | ||
79aaba0a DM |
233 | /* The JSON object for the invocation object. */ |
234 | sarif_invocation *m_invocation_obj; | |
235 | ||
6cf276dd DM |
236 | /* The JSON array of pending diagnostics. */ |
237 | json::array *m_results_array; | |
238 | ||
239 | /* The JSON object for the result object (if any) in the current | |
240 | diagnostic group. */ | |
241 | sarif_result *m_cur_group_result; | |
242 | ||
243 | hash_set <const char *> m_filenames; | |
244 | bool m_seen_any_relative_paths; | |
245 | hash_set <free_string_hash> m_rule_id_set; | |
246 | json::array *m_rules_arr; | |
247 | ||
248 | /* The set of all CWE IDs we've seen, if any. */ | |
249 | hash_set <int_hash <int, 0, 1> > m_cwe_id_set; | |
250 | ||
251 | int m_tabstop; | |
3bd8241a DM |
252 | |
253 | bool m_formatted; | |
6cf276dd DM |
254 | }; |
255 | ||
75d62394 DM |
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 ("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 ("properties", bag); | |
270 | return *bag; | |
271 | } | |
272 | ||
273 | /* class sarif_invocation : public sarif_object. */ | |
79aaba0a DM |
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, | |
8fc4e6c3 | 280 | const diagnostic_info &diagnostic, |
79aaba0a DM |
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 (notification_obj); | |
288 | } | |
289 | ||
290 | void | |
75d62394 | 291 | sarif_invocation::prepare_to_flush (diagnostic_context *context) |
79aaba0a DM |
292 | { |
293 | /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */ | |
070944fd | 294 | set_bool ("executionSuccessful", m_success); |
79aaba0a DM |
295 | |
296 | /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */ | |
297 | set ("toolExecutionNotifications", m_notifications_arr); | |
75d62394 DM |
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. */ | |
8200cd97 DM |
301 | if (auto client_data_hooks = context->get_client_data_hooks ()) |
302 | client_data_hooks->add_sarif_invocation_properties (*this); | |
79aaba0a DM |
303 | } |
304 | ||
75d62394 | 305 | /* class sarif_result : public sarif_object. */ |
6cf276dd DM |
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, | |
8fc4e6c3 | 315 | const diagnostic_info &diagnostic, |
6cf276dd DM |
316 | diagnostic_t /*orig_diag_kind*/, |
317 | sarif_builder *builder) | |
318 | { | |
6cf276dd DM |
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 | |
8fc4e6c3 | 323 | = builder->make_location_object (*diagnostic.richloc, NULL); |
6cf276dd DM |
324 | json::object *message_obj |
325 | = builder->make_message_object (pp_formatted_text (context->printer)); | |
326 | pp_clear_output_area (context->printer); | |
327 | location_obj->set ("message", message_obj); | |
328 | ||
4f01ae37 DM |
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 ("message", 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 ("relatedLocations", m_related_locations_arr); | |
361 | } | |
6cf276dd DM |
362 | m_related_locations_arr->append (location_obj); |
363 | } | |
364 | ||
75d62394 | 365 | /* class sarif_ice_notification : public sarif_object. */ |
79aaba0a DM |
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, | |
8fc4e6c3 | 371 | const diagnostic_info &diagnostic, |
79aaba0a DM |
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 ("locations", locations_arr); | |
377 | ||
378 | /* "message" property (SARIF v2.1.0 section 3.85.5). */ | |
379 | json::object *message_obj | |
380 | = builder->make_message_object (pp_formatted_text (context->printer)); | |
381 | pp_clear_output_area (context->printer); | |
382 | set ("message", message_obj); | |
383 | ||
384 | /* "level" property (SARIF v2.1.0 section 3.58.6). */ | |
070944fd | 385 | set_string ("level", "error"); |
79aaba0a DM |
386 | } |
387 | ||
3a1e9f3e DM |
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 (false)); | |
070944fd | 394 | set_string ("id", name.get ()); |
3a1e9f3e DM |
395 | |
396 | /* "locations" property (SARIF v2.1.0 section 3.37.6). */ | |
397 | m_locations_arr = new json::array (); | |
398 | set ("locations", m_locations_arr); | |
399 | } | |
400 | ||
6cf276dd DM |
401 | /* class sarif_builder. */ |
402 | ||
403 | /* sarif_builder's ctor. */ | |
404 | ||
3bd8241a DM |
405 | sarif_builder::sarif_builder (diagnostic_context *context, |
406 | bool formatted) | |
6cf276dd | 407 | : m_context (context), |
79aaba0a | 408 | m_invocation_obj (new sarif_invocation ()), |
6cf276dd DM |
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 ()), | |
3bd8241a DM |
414 | m_tabstop (context->m_tabstop), |
415 | m_formatted (formatted) | |
6cf276dd DM |
416 | { |
417 | } | |
418 | ||
419 | /* Implementation of "end_diagnostic" for SARIF output. */ | |
420 | ||
421 | void | |
422 | sarif_builder::end_diagnostic (diagnostic_context *context, | |
8fc4e6c3 | 423 | const diagnostic_info &diagnostic, |
6cf276dd DM |
424 | diagnostic_t orig_diag_kind) |
425 | { | |
8fc4e6c3 | 426 | if (diagnostic.kind == DK_ICE || diagnostic.kind == DK_ICE_NOBT) |
79aaba0a DM |
427 | { |
428 | m_invocation_obj->add_notification_for_ice (context, diagnostic, this); | |
429 | return; | |
430 | } | |
6cf276dd DM |
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 | 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 (result_obj); | |
444 | m_cur_group_result = result_obj; | |
445 | } | |
446 | } | |
447 | ||
4f01ae37 DM |
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, this); | |
458 | } | |
459 | ||
6cf276dd DM |
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 | { | |
75d62394 | 476 | m_invocation_obj->prepare_to_flush (m_context); |
79aaba0a | 477 | json::object *top = make_top_level_object (m_invocation_obj, m_results_array); |
3bd8241a | 478 | top->dump (outf, m_formatted); |
79aaba0a | 479 | m_invocation_obj = NULL; |
6cf276dd DM |
480 | m_results_array = NULL; |
481 | fprintf (outf, "\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 (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, | |
8fc4e6c3 | 535 | const diagnostic_info &diagnostic, |
6cf276dd DM |
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 | |
8fc4e6c3 DM |
543 | = context->make_option_name (diagnostic.option_index, |
544 | orig_diag_kind, diagnostic.kind)) | |
6cf276dd DM |
545 | { |
546 | /* Lazily create reportingDescriptor objects for and add to m_rules_arr. | |
547 | Set ruleId referencing them. */ | |
070944fd | 548 | result_obj->set_string ("ruleId", option_text); |
6cf276dd DM |
549 | if (m_rule_id_set.contains (option_text)) |
550 | free (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 (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 (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 (orig_diag_kind); | |
070944fd | 572 | result_obj->set_string ("ruleId", rule_id); |
6cf276dd DM |
573 | free (rule_id); |
574 | } | |
575 | ||
8fc4e6c3 | 576 | if (diagnostic.metadata) |
12b67d1e DM |
577 | { |
578 | /* "taxa" property (SARIF v2.1.0 section 3.27.8). */ | |
8fc4e6c3 | 579 | if (int cwe_id = diagnostic.metadata->get_cwe ()) |
12b67d1e DM |
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 (cwe_id_obj); | |
585 | result_obj->set ("taxa", taxa_arr); | |
586 | } | |
587 | ||
8fc4e6c3 | 588 | diagnostic.metadata->maybe_add_sarif_properties (*result_obj); |
12b67d1e | 589 | } |
6cf276dd DM |
590 | |
591 | /* "level" property (SARIF v2.1.0 section 3.27.10). */ | |
8fc4e6c3 | 592 | if (const char *sarif_level = maybe_get_sarif_level (diagnostic.kind)) |
070944fd | 593 | result_obj->set_string ("level", sarif_level); |
6cf276dd DM |
594 | |
595 | /* "message" property (SARIF v2.1.0 section 3.27.11). */ | |
596 | json::object *message_obj | |
597 | = make_message_object (pp_formatted_text (context->printer)); | |
598 | pp_clear_output_area (context->printer); | |
599 | result_obj->set ("message", message_obj); | |
600 | ||
601 | /* "locations" property (SARIF v2.1.0 section 3.27.12). */ | |
79aaba0a | 602 | json::array *locations_arr = make_locations_arr (diagnostic); |
6cf276dd DM |
603 | result_obj->set ("locations", locations_arr); |
604 | ||
605 | /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */ | |
8fc4e6c3 | 606 | if (const diagnostic_path *path = diagnostic.richloc->get_path ()) |
6cf276dd DM |
607 | { |
608 | json::array *code_flows_arr = new json::array (); | |
609 | json::object *code_flow_obj = make_code_flow_object (*path); | |
610 | code_flows_arr->append (code_flow_obj); | |
611 | result_obj->set ("codeFlows", code_flows_arr); | |
612 | } | |
613 | ||
614 | /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is | |
615 | set up later, if any nested diagnostics occur within this diagnostic | |
616 | group. */ | |
617 | ||
618 | /* "fixes" property (SARIF v2.1.0 section 3.27.30). */ | |
8fc4e6c3 | 619 | const rich_location *richloc = diagnostic.richloc; |
6cf276dd DM |
620 | if (richloc->get_num_fixit_hints ()) |
621 | { | |
622 | json::array *fix_arr = new json::array (); | |
623 | json::object *fix_obj = make_fix_object (*richloc); | |
624 | fix_arr->append (fix_obj); | |
625 | result_obj->set ("fixes", 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, | |
8fc4e6c3 | 637 | const diagnostic_info &diagnostic, |
6cf276dd DM |
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). */ | |
070944fd | 644 | reporting_desc->set_string ("id", option_text); |
6cf276dd DM |
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). */ | |
8fc4e6c3 | 650 | if (char *option_url = context->make_option_url (diagnostic.option_index)) |
6cf276dd | 651 | { |
353f146c DM |
652 | reporting_desc->set_string ("helpUri", option_url); |
653 | free (option_url); | |
6cf276dd DM |
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); | |
070944fd | 671 | reporting_desc->set_string ("id", pp_formatted_text (&pp)); |
6cf276dd DM |
672 | } |
673 | ||
674 | /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */ | |
675 | { | |
676 | char *url = get_cwe_url (cwe_id); | |
070944fd | 677 | reporting_desc->set_string ("helpUri", url); |
6cf276dd DM |
678 | free (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); | |
070944fd | 698 | desc_ref_obj->set_string ("id", pp_formatted_text (&pp)); |
6cf276dd DM |
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 ("toolComponent", comp_ref_obj); | |
704 | ||
705 | /* Add CWE_ID to our set. */ | |
706 | gcc_assert (cwe_id > 0); | |
707 | m_cwe_id_set.add (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). */ | |
070944fd | 722 | comp_ref_obj->set_string ("name", "cwe"); |
6cf276dd DM |
723 | |
724 | return comp_ref_obj; | |
725 | } | |
726 | ||
79aaba0a DM |
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 * | |
8fc4e6c3 | 732 | sarif_builder::make_locations_arr (const diagnostic_info &diagnostic) |
79aaba0a DM |
733 | { |
734 | json::array *locations_arr = new json::array (); | |
735 | const logical_location *logical_loc = NULL; | |
8200cd97 DM |
736 | if (auto client_data_hooks = m_context->get_client_data_hooks ()) |
737 | logical_loc = client_data_hooks->get_current_logical_location (); | |
79aaba0a DM |
738 | |
739 | json::object *location_obj | |
8fc4e6c3 | 740 | = make_location_object (*diagnostic.richloc, logical_loc); |
79aaba0a DM |
741 | locations_arr->append (location_obj); |
742 | return locations_arr; | |
743 | } | |
744 | ||
6cf276dd DM |
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; | |
05c99b1c | 755 | json::object *logical_loc_obj = make_sarif_logical_location_object (*logical_loc); |
6cf276dd DM |
756 | json::array *location_locs_arr = new json::array (); |
757 | location_locs_arr->append (logical_loc_obj); | |
758 | location_obj->set ("logicalLocations", location_locs_arr); | |
759 | } | |
760 | ||
761 | /* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC | |
762 | and LOGICAL_LOC. */ | |
763 | ||
764 | json::object * | |
765 | 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 ("physicalLocation", 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 ("physicalLocation", 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 (false); | |
f858fe7a | 802 | json::object *message_obj = make_message_object (ev_desc.get ()); |
6cf276dd | 803 | location_obj->set ("message", message_obj); |
6cf276dd DM |
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 | { | |
2e8a0553 | 815 | if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL) |
6cf276dd DM |
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 ("artifactLocation", 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 ("region", 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 ("contextRegion", context_region_obj); | |
833 | ||
834 | /* Instead, we add artifacts to the run as a whole, | |
835 | with artifact.contents. | |
836 | Could do both, though. */ | |
837 | ||
838 | return phys_loc_obj; | |
839 | } | |
840 | ||
841 | /* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC, | |
842 | or return NULL. */ | |
843 | ||
844 | json::object * | |
845 | 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). */ | |
070944fd | 864 | artifact_loc_obj->set_string ("uri", filename); |
6cf276dd DM |
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). */ | |
070944fd | 870 | artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME); |
6cf276dd DM |
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 (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] == '/'); | |
070944fd | 914 | artifact_loc_obj->set_string ("uri", pwd); |
6cf276dd DM |
915 | free (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); | |
1bdd665a DM |
927 | return location_compute_display_column (m_context->get_file_cache (), |
928 | exploc, policy); | |
6cf276dd DM |
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) */ | |
070944fd | 957 | region_obj->set_integer ("startLine", exploc_start.line); |
6cf276dd DM |
958 | |
959 | /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */ | |
070944fd | 960 | region_obj->set_integer ("startColumn", get_sarif_column (exploc_start)); |
6cf276dd DM |
961 | |
962 | /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ | |
963 | if (exploc_finish.line != exploc_start.line) | |
070944fd | 964 | region_obj->set_integer ("endLine", exploc_finish.line); |
6cf276dd DM |
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_finish) + 1; | |
070944fd | 970 | region_obj->set_integer ("endColumn", next_column); |
6cf276dd DM |
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) */ | |
070944fd | 1007 | region_obj->set_integer ("startLine", exploc_start.line); |
6cf276dd DM |
1008 | |
1009 | /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ | |
1010 | if (exploc_finish.line != exploc_start.line) | |
070944fd | 1011 | region_obj->set_integer ("endLine", exploc_finish.line); |
6cf276dd DM |
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 (exploc_start.file, | |
1016 | exploc_start.line, | |
1017 | exploc_finish.line)) | |
1018 | region_obj->set ("snippet", artifact_content_obj); | |
1019 | ||
1020 | return region_obj; | |
1021 | } | |
1022 | ||
1023 | /* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region | |
1024 | of HINT (as per SARIF v2.1.0 section 3.57.3). */ | |
1025 | ||
1026 | json::object * | |
1027 | 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) */ | |
070944fd | 1038 | region_obj->set_integer ("startLine", exploc_start.line); |
6cf276dd DM |
1039 | |
1040 | /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */ | |
1041 | int start_col = get_sarif_column (exploc_start); | |
070944fd | 1042 | region_obj->set_integer ("startColumn", start_col); |
6cf276dd DM |
1043 | |
1044 | /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ | |
1045 | if (exploc_next.line != exploc_start.line) | |
070944fd | 1046 | region_obj->set_integer ("endLine", exploc_next.line); |
6cf276dd DM |
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_next); | |
070944fd | 1051 | region_obj->set_integer ("endColumn", next_col); |
6cf276dd DM |
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 * | |
05c99b1c | 1093 | make_sarif_logical_location_object (const logical_location &logical_loc) |
6cf276dd DM |
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 ()) | |
070944fd | 1099 | logical_loc_obj->set_string ("name", short_name); |
6cf276dd DM |
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 ()) | |
070944fd | 1103 | logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope); |
6cf276dd DM |
1104 | |
1105 | /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */ | |
1106 | if (const char *internal_name = logical_loc.get_internal_name ()) | |
070944fd | 1107 | logical_loc_obj->set_string ("decoratedName", internal_name); |
6cf276dd DM |
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)) | |
070944fd | 1112 | logical_loc_obj->set_string ("kind", sarif_kind_str); |
6cf276dd DM |
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 | ||
3a1e9f3e | 1124 | /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */ |
6cf276dd | 1125 | json::array *thread_flows_arr = new json::array (); |
6cf276dd | 1126 | |
3a1e9f3e DM |
1127 | /* Walk the events, consolidating into per-thread threadFlow objects, |
1128 | using the index with PATH as the overall executionOrder. */ | |
1129 | hash_map<int_hash<diagnostic_thread_id_t, -1, -2>, | |
1130 | sarif_thread_flow *> thread_id_map; | |
6cf276dd DM |
1131 | for (unsigned i = 0; i < path.num_events (); i++) |
1132 | { | |
1133 | const diagnostic_event &event = path.get_event (i); | |
3a1e9f3e DM |
1134 | const diagnostic_thread_id_t thread_id = event.get_thread_id (); |
1135 | sarif_thread_flow *thread_flow_obj; | |
1136 | ||
1137 | if (sarif_thread_flow **slot = thread_id_map.get (thread_id)) | |
1138 | thread_flow_obj = *slot; | |
1139 | else | |
1140 | { | |
1141 | const diagnostic_thread &thread = path.get_thread (thread_id); | |
1142 | thread_flow_obj = new sarif_thread_flow (thread); | |
1143 | thread_flows_arr->append (thread_flow_obj); | |
1144 | thread_id_map.put (thread_id, thread_flow_obj); | |
1145 | } | |
1146 | ||
1147 | /* Add event to thread's threadFlow object. */ | |
6cf276dd | 1148 | json::object *thread_flow_loc_obj |
3a1e9f3e DM |
1149 | = make_thread_flow_location_object (event, i); |
1150 | thread_flow_obj->add_location (thread_flow_loc_obj); | |
6cf276dd | 1151 | } |
3a1e9f3e | 1152 | code_flow_obj->set ("threadFlows", thread_flows_arr); |
6cf276dd | 1153 | |
3a1e9f3e | 1154 | return code_flow_obj; |
6cf276dd DM |
1155 | } |
1156 | ||
1157 | /* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */ | |
1158 | ||
1159 | json::object * | |
3a1e9f3e DM |
1160 | sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev, |
1161 | int path_event_idx) | |
6cf276dd | 1162 | { |
05c99b1c DM |
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); | |
6cf276dd DM |
1168 | |
1169 | /* "location" property (SARIF v2.1.0 section 3.38.3). */ | |
1170 | json::object *location_obj = make_location_object (ev); | |
1171 | thread_flow_loc_obj->set ("location", 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 ("kinds", kinds_arr); | |
1177 | ||
1178 | /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */ | |
070944fd | 1179 | thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ()); |
6cf276dd | 1180 | |
3a1e9f3e DM |
1181 | /* "executionOrder" property (SARIF v2.1.0 3.38.11). |
1182 | Offset by 1 to match the human-readable values emitted by %@. */ | |
070944fd | 1183 | thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1); |
3a1e9f3e | 1184 | |
6cf276dd DM |
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 (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 (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 (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). */ | |
070944fd | 1227 | message_obj->set_string ("text", msg); |
6cf276dd DM |
1228 | |
1229 | return message_obj; | |
1230 | } | |
1231 | ||
4f01ae37 DM |
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). */ | |
070944fd | 1243 | message_obj->set_string ("text", diagram.get_alt_text ()); |
4f01ae37 DM |
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 (context->printer, " "); | |
1252 | pp_set_prefix (context->printer, saved_prefix); | |
1253 | ||
1254 | /* "markdown" property (SARIF v2.1.0 section 3.11.9). */ | |
070944fd | 1255 | message_obj->set_string ("markdown", pp_formatted_text (context->printer)); |
4f01ae37 DM |
1256 | |
1257 | pp_clear_output_area (context->printer); | |
1258 | ||
1259 | return message_obj; | |
1260 | } | |
1261 | ||
6cf276dd DM |
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). */ | |
070944fd | 1271 | message_obj->set_string ("text", msg); |
6cf276dd DM |
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). | |
79aaba0a | 1280 | Take ownership of INVOCATION_OBJ and RESULTS. */ |
6cf276dd DM |
1281 | |
1282 | json::object * | |
79aaba0a DM |
1283 | sarif_builder::make_top_level_object (sarif_invocation *invocation_obj, |
1284 | json::array *results) | |
6cf276dd DM |
1285 | { |
1286 | json::object *log_obj = new json::object (); | |
1287 | ||
1288 | /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */ | |
070944fd | 1289 | log_obj->set_string ("$schema", SARIF_SCHEMA); |
6cf276dd DM |
1290 | |
1291 | /* "version" property (SARIF v2.1.0 section 3.13.2). */ | |
070944fd | 1292 | log_obj->set_string ("version", SARIF_VERSION); |
6cf276dd DM |
1293 | |
1294 | /* "runs" property (SARIF v2.1.0 section 3.13.4). */ | |
1295 | json::array *run_arr = new json::array (); | |
79aaba0a | 1296 | json::object *run_obj = make_run_object (invocation_obj, results); |
6cf276dd DM |
1297 | run_arr->append (run_obj); |
1298 | log_obj->set ("runs", run_arr); | |
1299 | ||
1300 | return log_obj; | |
1301 | } | |
1302 | ||
1303 | /* Make a run object (SARIF v2.1.0 section 3.14). | |
79aaba0a | 1304 | Take ownership of INVOCATION_OBJ and RESULTS. */ |
6cf276dd DM |
1305 | |
1306 | json::object * | |
79aaba0a DM |
1307 | sarif_builder::make_run_object (sarif_invocation *invocation_obj, |
1308 | json::array *results) | |
6cf276dd DM |
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 ("tool", 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 ("taxonomies", taxonomies_arr); | |
1319 | ||
79aaba0a DM |
1320 | /* "invocations" property (SARIF v2.1.0 section 3.14.11). */ |
1321 | { | |
1322 | json::array *invocations_arr = new json::array (); | |
1323 | invocations_arr->append (invocation_obj); | |
1324 | run_obj->set ("invocations", invocations_arr); | |
1325 | } | |
1326 | ||
6cf276dd DM |
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 ("originalUriBaseIds", 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, 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 (iter); | |
1341 | artifacts_arr->append (artifact_obj); | |
1342 | } | |
1343 | run_obj->set ("artifacts", artifacts_arr); | |
1344 | ||
1345 | /* "results" property (SARIF v2.1.0 section 3.14.23). */ | |
1346 | run_obj->set ("results", 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 ("driver", driver_obj); | |
1361 | ||
1362 | /* Report plugins via the "extensions" property | |
1363 | (SARIF v2.1.0 section 3.18.3). */ | |
8200cd97 | 1364 | if (auto client_data_hooks = m_context->get_client_data_hooks ()) |
6cf276dd | 1365 | if (const client_version_info *vinfo |
8200cd97 | 1366 | = client_data_hooks->get_any_version_info ()) |
6cf276dd DM |
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 (plugin_obj); | |
1377 | ||
1378 | /* "name" property (SARIF v2.1.0 section 3.19.8). */ | |
1379 | if (const char *short_name = p.get_short_name ()) | |
070944fd | 1380 | plugin_obj->set_string ("name", short_name); |
6cf276dd DM |
1381 | |
1382 | /* "fullName" property (SARIF v2.1.0 section 3.19.9). */ | |
1383 | if (const char *full_name = p.get_full_name ()) | |
070944fd | 1384 | plugin_obj->set_string ("fullName", full_name); |
6cf276dd DM |
1385 | |
1386 | /* "version" property (SARIF v2.1.0 section 3.19.13). */ | |
1387 | if (const char *version = p.get_version ()) | |
070944fd | 1388 | plugin_obj->set_string ("version", version); |
6cf276dd DM |
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 ("extensions", extensions_arr); | |
1398 | for (auto iter : v.m_plugin_objs) | |
1399 | extensions_arr->append (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 | ||
8200cd97 | 1417 | if (auto client_data_hooks = m_context->get_client_data_hooks ()) |
6cf276dd | 1418 | if (const client_version_info *vinfo |
8200cd97 | 1419 | = client_data_hooks->get_any_version_info ()) |
6cf276dd DM |
1420 | { |
1421 | /* "name" property (SARIF v2.1.0 section 3.19.8). */ | |
1422 | if (const char *name = vinfo->get_tool_name ()) | |
070944fd | 1423 | driver_obj->set_string ("name", name); |
6cf276dd DM |
1424 | |
1425 | /* "fullName" property (SARIF v2.1.0 section 3.19.9). */ | |
1426 | if (char *full_name = vinfo->maybe_make_full_name ()) | |
1427 | { | |
070944fd | 1428 | driver_obj->set_string ("fullName", full_name); |
6cf276dd DM |
1429 | free (full_name); |
1430 | } | |
1431 | ||
1432 | /* "version" property (SARIF v2.1.0 section 3.19.13). */ | |
1433 | if (const char *version = vinfo->get_version_string ()) | |
070944fd | 1434 | driver_obj->set_string ("version", version); |
6cf276dd DM |
1435 | |
1436 | /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */ | |
1437 | if (char *version_url = vinfo->maybe_make_version_url ()) | |
1438 | { | |
070944fd | 1439 | driver_obj->set_string ("informationUri", version_url); |
6cf276dd DM |
1440 | free (version_url); |
1441 | } | |
1442 | } | |
1443 | ||
1444 | /* "rules" property (SARIF v2.1.0 section 3.19.23). */ | |
1445 | driver_obj->set ("rules", 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 (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). */ | |
070944fd | 1484 | taxonomy_obj->set_string ("name", "CWE"); |
6cf276dd DM |
1485 | |
1486 | /* "version" property (SARIF v2.1.0 section 3.19.13). */ | |
070944fd | 1487 | taxonomy_obj->set_string ("version", "4.7"); |
6cf276dd DM |
1488 | |
1489 | /* "organization" property (SARIF v2.1.0 section 3.19.18). */ | |
070944fd | 1490 | taxonomy_obj->set_string ("organization", "MITRE"); |
6cf276dd DM |
1491 | |
1492 | /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */ | |
1493 | json::object *short_desc | |
1494 | = make_multiformat_message_string ("The MITRE" | |
1495 | " Common Weakness Enumeration"); | |
1496 | taxonomy_obj->set ("shortDescription", 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 (cwe_taxon); | |
1505 | } | |
1506 | taxonomy_obj->set ("taxa", 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 ("location", 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 ("contents", artifact_content_obj); | |
1526 | ||
1527 | /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */ | |
8200cd97 | 1528 | if (auto client_data_hooks = m_context->get_client_data_hooks ()) |
6cf276dd | 1529 | if (const char *source_lang |
8200cd97 | 1530 | = client_data_hooks->maybe_get_sarif_source_language (filename)) |
070944fd | 1531 | artifact_obj->set_string ("sourceLanguage", source_lang); |
6cf276dd DM |
1532 | |
1533 | return artifact_obj; | |
1534 | } | |
1535 | ||
6cf276dd DM |
1536 | /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the |
1537 | full contents of FILENAME. */ | |
1538 | ||
1539 | json::object * | |
1540 | sarif_builder::maybe_make_artifact_content_object (const char *filename) const | |
1541 | { | |
d495ea2b | 1542 | /* Let input.cc handle any charset conversion. */ |
14082026 | 1543 | char_span utf8_content |
1bdd665a | 1544 | = m_context->get_file_cache ().get_source_file_content (filename); |
d495ea2b | 1545 | if (!utf8_content) |
6cf276dd DM |
1546 | return NULL; |
1547 | ||
d495ea2b DM |
1548 | /* Don't add it if it's not valid UTF-8. */ |
1549 | if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ())) | |
1550 | return NULL; | |
6cf276dd | 1551 | |
d495ea2b DM |
1552 | json::object *artifact_content_obj = new json::object (); |
1553 | artifact_content_obj->set ("text", | |
1554 | new json::string (utf8_content.get_buffer (), | |
1555 | utf8_content.length ())); | |
6cf276dd DM |
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 | ||
14082026 DM |
1562 | char * |
1563 | sarif_builder::get_source_lines (const char *filename, | |
1564 | int start_line, | |
1565 | int end_line) const | |
6cf276dd DM |
1566 | { |
1567 | auto_vec<char> result; | |
1568 | ||
1569 | for (int line = start_line; line <= end_line; line++) | |
1570 | { | |
14082026 | 1571 | char_span line_content |
1bdd665a | 1572 | = m_context->get_file_cache ().get_source_line (filename, line); |
6cf276dd DM |
1573 | if (!line_content.get_buffer ()) |
1574 | return NULL; | |
1575 | result.reserve (line_content.length () + 1); | |
1576 | for (size_t i = 0; i < line_content.length (); i++) | |
1577 | result.quick_push (line_content[i]); | |
1578 | result.quick_push ('\n'); | |
1579 | } | |
1580 | result.safe_push ('\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 | ||
d495ea2b DM |
1598 | /* Don't add it if it's not valid UTF-8. */ |
1599 | if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8))) | |
1600 | { | |
1601 | free (text_utf8); | |
1602 | return NULL; | |
1603 | } | |
1604 | ||
6cf276dd | 1605 | json::object *artifact_content_obj = new json::object (); |
070944fd | 1606 | artifact_content_obj->set_string ("text", text_utf8); |
6cf276dd DM |
1607 | free (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 (artifact_change_obj); | |
1624 | fix_obj->set ("artifactChanges", 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 (richloc.get_loc ()); | |
1639 | artifact_change_obj->set ("artifactLocation", 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 (i); | |
1646 | json::object *replacement_obj = make_replacement_object (*hint); | |
1647 | replacement_arr->append (replacement_obj); | |
1648 | } | |
1649 | artifact_change_obj->set ("replacements", 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 ("deletedRegion", region_obj); | |
1664 | ||
1665 | /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */ | |
1666 | json::object *content_obj = make_artifact_content_object (hint.get_string ()); | |
1667 | replacement_obj->set ("insertedContent", 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). */ | |
070944fd | 1680 | content_obj->set_string ("text", text); |
6cf276dd DM |
1681 | |
1682 | return content_obj; | |
1683 | } | |
1684 | ||
79aaba0a DM |
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 | ||
14082026 DM |
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 | |
8fc4e6c3 | 1714 | on_begin_diagnostic (const diagnostic_info &) final override |
14082026 DM |
1715 | { |
1716 | /* No-op, */ | |
1717 | } | |
1718 | void | |
8fc4e6c3 | 1719 | on_end_diagnostic (const diagnostic_info &diagnostic, |
14082026 DM |
1720 | diagnostic_t orig_diag_kind) final override |
1721 | { | |
1722 | m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind); | |
1723 | } | |
1724 | void on_diagram (const diagnostic_diagram &diagram) final override | |
1725 | { | |
1726 | m_builder.emit_diagram (&m_context, diagram); | |
1727 | } | |
4f01ae37 | 1728 | |
14082026 | 1729 | protected: |
3bd8241a DM |
1730 | sarif_output_format (diagnostic_context &context, |
1731 | bool formatted) | |
14082026 | 1732 | : diagnostic_output_format (context), |
3bd8241a | 1733 | m_builder (&context, formatted) |
14082026 DM |
1734 | {} |
1735 | ||
1736 | sarif_builder m_builder; | |
1737 | }; | |
1738 | ||
1739 | class sarif_stream_output_format : public sarif_output_format | |
4f01ae37 | 1740 | { |
14082026 | 1741 | public: |
3bd8241a DM |
1742 | sarif_stream_output_format (diagnostic_context &context, |
1743 | bool formatted, | |
1744 | FILE *stream) | |
1745 | : sarif_output_format (context, formatted), | |
14082026 DM |
1746 | m_stream (stream) |
1747 | { | |
1748 | } | |
1749 | ~sarif_stream_output_format () | |
1750 | { | |
1751 | m_builder.flush_to_file (m_stream); | |
1752 | } | |
0bf99b1b DM |
1753 | bool machine_readable_stderr_p () const final override |
1754 | { | |
1755 | return m_stream == stderr; | |
1756 | } | |
14082026 DM |
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, | |
3bd8241a DM |
1765 | bool formatted, |
1766 | const char *base_file_name) | |
1767 | : sarif_output_format (context, formatted), | |
14082026 DM |
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 (m_base_file_name); | |
1775 | m_base_file_name = nullptr; | |
1776 | FILE *outf = fopen (filename, "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 (filename); | |
1783 | return; | |
1784 | } | |
1785 | m_builder.flush_to_file (outf); | |
1786 | fclose (outf); | |
1787 | free (filename); | |
1788 | } | |
0bf99b1b DM |
1789 | bool machine_readable_stderr_p () const final override |
1790 | { | |
1791 | return false; | |
1792 | } | |
14082026 DM |
1793 | |
1794 | private: | |
1795 | char *m_base_file_name; | |
1796 | }; | |
4f01ae37 | 1797 | |
6cf276dd DM |
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 | { | |
6cf276dd | 1804 | /* Override callbacks. */ |
8200cd97 DM |
1805 | context->m_print_path = nullptr; /* handled in sarif_end_diagnostic. */ |
1806 | context->set_ice_handler_callback (sarif_ice_handler); | |
6cf276dd DM |
1807 | |
1808 | /* The metadata is handled in SARIF format, rather than as text. */ | |
8200cd97 DM |
1809 | context->set_show_cwe (false); |
1810 | context->set_show_rules (false); | |
6cf276dd DM |
1811 | |
1812 | /* The option is handled in SARIF format, rather than as text. */ | |
8200cd97 | 1813 | context->set_show_option_requested (false); |
6cf276dd DM |
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 | |
3bd8241a DM |
1822 | diagnostic_output_format_init_sarif_stderr (diagnostic_context *context, |
1823 | bool formatted) | |
6cf276dd DM |
1824 | { |
1825 | diagnostic_output_format_init_sarif (context); | |
8200cd97 | 1826 | context->set_output_format (new sarif_stream_output_format (*context, |
3bd8241a | 1827 | formatted, |
8200cd97 | 1828 | stderr)); |
6cf276dd DM |
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, | |
3bd8241a | 1836 | bool formatted, |
14082026 DM |
1837 | const char *base_file_name) |
1838 | { | |
1839 | diagnostic_output_format_init_sarif (context); | |
8200cd97 | 1840 | context->set_output_format (new sarif_file_output_format (*context, |
3bd8241a | 1841 | formatted, |
8200cd97 | 1842 | base_file_name)); |
14082026 DM |
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, | |
3bd8241a | 1849 | bool formatted, |
14082026 | 1850 | FILE *stream) |
6cf276dd DM |
1851 | { |
1852 | diagnostic_output_format_init_sarif (context); | |
8200cd97 | 1853 | context->set_output_format (new sarif_stream_output_format (*context, |
3bd8241a | 1854 | formatted, |
8200cd97 | 1855 | stream)); |
6cf276dd | 1856 | } |